<?php

namespace App\Services;

use App\Models\Loan;
use App\Models\LoanApprovalDecision;
use App\Models\LoanStatusHistory;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\ValidationException;

class LoanApprovalService
{
    public function __construct(
        private MoneyMath $money,
        private CustomerRiskService $risk,
    ) {}

    public function submit(Loan $loan, User $user, ?string $comments = null): Loan
    {
        Gate::forUser($user)->authorize('transition',$loan);

        return DB::transaction(function () use ($loan, $user, $comments): Loan {
            $locked = Loan::query()
                ->with(['branch.business','customer'])
                ->lockForUpdate()
                ->findOrFail($loan->id);

            if (! $locked->canTransitionTo('under_review')) {
                throw ValidationException::withMessages(['status'=>"Loan cannot be submitted for approval from {$locked->status}."]);
            }

            $snapshot = $this->risk->snapshotForLoan($locked, 'loan_application_submitted');
            $locked->update([
                'application_credit_score_history_id' => $snapshot->id,
                'updated_by' => $user->id,
            ]);

            $business = $locked->branch->business;
            if ($business->loan_approval_mode === 'auto_authorized'
                && $user->can('loans.auto-approve')
                && ! $this->makerCheckerBlocks($locked, $user)
                && ! $this->managerThresholdBlocks($locked, $user)) {
                return $this->recordDecision($locked, $user, 'auto_approve', 'ready_for_disbursement', $comments ?: 'Automatically approved by authorized user.', true);
            }

            $from = $locked->status;
            $locked->update(['status'=>'under_review','updated_by'=>$user->id]);
            $this->history($locked, $from, 'under_review', $comments ?: 'Submitted for approval review.', $user);

            return $locked->fresh(['applicationCreditScoreHistory']);
        });
    }

    public function decide(Loan $loan, User $user, string $decision, ?string $comments = null): Loan
    {
        Gate::forUser($user)->authorize('approve',$loan);

        if ($loan->status !== 'under_review') {
            throw ValidationException::withMessages(['decision'=>'Only loans under review can receive an approval decision.']);
        }

        $target = match ($decision) {
            'approve' => 'ready_for_disbursement',
            'reject' => 'rejected',
            'request_correction' => 'correction_requested',
            'request_documents' => 'waiting_for_documents',
            'request_additional_collateral' => 'additional_collateral_requested',
            default => throw ValidationException::withMessages(['decision'=>'Unsupported approval decision.']),
        };

        if ($decision === 'approve') {
            if ($this->makerCheckerBlocks($loan, $user)) {
                throw ValidationException::withMessages(['decision'=>'Maker-checker is enabled. The creator cannot approve their own loan application.']);
            }
            if ($this->managerThresholdBlocks($loan, $user)) {
                throw ValidationException::withMessages(['decision'=>'This loan exceeds the manager approval threshold and requires a manager-authorized approver.']);
            }
        }

        if ($decision !== 'approve' && blank($comments)) {
            throw ValidationException::withMessages(['comments'=>'Comments are required for this decision.']);
        }

        return $this->recordDecision($loan, $user, $decision, $target, $comments, false);
    }

    public function requiresManagerApproval(Loan $loan): bool
    {
        $loan->loadMissing('branch.business');
        $threshold = $loan->branch->business->manager_approval_threshold;
        if ($threshold === null || $this->money->toMinor((string) $threshold) <= 0) return false;
        return $this->money->toMinor((string) $loan->principal) > $this->money->toMinor((string) $threshold);
    }

    public function makerCheckerBlocks(Loan $loan, User $user): bool
    {
        $loan->loadMissing('branch.business');
        return (bool) $loan->branch->business->maker_checker_enabled && (int) $loan->created_by === (int) $user->id;
    }

    private function managerThresholdBlocks(Loan $loan, User $user): bool
    {
        return $this->requiresManagerApproval($loan) && ! $user->can('loans.manager-approve');
    }

    private function recordDecision(Loan $loan, User $user, string $decision, string $target, ?string $comments, bool $isAuto): Loan
    {
        return DB::transaction(function () use ($loan, $user, $decision, $target, $comments, $isAuto): Loan {
            $from = $loan->status;
            $attributes = ['status'=>$target,'updated_by'=>$user->id];
            if ($target === 'ready_for_disbursement') {
                $attributes['approved_at'] = now();
                $attributes['approved_by'] = $user->id;
            }
            $loan->update($attributes);

            LoanApprovalDecision::create([
                'loan_id'=>$loan->id,
                'decision'=>$decision,
                'previous_status'=>$from,
                'new_status'=>$target,
                'comments'=>$comments,
                'user_id'=>$user->id,
                'is_auto'=>$isAuto,
                'decided_at'=>now(),
            ]);

            $label = str($decision)->replace('_',' ')->title()->toString();
            $this->history($loan, $from, $target, trim($label.($comments ? ': '.$comments : '')), $user);
            return $loan->fresh(['applicationCreditScoreHistory']);
        });
    }

    private function history(Loan $loan, ?string $from, string $to, ?string $notes, User $user): void
    {
        LoanStatusHistory::create([
            'loan_id'=>$loan->id,'from_status'=>$from,'to_status'=>$to,'notes'=>$notes,
            'changed_by'=>$user->id,'changed_at'=>now(),
        ]);
    }
}
