<?php

use App\Models\Beneficiary;
use App\Models\CryptoWallet;
use App\Models\PaymentMethod;
use App\Models\Transaction;
use App\Models\Transfer;
use App\Models\WalletReserve;
use App\Notifications\TransactionNotification;
use App\Services\PinService;
use App\Services\TransactionLimitService;
use App\Traits\RequiresConfirmation;
use App\Traits\RequiresPin;
use Livewire\Attributes\On;
use Livewire\Component;
use App\Support\Currency;
use Illuminate\Validation\Rule;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use App\Services\AdminNotifier;
new class extends Component {
    use RequiresPin, RequiresConfirmation;

    public $paymentMethods = [];
    public $accounts = [];
    public $cryptoWallets = [];


    public $selectedMethod = null;
    public $selectedAccount = null;
    public $selectedCoin = null;
    private ?string $lastCheckedAccount = null;


    public $amount = '';
    public $fee = 0;
    public $total = 0;

    public $instructions = [];
    public $details = [];
    public $fields = [];

    public $fieldValues = [];
    public $dynamicUploads = [];
    public $currencySymbol;

    // === Prevalidated transaction data ===
    public bool $autoFilling = false;
    public string $idempotencyKey;

    public string $accountName = '';

    /* =========================
    |  Validation
    ========================= */


    public function updatedFieldValuesAccountNumber($value): void
    {
        $value = (string) $value;
        $this->accountName = '';

        if ($value === '' || strlen($value) < 8) {
            $this->lastCheckedAccount = null;
            $this->accountName = '';
            return;
        }

        // Skip duplicate lookups
        if ($this->lastCheckedAccount === $value) {
            return;
        }

        $this->lastCheckedAccount = $value;
        $this->accountName = $this->checkAccountNumber($value);
    }

    public function updatedSelectedMethod($value)
    {
        $this->dispatch(
            'pages::user.transfer.beneficiaries-card',
            'paymentMethodChanged',
            $value
        );
    }


    protected function getValidationRules()
    {
        $rules = [
            'selectedMethod' => ['required', Rule::exists('payment_methods', 'id')->where('is_active', 1)],
            'selectedAccount' => 'required',
            'amount' => 'required|numeric|min:1',
        ];

        foreach ($this->fields as $field) {
            $name = $field['name'];

            if (empty($field['required'])) {
                $rules["fieldValues.$name"] = 'nullable';
                continue;
            }

            if ($name === 'account_number') {
                $rules["fieldValues.$name"] = 'required|string|regex:/^[0-9]+$/|min:8|max:20';
                continue;
            }

            $rules[
                $field['type'] === 'file'
                ? "dynamicUploads.$name"
                : "fieldValues.$name"
            ] = match ($field['type']) {
                'file' => 'required|file|mimes:jpg,jpeg,png,pdf|max:2048',
                'number' => 'required|numeric|min:1',
                'select' => [
                    'required',
                    Rule::in(
                        collect($field['options'] ?? [])
                            ->pluck('value')
                            ->map(fn($v) => (string) $v)
                            ->toArray()
                    ),
                ],
                default => 'required|string|max:255',
            };

        }

        if ($this->isCrypto()) {
            $rules['selectedCoin'] = 'required|string';
        }

        return $rules;
    }

    protected function getValidationMessages()
    {
        $messages = [];

        foreach ($this->fields as $field) {
            $name = $field['name'];
            $label = $field['label'] ?? ucfirst(str_replace('_', ' ', $name));

            if ($field['type'] === 'file') {
                $messages["dynamicUploads.$name.required"] = "{$label} is required.";
                $messages["dynamicUploads.$name.file"] = "{$label} must be a valid file.";
                $messages["dynamicUploads.$name.mimes"] = "{$label} must be jpg, png or pdf.";
                $messages["dynamicUploads.$name.max"] = "{$label} may not exceed 2MB.";
            } else {
                $messages["fieldValues.$name.required"] = "{$label} is required.";
                $messages["fieldValues.$name.numeric"] = "{$label} must be numeric.";
                $messages["fieldValues.$name.max"] = "{$label} may not exceed 255 characters.";
            }
        }

        if ($this->isCrypto()) {
            $messages['selectedCoin.required'] = 'Please select a cryptocurrency.';
        }

        if ($name === 'account_number') {
            $messages["fieldValues.$name.required"] = "Account number is required.";
            $messages["fieldValues.$name.regex"] = "Account number must contain digits only.";
            $messages["fieldValues.$name.min"] = "Account number must be at least 8 digits.";
            $messages["fieldValues.$name.max"] = "Account number may not exceed 20 digits.";
        }

        $messages['pin.required'] = 'Enter a valid transaction PIN.';
        $messages['pin.digits'] = 'PIN must be exactly 4 digits.';

        return $messages;
    }

    /* =========================
     |  Lifecycle
     ========================= */

    public function mount()
    {
        $user = auth()->user();
        $this->idempotencyKey = (string) Str::uuid();

        $this->paymentMethods = PaymentMethod::where('type', 'local_transfer')
            ->where('is_active', true)
            ->get();

        $this->accounts = $user->profile
            ? $user->profile->accounts()->where('is_active', true)->get()
            : collect();

        $this->selectedAccount = $this->accounts->first()?->id;

        $this->currencySymbol = $user->profile
            ? Currency::symbol($user->profile->currency)
            : '$';

        // ✅ Auto-select first method
        if ($this->paymentMethods->isNotEmpty()) {
            $this->selectMethod($this->paymentMethods->first()->id);
        }
    }


    /* =========================
     |  Helpers
     ========================= */

    private function getSelectedMethod()
    {
        return $this->paymentMethods->firstWhere('id', $this->selectedMethod);
    }

    private function isCrypto(): bool
    {
        return strtolower($this->getSelectedMethod()?->name ?? '') === 'crypto';
    }
    /* =========================
     |  UI Events
     ========================= */

    private function resetForm()
    {
        $this->resetErrorBag();

        $this->reset([
            'amount',
            'fee',
            'total',
            'selectedCoin',
            'instructions',
            'details',
            'fields',
            'fieldValues',
            'accountName',
            'dynamicUploads',
        ]);
    }


    public function selectMethod($methodId)
    {
        $this->resetForm(); // reset only form-related properties
        $this->selectedMethod = $methodId;
        $method = $this->getSelectedMethod();

        if (!$method) {
            return;
        }

        $this->instructions = $method->instructions['local_transfer'] ?? [];
        $this->details = $method->details['local_transfer'] ?? [];
        $this->fields = $method->fields['fields'] ?? [];

        foreach ($this->fields as $field) {
            if ($field['type'] === 'file') {
                $this->dynamicUploads[$field['name']] = null;
            } elseif ($field['type'] === 'select') {
                $this->fieldValues[$field['name']] = '';

            } else {
                $this->fieldValues[$field['name']] = null;
            }
        }

        if ($this->isCrypto()) {
            $this->cryptoWallets = CryptoWallet::where('is_active', true)->get();
            $this->selectedCoin = $this->cryptoWallets->first()?->code;
        }

        $this->calculateTotals();
    }

    public function updated($propertyName, $value)
    {
        if (
            str_starts_with($propertyName, 'dynamicUploads.') ||
            str_starts_with($propertyName, 'fieldValues.') ||
            in_array($propertyName, ['amount', 'selectedAccount'])
        ) {
            $this->validateOnly($propertyName, $this->getValidationRules(), $this->getValidationMessages());
        }
    }



    /* =========================
     |  Calculations
     ========================= */

    public function calculateTotals()
    {
        if (!is_numeric($this->amount)) {
            $this->fee = $this->total = 0;
            return;
        }

        $amount = (string) $this->amount;
        $this->fee = '0';
        $this->total = $amount;

        if ($this->selectedMethod) {
            $method = $this->getSelectedMethod();

            if ($method) {
                // Determine decimals: crypto = 8, fiat = 2
                $decimals = $this->isCrypto() ? 8 : 2;

                $this->fee = bcdiv(
                    bcmul($amount, (string) $method->fee_percent, $decimals),
                    '100',
                    $decimals
                );

                $this->total = bcadd($amount, $this->fee, $decimals);
            }
        }
    }


    public function checkAccountNumber(?string $accountNumber = ''): string
    {
        $accountNumber = (string) $accountNumber;

        if ($accountNumber === '') {
            $this->getErrorBag()->forget('fieldValues.account_number');
            return '';
        }

        $user = auth()->user();

        // Load account with its profile and user
        $account = \App\Models\Account::where('account_number', $accountNumber)
            ->with('profile.user')
            ->first();

        if (!$account || !$account->profile || !$account->profile->user) {
            $this->addError("fieldValues.account_number", "Account number not found");
            return '';
        }

        // Prevent transferring to self
        if ($account->profile->user->id === $user->id) {
            $this->addError("fieldValues.account_number", "You cannot transfer to your own account.");
            return '';
        }

        $this->getErrorBag()->forget('fieldValues.account_number');

        // Return the linked user's name
        return (string) $account->profile->user->name;
    }


    #[On('beneficiary-selected')]
    public function beneficiarySelected(int $id): void
    {
        $beneficiary = Beneficiary::find($id);

        if (!$beneficiary) {
            return;
        }

        $this->fieldValues['account_number'] = $beneficiary->account_number;

        // ✅ Make sure accountName is set
        $this->accountName = $this->checkAccountNumber($beneficiary->account_number);
    }
    public function updatedAmount($value)
    {
        $this->calculateTotals();
    }

    /**
     * Recalculate totals when coin changes
     */
    public function updatedSelectedCoin()
    {
        $this->calculateTotals();
    }
    public function resetSelection()
    {
        $this->selectedMethod = null;
        $this->resetForm(); // reset only form-related properties
    }

    /* =========================
     |  Submit
     ========================= */

    /**
     * Step 1: Validate form data (except PIN) and open confirmation modal
     */

    public function validateAndShowConfirmation(PinService $pinService, TransactionLimitService $limitService): void
    {
        // Step 1: validate inputs
        $this->validate(
            $this->getValidationRules(),
            $this->getValidationMessages()
        );

        $user = auth()->user();
        $account = $this->accounts->firstWhere('id', $this->selectedAccount);

        if (!$user->kyc_verified) {
            $this->addError('selectedAccount', 'Complete KYC before initiating transfer.');
            return;
        }

        if (!$account) {
            $this->addError('selectedAccount', 'Invalid account selected.');
            return;
        }

        if (bccomp((string) $this->total, (string) $account->available_balance, 2) === 1) {
            $this->addError('selectedAccount', 'Insufficient balance including fees.');
            return;
        }

        $limitCheck = $limitService->check($account, (string) $this->total);

        if (!$limitCheck['allowed']) {
            $this->addError('amount', $limitCheck['message']);
            return;
        }

        // Step 2: require PIN before confirmation
        $this->requirePin($pinService, function () {

            // Snapshot *everything* needed for execution
            $this->validateAndConfirm(
                data: [
                    'selectedMethod' => $this->selectedMethod,
                    'selectedAccount' => $this->selectedAccount,
                    'selectedCoin' => $this->selectedCoin,
                    'amount' => $this->amount,
                    'fee' => $this->fee,
                    'total' => $this->total,
                    'fieldValues' => [...$this->fieldValues],
                    'dynamicUploads' => $this->dynamicUploads,
                    'instructions' => $this->instructions,
                    'details' => $this->details,
                ],
                rules: [], // already validated above
            );
        });
    }


    /**
     * Step 2: Submit transfer after PIN confirmation
     */
    public function confirmTransfer(PinService $pinService): void
    {
        if (!$this->verifyPin($pinService)) {
            return;
        }

        $this->confirmAction(function (array $data) {
            $this->submitTransferInternal($data);
        });
    }

    private function submitTransferInternal(array $data): void
    {
        $user = auth()->user();

        $account = $this->accounts->firstWhere('id', $data['selectedAccount']);
        $method = $this->paymentMethods->firstWhere('id', $data['selectedMethod']);

        $idempotencyKey = $this->idempotencyKey;


        if (Transfer::where('idempotency_key', $idempotencyKey)->exists()) {
            $this->addError('error', 'This transfer has already been submitted.');
            return;
        }


        DB::beginTransaction();

        try {
            $files = [];

            foreach ($data['dynamicUploads'] as $key => $file) {
                if ($file) {
                    $files[$key] = $file->store('transfers', 'public');
                }
            }

            $meta = array_merge($data['fieldValues'], $files);

            $transfer = Transfer::create([
                'account_id' => $account->id,
                'idempotency_key' => $idempotencyKey,
                'payment_method_id' => $method->id,
                'type' => Transfer::TYPE_LOCAL,
                'amount' => $data['amount'],
                'fee' => $data['fee'],
                'total' => $data['total'],
                'meta' => $meta,
                'status' => Transfer::STATUS_PENDING,
            ]);

            WalletReserve::create([
                'account_id' => $account->id,
                'action_type' => 'transfer',
                'amount' => $data['total'],
                'status' => 'pending',
                'locked_at' => now(),
            ]);

            $transaction = Transaction::create([
                'user_id' => $user->id,
                'account_id' => $account->id,
                'transfer_id' => $transfer->id,
                'type' => 'transfer_out',
                'method' => $transfer->type,
                'amount' => $data['total'],
                'status' => Transaction::STATUS_PENDING,
                'balance_after' => $account->balance,
                'description' => 'local transfer',
            ]);

            $user->notify(new TransactionNotification($transaction));

            AdminNotifier::notify('Local Transfer Submitted', [
                'transfer_id' => $transaction->id,
                'account_id' => $account->id,
                'amount' => $transaction->amount,
                'method' => $method->name,
                'coin' => $this->selectedCoin, // null if not crypto
            ], $user);

            DB::commit();
            // Reset idempotency key for next action
            $this->idempotencyKey = (string) Str::uuid();
            $this->dispatch('transaction-success', type: 'transfer', amount: $data['total']);
            $this->dispatch('notification-sent');
            $this->resetPinState(); // clear verified PIN from memory
            $this->resetForm();

        } catch (\Throwable $e) {
            DB::rollBack();

            foreach ($files ?? [] as $path) {
                Storage::disk('public')->delete($path);
            }

            Log::error('International transfer error', [
                'exception' => $e,
                'user_id' => $user->id,
            ]);

            $this->addError('error', 'Something went wrong. Please try again.');
        }
    }


};