<?php

use App\Services\PinService;
use App\Traits\RequiresPin;
use Illuminate\Support\Facades\Log;
use Livewire\Component;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use App\Models\{
    CryptoWallet,
    PaymentMethod,
    WalletReserve,
    Withdrawal as WithdrawalModel,
    Transaction
};
use App\Notifications\TransactionNotification;
use App\Support\Currency;
use Illuminate\Validation\Rule;
use Illuminate\Support\Str;
use App\Services\AdminNotifier;


new class extends Component {
    use RequiresPin;
    public $paymentMethods = [];
    public $accounts = [];
    public $cryptoWallets = [];


    public $selectedMethod = null;
    public $selectedAccount = null;
    public $selectedCoin = null;

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

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

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

    public $currencySymbol;
    public string $idempotencyKey;

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

    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;
            }

            $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(
                        array_map(fn($o) => $o['value'] ?? $o, $field['options'] ?? [])
                    )
                ],
                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.';
        }

        return $messages;
    }

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

    public function mount()
    {
        $user = auth()->user();
        $this->idempotencyKey = (string) Str::uuid();
        $this->paymentMethods = PaymentMethod::where('type', 'withdrawal')
            ->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 withdrawal 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();

        // Reset only transient properties related to the form
        $this->reset([
            'amount',
            'fee',
            'total',
            'selectedCoin',
            'instructions',
            'details',
            'fields',
            'fieldValues',
            '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['withdrawal'] ?? [];
        $this->details = $method->details['withdrawal'] ?? [];
        $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']] = ''; // <- empty string
            } 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)
    {
        // Only validate relevant properties
        if (
            !property_exists($this, $propertyName) &&
            !str_starts_with($propertyName, 'dynamicUploads.') &&
            !str_starts_with($propertyName, 'fieldValues.')
        ) {
            return;
        }

        $this->validateOnly($propertyName, $this->getValidationRules(), $this->getValidationMessages());
        if (in_array($propertyName, ['amount', 'selectedCoin'])) {
            $this->calculateTotals();
        }
    }

    /* =========================
     |  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);
            }
        }
    }


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

    public function submitWithdrawal(PinService $pinService)
    {
        $this->validate($this->getValidationRules(), $this->getValidationMessages());
        $user = auth()->user();
        $method = $this->getSelectedMethod();
        $amount = (string) $this->amount;
        $fee = bcdiv(bcmul((string) $method->fee_percent, $amount, 8), '100', 2);
        $total = bcadd($amount, $fee, 2); // amount + fee
        $account = $this->accounts->firstWhere('id', $this->selectedAccount);

        // KYC and balance checks
        if (!$user->kyc_verified) {
            $this->addError('selectedAccount', 'Complete KYC before withdrawing.');
            return;
        }

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

        if (bccomp($total, (string) $account->available_balance, 2) === 1) {
            $this->addError('selectedAccount', 'Insufficient balance.');
            return;
        }
        $this->requirePin($pinService, function () use ($pinService) {
            // Verify the PIN entered by the user
            if (!$this->verifyPin($pinService)) {
                return; // stops if PIN verification fails
            }

            // PIN is verified → proceed with withdrawal
            $this->processWithdrawal();
        });
    }

    private function processWithdrawal()
    {
        $user = auth()->user();
        $account = $this->accounts->firstWhere('id', $this->selectedAccount);
        $method = $this->getSelectedMethod();
        $amount = (string) $this->amount;

        $fee = bcdiv(bcmul((string) $method->fee_percent, $amount, 8), '100', 2);
        $total = bcadd($amount, $fee, 2);
        $idempotencyKey = $this->idempotencyKey;

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

        $files = [];

        try {
            DB::transaction(function () use ($user, $account, $method, $amount, $fee, $total, $idempotencyKey, &$files) {
                // Store uploaded files
                foreach ($this->dynamicUploads as $key => $file) {
                    if ($file) {
                        $files[$key] = $file->store('withdrawals', 'public');
                    }
                }

                $meta = array_merge($this->fieldValues, $files);
                if ($this->selectedCoin) {
                    $meta['coin'] = $this->selectedCoin;
                }

                // Create pending withdrawal
                $withdrawal = WithdrawalModel::create([
                    'account_id' => $account->id,
                    'idempotency_key' => $idempotencyKey,
                    'payment_method_id' => $this->selectedMethod,
                    'amount' => $amount,
                    'fee' => $fee,
                    'total' => $total,
                    'meta' => $meta,
                    'status' => 'pending',
                ]);

                // Reserve the funds
                WalletReserve::create([
                    'account_id' => $account->id,
                    'action_type' => 'withdrawal',
                    'action_id' => $withdrawal->id,
                    'amount' => $total,
                    'status' => WalletReserve::STATUS_PENDING,
                    'locked_at' => now(),
                ]);

                // Create pending transaction for transparency (optional)
                Transaction::create([
                    'user_id' => $user->id,
                    'idempotency_key' => $idempotencyKey,
                    'account_id' => $account->id,
                    'withdrawal_id' => $withdrawal->id,
                    'type' => 'withdrawal',
                    'amount' => $total,
                    'status' => Transaction::STATUS_PENDING,
                    'balance_after' => $account->balance, // no balance deduction yet
                    'description' => 'Withdrawal request via ' . $method->name,
                ]);

                // Notify user of request submission
                $user->notify(new TransactionNotification(
                    Transaction::where('idempotency_key', $idempotencyKey)->first()
                ));
                // Notify admins about the new withdrawal
                AdminNotifier::notify('Withdrawal Submitted', [
                    'withdrawal_id' => $withdrawal->id,
                    'account_id' => $account->id,
                    'amount' => $total,
                    'method' => $method->name,
                    'coin' => $this->selectedCoin,
                ], $user);

            });

            // Reset idempotency key and form
            $this->idempotencyKey = (string) Str::uuid();
            $this->dispatch('transaction-success', type: 'withdrawal', amount: $total);
            $this->dispatch('notification-sent');
            $this->resetPinState();
            $this->resetForm();

        } catch (\Throwable $e) {
            // Rollback is automatic
            foreach ($files as $path) {
                Storage::disk('public')->delete($path);
            }

            Log::error('Withdrawal submission error', [
                'exception' => $e,
                'user_id' => $user->id ?? null,
                'account_id' => $account->id ?? null,
                'amount' => $amount,
            ]);

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



};
