<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\EmiCard;
use App\Models\Loan;
use App\Models\LoanDetail;
use App\Models\LoanSetting;
use App\Models\LoanType;
use App\Models\User;
use App\Services\LoanCalculationService;
use App\Services\NotificationService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

class AdminLoanCreateController extends Controller
{
    /**
     * Show admin loan create page.
     */
    public function create()
    {
        $clients = User::query()
            ->where('role', 'client')
            ->orderBy('name')
            ->get([
                'id',
                'name',
                'email',
                'mobile',
            ]);

        $loanTypes = LoanType::query()
            ->where('status', true)
            ->orderBy('name')
            ->get();

        $emiCards = EmiCard::query()
            ->with('user')
            ->where('status', 'active')
            ->orderByDesc('id')
            ->get();

        $settings = LoanSetting::first();

        return view('admin.loans.create', compact(
            'clients',
            'loanTypes',
            'emiCards',
            'settings'
        ));
    }


    /**
     * Store admin-created loan.
     */
    public function store(
        Request $request,
        LoanCalculationService $calculator,
        NotificationService $notificationService
    ) {
        $validated = $request->validate([
            'user_id' => [
                'required',
                'integer',
                'exists:users,id',
            ],

            'emi_card_id' => [
                'required',
                'integer',
                'exists:emi_cards,id',
            ],

            'loan_type_id' => [
                'required',
                'integer',
                'exists:loan_types,id',
            ],

            'loan_amount' => [
                'required',
                'numeric',
                'min:0.01',
            ],

            'tenure_months' => [
                'required',
                'integer',
                'min:1',
            ],

            'details' => [
                'nullable',
                'array',
            ],

            'details.*' => [
                'nullable',
                'string',
                'max:500',
            ],
        ]);


        try {

            $loan = DB::transaction(function () use (
                $validated,
                $calculator
            ) {

                /*
                |--------------------------------------------------------------------------
                | Client
                |--------------------------------------------------------------------------
                */

                $user = User::where(
                    'id',
                    $validated['user_id']
                )
                    ->where(
                        'role',
                        'client'
                    )
                    ->first();


                if (!$user) {

                    throw ValidationException::withMessages([
                        'user_id' =>
                            'Selected client is not valid.',
                    ]);
                }


                /*
                |--------------------------------------------------------------------------
                | Loan Type
                |--------------------------------------------------------------------------
                */

                $loanType = LoanType::where(
                    'id',
                    $validated['loan_type_id']
                )
                    ->where(
                        'status',
                        true
                    )
                    ->first();


                if (!$loanType) {

                    throw ValidationException::withMessages([
                        'loan_type_id' =>
                            'Selected loan type is not available.',
                    ]);
                }


                /*
                |--------------------------------------------------------------------------
                | Loan Settings
                |--------------------------------------------------------------------------
                */

                $settings =
                    LoanSetting::first();


                if (
                    !$settings ||
                    !$settings->enabled
                ) {

                    throw ValidationException::withMessages([
                        'loan_amount' =>
                            'Loan applications are currently unavailable.',
                    ]);
                }


                /*
                |--------------------------------------------------------------------------
                | EMI Card
                |--------------------------------------------------------------------------
                */

                $emiCard =
                    EmiCard::where(
                        'id',
                        $validated['emi_card_id']
                    )
                        ->where(
                            'user_id',
                            $user->id
                        )
                        ->lockForUpdate()
                        ->first();


                if (!$emiCard) {

                    throw ValidationException::withMessages([
                        'emi_card_id' =>
                            'Selected EMI card does not belong to the selected client.',
                    ]);
                }


                if (
                    $emiCard->status
                    !== 'active'
                ) {

                    throw ValidationException::withMessages([
                        'emi_card_id' =>
                            'Selected EMI card is not active.',
                    ]);
                }


                if (
                    $emiCard->expires_at &&
                    now()->greaterThan(
                        $emiCard->expires_at
                    )
                ) {

                    throw ValidationException::withMessages([
                        'emi_card_id' =>
                            'Selected EMI card has expired.',
                    ]);
                }


                /*
                |--------------------------------------------------------------------------
                | Loan Amount
                |--------------------------------------------------------------------------
                */

                $loanAmount =
                    (float) $validated['loan_amount'];


                $minimumAmount =
                    (float) $settings->minimum_loan_amount;


                $maximumAmount =
                    min(
                        (float) $settings->maximum_loan_amount,
                        (float) $emiCard->available_limit
                    );


                if (
                    $loanAmount
                    < $minimumAmount
                ) {

                    throw ValidationException::withMessages([
                        'loan_amount' =>
                            'Minimum loan amount is ' .
                            money($minimumAmount),
                    ]);
                }


                if (
                    $loanAmount
                    > $maximumAmount
                ) {

                    throw ValidationException::withMessages([
                        'loan_amount' =>
                            'Maximum available loan amount is ' .
                            money($maximumAmount),
                    ]);
                }


                /*
                |--------------------------------------------------------------------------
                | Tenure
                |--------------------------------------------------------------------------
                */

                $allowedTenures =
                    collect(
                        $settings->allowed_tenures ?? []
                    )
                        ->map(
                            fn ($tenure) =>
                                (int) $tenure
                        )
                        ->all();


                $tenure =
                    (int) $validated[
                        'tenure_months'
                    ];


                if (
                    $allowedTenures &&
                    !in_array(
                        $tenure,
                        $allowedTenures,
                        true
                    )
                ) {

                    throw ValidationException::withMessages([
                        'tenure_months' =>
                            'Selected loan tenure is not available.',
                    ]);
                }


                /*
                |--------------------------------------------------------------------------
                | Interest Rate
                |--------------------------------------------------------------------------
                |
                | Interest is controlled globally from Loan Settings.
                |
                | ON  -> Use selected Loan Type interest_rate
                | OFF -> 0%
                |
                */

                if (
                    (bool) (
                        $settings->interest_enabled ?? false
                    )
                ) {

                    $interestRate =
                        (float) (
                            $loanType->interest_rate ?? 0
                        );

                } else {

                    $interestRate = 0;
                }


                /*
                |--------------------------------------------------------------------------
                | Processing Fee
                |--------------------------------------------------------------------------
                */

                $processingFee =
                    (float) $settings->processing_fee;


                $processingFeeType =
                    $settings->processing_fee_type;


                /*
                |--------------------------------------------------------------------------
                | Calculate Loan
                |--------------------------------------------------------------------------
                */

                $calculation =
                    $calculator->calculate(

                        loanAmount:
                            $loanAmount,

                        interestRate:
                            $interestRate,

                        tenureMonths:
                            $tenure,

                        processingFee:
                            $processingFee,

                        processingFeeType:
                            $processingFeeType
                    );


                /*
                |--------------------------------------------------------------------------
                | Loan Number
                |--------------------------------------------------------------------------
                */

                $loanNumber =
                    $this->generateLoanNumber();


                /*
                |--------------------------------------------------------------------------
                | Create Loan
                |--------------------------------------------------------------------------
                */

                $loan = Loan::create([

                    'user_id' =>
                        $user->id,

                    'emi_card_id' =>
                        $emiCard->id,

                    'loan_number' =>
                        $loanNumber,

                    'loan_amount' =>
                        $calculation[
                            'loan_amount'
                        ],

                    'interest_rate' =>
                        $calculation[
                            'interest_rate'
                        ],

                    'tenure_months' =>
                        $calculation[
                            'tenure_months'
                        ],

                    'processing_fee' =>
                        $calculation[
                            'processing_fee'
                        ],

                    'processing_fee_paid_at' =>
                        null,

                    'processing_fee_paid_by' =>
                        null,

                    'interest_amount' =>
                        $calculation[
                            'interest_amount'
                        ],

                    'total_payable' =>
                        $calculation[
                            'total_payable'
                        ],

                    'emi_amount' =>
                        $calculation[
                            'emi_amount'
                        ],

                    'disbursed_amount' =>
                        0,

                    'outstanding_amount' =>
                        $calculation[
                            'total_payable'
                        ],

                    'applied_at' =>
                        now(),

                    'status' =>
                        'pending',
                ]);


                /*
                |--------------------------------------------------------------------------
                | Loan Details
                |--------------------------------------------------------------------------
                */

                LoanDetail::create([

                    'loan_id' =>
                        $loan->id,

                    'loan_type_id' =>
                        $loanType->id,

                    'details' =>
                        $this->cleanDetails(
                            $validated['details'] ?? []
                        ),
                ]);


                return $loan;
            });


        } catch (
            ValidationException $e
        ) {

            throw $e;

        } catch (
            \Throwable $e
        ) {

            report($e);

            return back()
                ->withInput()
                ->with(
                    'error',
                    'Unable to create loan. Please try again.'
                );
        }


        /*
        |--------------------------------------------------------------------------
        | Notification
        |--------------------------------------------------------------------------
        */

        $loan->refresh();

        $notificationService->loanApplied(
            $loan
        );


        return redirect()
            ->route(
                'admin.loans.show',
                $loan
            )
            ->with(
                'success',
                'Loan created successfully and sent for approval.'
            );
    }


    /**
     * Clean loan details.
     */
    private function cleanDetails(
        array $details
    ): array {

        return collect($details)
            ->mapWithKeys(
                function (
                    $value,
                    $key
                ) {

                    $value =
                        is_string($value)
                            ? trim($value)
                            : $value;


                    return [
                        $key =>
                            $value === ''
                                ? null
                                : $value,
                    ];
                }
            )
            ->filter(
                fn ($value) =>
                    !is_null($value)
            )
            ->all();
    }


    /**
     * Generate unique loan number.
     */
    private function generateLoanNumber(): string
    {
        do {

            $number =
                'LN-' .
                now()->format('Ymd') .
                '-' .
                strtoupper(
                    substr(
                        str_shuffle(
                            'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'
                        ),
                        0,
                        6
                    )
                );

        } while (
            Loan::where(
                'loan_number',
                $number
            )->exists()
        );


        return $number;
    }
}