<?php

use App\Models\AccountCryptoBalance;
use App\Models\CryptoWallet;
use App\Models\PaymentMethod;
use App\Support\Currency;
use Illuminate\Validation\ValidationException;
use Livewire\Component;
use Livewire\WithFileUploads;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use App\Models\Deposit as DepositModel;
use App\Models\Transaction;
use App\Notifications\TransactionNotification;
use BaconQrCode\Renderer\ImageRenderer;
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
use BaconQrCode\Writer;
use Illuminate\Http\UploadedFile;
use Illuminate\Validation\Rule;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Log;
use App\Services\AdminNotifier;


new class extends Component {
    use WithFileUploads;

    // Common properties
    public $paymentMethods = [];
    public $selectedMethod = null;
    public $instructions = [];
    public $fields = [];
    public $details = [];
    public $loading = false;

    // Payment method crypto (platform wallets)
    public $paymentMethodWallets = [];
    public $selectedPaymentMethodCoin = null;
    public $walletAddress = null;
    public $walletQrCode = null;

    protected ?PaymentMethod $cachedMethod = null;

    // User-related properties
    public $userAccounts = [];
    public $selectedAccount = null;
    public $userCryptoBalances; // AccountCryptoBalance collection
    public $selectedUserCryptoBalance = null; // <-- NEW: user's balance selection

    // Payment fields
    public $amount = '';
    public $charge = 0;
    public $total = 0;
    public $proof = '';
    public $currencySymbol;
    public $depositType = 'fiat'; // default 'fiat' or 'crypto'

    // Dynamic fields
    public $fieldValues = [];     // text/select/textarea fields
    public $dynamicUploads = [];  // file fields
    public string $idempotencyKey;

    // Base validation rules
    protected $baseRules = [
        'selectedMethod' => 'required|exists:payment_methods,id',
        'selectedAccount' => 'required',
        'amount' => 'required|numeric|min:1',
        'proof' => 'nullable|image|max:2048|mimes:jpg,jpeg,png,webp',
    ];

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

        // Load payment methods
        $this->paymentMethods = PaymentMethod::where('is_active', true)
            ->where('type', 'deposit')
            ->get();

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

        // Load user crypto balances (ALWAYS collection)
        $this->userCryptoBalances = $user->profile?->accounts()
            ->with('cryptoBalances')
            ->get()
            ->pluck('cryptoBalances')
            ->flatten() ?? collect([]);

        // Default account
        $this->selectedAccount = $this->userAccounts->first()?->id ?? null;

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

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


    public function resetSelection()
    {
        $this->selectedMethod = null;
        $this->reset([
            'amount',
            'selectedMethod',
            'proof',
            'charge',
            'total',
            'selectedPaymentMethodCoin',
            'instructions',
            'fields',
            'dynamicUploads',
            'details',
            'fieldValues',
            'walletAddress',
            'walletQrCode',
        ]);


        $this->resetErrorBag();
    }

    // Options for user crypto selection
    public function getUserCryptoOptionsProperty(): array
    {
        return $this->userCryptoBalances
            ->mapWithKeys(fn(AccountCryptoBalance $crypto) => [
                (string) $crypto->id => $crypto->label() . ' - ' . $crypto->available_balance
            ])
            ->toArray();
    }

    /**
     * Select a payment method
     */

    /**
     * Generate a QR code for a wallet address
     */
    public function generateQrCode($walletAddress)
    {
        if (!$walletAddress) {
            return null;
        }

        $renderer = new ImageRenderer(
            new RendererStyle(250),
            new \BaconQrCode\Renderer\Image\SvgImageBackEnd()
        );

        return (new Writer($renderer))->writeString($walletAddress);
    }

    public function selectMethod($methodId)
    {
        $this->reset([
            'amount',
            'selectedMethod',
            'proof',
            'charge',
            'total',
            'selectedPaymentMethodCoin',
            'instructions',
            'fields',
            'dynamicUploads',
            'details',
            'fieldValues',
            'walletAddress',
            'walletQrCode',
        ]);


        $this->resetErrorBag();

        $this->selectedMethod = $methodId;
        $this->loading = true;

        $method = PaymentMethod::find($methodId);
        $this->cachedMethod = $method;

        if ($method) {
            $this->instructions = $method->instructions['deposit'] ?? [];
            $this->fields = $method->fields['fields'] ?? [];
            $this->details = $method->details['deposit'] ?? [];

            // Initialize dynamic fields
            $this->fieldValues = [];
            $this->dynamicUploads = [];
            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;
                }
            }

            // Payment method crypto wallets
            if (strtolower($method->name) === 'crypto') {
                $this->paymentMethodWallets = CryptoWallet::where('is_active', true)->get();
                if ($this->paymentMethodWallets->isNotEmpty()) {
                    $this->selectedPaymentMethodCoin = $this->paymentMethodWallets[0]->code;
                    $this->walletAddress = $this->paymentMethodWallets[0]->address;
                    $this->walletQrCode = $this->generateQrCode($this->walletAddress);
                }

                // Default select user's crypto balance
                $this->selectedUserCryptoBalance = $this->userCryptoBalances->first()?->id ?? null;
            } else {
                $this->paymentMethodWallets = [];
                $this->selectedPaymentMethodCoin = null;
                $this->walletAddress = null;
                $this->walletQrCode = null;
                $this->selectedUserCryptoBalance = null;
            }
        }

        $this->loading = false;
    }

    // Update wallet info when method coin changes
    public function updatedSelectedPaymentMethodCoin()
    {
        if ($this->selectedPaymentMethodCoin) {
            $wallet = $this->paymentMethodWallets->firstWhere('code', $this->selectedPaymentMethodCoin);
            $this->walletAddress = $wallet ? $wallet->address : null;
            $this->walletQrCode = $wallet ? $this->generateQrCode($this->walletAddress) : null;
        }
        $this->calculateTotals();
    }

    // Ensure amount does not exceed user's crypto balance if crypto deposit
    public function updatedAmount()
    {
        $this->calculateTotals();

        if ($this->cachedMethod && strtolower($this->cachedMethod->name) === 'crypto' && $this->selectedUserCryptoBalance) {
            $balance = $this->userCryptoBalances->firstWhere('id', $this->selectedUserCryptoBalance)?->available_balance ?? 0;
            if (bccomp($this->amount, $balance, 8) === 1) {
                $this->addError('amount', "Amount exceeds your available balance ({$balance}).");
            } else {
                $this->clearError('amount');
            }
        }
    }

    public function updated($propertyName)
    {
        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', 'selectedPaymentMethodCoin'])) {
            $this->calculateTotals();
        }
    }

    protected function getValidationRules()
    {
        $rules = $this->baseRules;

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

            // Require payment method coin only if crypto
            if ($method && strtolower($method->name) === 'crypto') {
                $rules['selectedPaymentMethodCoin'] = [
                    'required',
                    Rule::exists('crypto_wallets', 'code')->where('is_active', true),
                ];

                $rules['selectedUserCryptoBalance'] = [
                    'required',
                    Rule::exists('account_crypto_balances', 'id')->where(function ($query) {
                        $query->where('available_balance', '>', 0);
                    }),
                ];

            }

            if ($this->userAccounts->isNotEmpty()) {
                $rules['selectedAccount'] = 'required|in:' . $this->userAccounts->pluck('id')->implode(',');
            }

            // Dynamic fields
            if ($method && $method->fields) {
                foreach ($method->fields['fields'] as $field) {
                    $fieldName = $field['name'];
                    if (!empty($field['required'])) {
                        $rules[
                            $field['type'] === 'file' ? "dynamicUploads.$fieldName" : "fieldValues.$fieldName"
                        ] = match ($field['type']) {
                            'file' => 'required|file|mimes:jpg,jpeg,png,webp|max:2048',
                            'text', 'textarea' => 'required|string|max:255',
                            'number' => 'required|numeric|min:1',
                            'select' => 'required|in:' . implode(',', array_map(fn($o) => $o['value'] ?? $o, $field['options'] ?? [])),
                            default => 'required',
                        };
                    } else {
                        $rules[
                            $field['type'] === 'file' ? "dynamicUploads.$fieldName" : "fieldValues.$fieldName"
                        ] = 'nullable';
                    }
                }
            }
        }

        return $rules;
    }

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

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

            if ($field['type'] === 'file') {
                $messages["dynamicUploads.$fieldName.required"] = "The {$label} field is required.";
                $messages["dynamicUploads.$fieldName.file"] = "The {$label} must be a valid file.";
                $messages["dynamicUploads.$fieldName.mimes"] = "The {$label} must be jpg, jpeg, png, or webp.";
                $messages["dynamicUploads.$fieldName.max"] = "The {$label} may not be greater than 2MB.";
            } else {
                $messages["fieldValues.$fieldName.required"] = "The {$label} field is required.";
                $messages["fieldValues.$fieldName.numeric"] = "The {$label} must be a number.";
                $messages["fieldValues.$fieldName.string"] = "The {$label} must be valid text.";
                $messages["fieldValues.$fieldName.max"] = "The {$label} may not exceed 255 characters.";
            }
        }

        if ($this->cachedMethod && strtolower($this->cachedMethod->name) === 'crypto') {
            $messages['selectedPaymentMethodCoin.required'] = 'Please select a cryptocurrency wallet.';
            $messages['selectedUserCryptoBalance.required'] = 'Please select a crypto balance to deposit from.';
        }

        return $messages;
    }

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

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

        if ($this->selectedMethod && $this->cachedMethod) {
            $this->charge = bcdiv(bcmul($amount, (string) $this->cachedMethod->fee_percent, 8), '100', 8);
            $this->total = bcadd($amount, $this->charge, 8);
        }
    }

    public function submitPayment()
    {
        $this->validate($this->getValidationRules(), $this->getValidationMessages());

        $user = auth()->user();
        $amount = (string) $this->amount;

        $paymentMethod = PaymentMethod::find($this->selectedMethod);
        if (!$paymentMethod) {
            $this->addError('selectedMethod', 'Invalid payment method selected.');
            return;
        }

        $account = $this->userAccounts->firstWhere('id', $this->selectedAccount);
        if (!$account) {
            $this->addError('selectedAccount', 'Selected account not found.');
            return;
        }

        $charge = bcdiv(bcmul($amount, (string) $paymentMethod->fee_percent, 8), '100', 8);
        $total = bcadd($amount, $charge, 8);

        // Handle uploaded files
        $proofPath = null;
        if ($this->proof instanceof UploadedFile) {
            $proofPath = $this->proof->storeAs('proofs', uniqid('proof_') . '.' . $this->proof->getClientOriginalExtension(), 'public');
        }

        $fieldPaths = [];
        foreach ($this->fields as $field) {
            $name = $field['name'];
            if ($field['type'] === 'file' && ($this->dynamicUploads[$name] ?? null) instanceof \Livewire\TemporaryUploadedFile) {
                $fieldPaths[$name] = $this->dynamicUploads[$name]->storeAs(
                    'proofs',
                    uniqid($name . '_') . '.' . $this->dynamicUploads[$name]->getClientOriginalExtension(),
                    'public'
                );
            } else {
                $fieldPaths[$name] = $this->fieldValues[$name] ?? null;
            }
        }

        $mainFilePath = $proofPath ?: collect($fieldPaths)->first(fn($v) => is_string($v) && str_starts_with($v, 'proofs/'));
        if (!$mainFilePath) {
            $this->addError('proof', 'A proof file is required.');
            return;
        }

        $cryptoWalletId = $this->selectedPaymentMethodCoin
            ? $this->paymentMethodWallets->firstWhere('code', $this->selectedPaymentMethodCoin)?->id
            : null;

        $userCryptoBalanceId = $this->selectedUserCryptoBalance;

        $idempotencyKey = $this->idempotencyKey;

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

        $uploadedFiles = array_filter([$proofPath] + $fieldPaths);

        try {
            DB::transaction(function () use ($account, $paymentMethod, $amount, $charge, $total, $fieldPaths, $mainFilePath, $cryptoWalletId, $userCryptoBalanceId, $idempotencyKey, $user) {
                $depositData = [
                    'account_id' => $account->id,
                    'idempotency_key' => $idempotencyKey,
                    'crypto_wallet_id' => $cryptoWalletId,
                    'payment_method_id' => $paymentMethod->id,
                    'amount' => $amount,
                    'charge' => $charge,
                    'total' => $total,
                    'file_path' => $mainFilePath,
                    'meta' => $fieldPaths,
                    'status' => DepositModel::STATUS_PENDING,
                    'deposit_type' => $this->depositType,
                ];

                // Only assign user_crypto_balance_id if deposit_type is crypto
                if ($this->depositType === 'crypto') {
                    $depositData['user_crypto_balance_id'] = $userCryptoBalanceId;
                }

                $deposit = DepositModel::create($depositData);


                $transaction = Transaction::create([
                    'account_id' => $account->id,
                    'deposit_id' => $deposit->id,
                    'idempotency_key' => $idempotencyKey,
                    'type' => 'deposit',
                    'method' => $this->selectedPaymentMethodCoin ?: $paymentMethod->name,
                    'amount' => $total,
                    'balance_after' => $account->available_balance,
                    'description' => 'Deposit via ' . ($this->selectedPaymentMethodCoin ?: $paymentMethod->name),
                    'status' => Transaction::STATUS_PENDING,
                ]);

                $user->notify(new TransactionNotification($transaction));
                // ✅ Notify admins
                AdminNotifier::notify('Deposit Submitted', [
                    'account_id' => $account->id,
                    'deposit_id' => $deposit->id,
                    'amount' => $total,
                    'method' => $this->selectedPaymentMethodCoin ?: $paymentMethod->name,
                ], $user);
            });

            $this->reset([
                'selectedMethod',
                'selectedPaymentMethodCoin',
                'selectedUserCryptoBalance',
                'amount',
                'charge',
                'total',
                'proof',
                'fields',
                'fieldValues',
                'dynamicUploads',
            ]);

            $this->idempotencyKey = (string) Str::uuid();
            $this->dispatch('file-upload-reset');
            $this->dispatch('notification-sent');
            $this->dispatch('transaction-success', type: 'deposit', amount: $total);

        } catch (\Throwable $e) {
            foreach ($uploadedFiles as $path) {
                Storage::disk('public')->delete($path);
            }
            Log::error('Deposit 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.');
        }
    }

    public function clearError($field)
    {
        $this->resetErrorBag($field);
    }
};
