<?php

use App\Models\Wallet;
use App\Models\WalletTransaction;
use App\Jobs\Vrm\SendMail;
use App\Mail\AdminTransactionMail;
use App\Mail\WalletTransactionMail;
use App\Services\DefaultCurrencyService;
use App\Services\MemberNotificationService;
use App\Services\Sms\SmsNotifier;
use App\Services\Trading\TradingSettingsService;
use App\Traits\Vrm\Livewire\WithNotifications;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Sleep;
use Illuminate\Validation\ValidationException;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Layout;
use Livewire\Component;

return new #[Layout('layouts.trading')] class extends Component {
    use WithNotifications;

    public string $step = 'method';
    public string $selectedMethod = '';
    public string $withdrawAmount = '';
    public string $mpesaPhone = '';
    public string $bankName = '';
    public string $bankAccountNumber = '';
    public string $bankAccountHolderName = '';
    public float $submittedAmount = 0.0;
    public ?array $finalToast = null;

    public function mount(): void
    {
        $this->useToasts();
    }

    #[Computed]
    public function wallet(): Wallet
    {
        return Wallet::query()->firstOrCreate(['user_id' => auth()->id()], ['currency_code' => app(DefaultCurrencyService::class)->code()]);
    }

    #[Computed]
    public function currencyCode(): string
    {
        return app(DefaultCurrencyService::class)->code();
    }

    #[Computed]
    public function withdrawalLimits(): array
    {
        $settings = app(TradingSettingsService::class);
        $channel = in_array($this->selectedMethod, ['mpesa', 'bank'], true) ? $this->selectedMethod : 'mpesa';

        return $settings->withdrawalLimits($channel);
    }

    public function selectMethod(string $method): void
    {
        if (!in_array($method, ['mpesa', 'bank'], true)) {
            return;
        }

        $this->selectedMethod = $method;
    }

    public function proceedToDetails(): void
    {
        if ($this->selectedMethod === '') {
            $this->notifyError(__('Select a withdrawal method first.'));

            return;
        }

        $this->step = 'details';
    }

    public function backToMethods(): void
    {
        $this->step = 'method';
    }

    public function backToDetails(): void
    {
        $this->step = 'details';
    }

    public function goToConfirmation(): void
    {
        if (!$this->canSubmitWithdrawal()) {
            return;
        }

        $this->step = 'confirm';
    }

    public function updatedWithdrawAmount(): void
    {
        $this->resetErrorBag('withdrawAmount');

        if ($this->withdrawAmount === '' || !is_numeric($this->withdrawAmount)) {
            return;
        }

        $amount = (float) $this->withdrawAmount;
        $limits = $this->withdrawalLimits;

        if ($amount <= 0) {
            $this->addError('withdrawAmount', __('Enter a valid withdrawal amount.'));

            return;
        }

        if ($amount < $limits['min']) {
            $this->addError('withdrawAmount', __('Withdrawal amount must be at least :min.', ['min' => number_format($limits['min'], 2)]));

            return;
        }

        if ($limits['max'] > 0 && $amount > $limits['max']) {
            $this->addError('withdrawAmount', __('Withdrawal amount must not exceed :max.', ['max' => number_format($limits['max'], 2)]));

            return;
        }

        if ($amount > $this->wallet->available_balance) {
            $this->addError('withdrawAmount', __('Insufficient available balance for this withdrawal.'));
        }
    }

    public function submitWithdrawalRequest(): void
    {
        if (!$this->canSubmitWithdrawal()) {
            return;
        }

        Sleep::for(7)->seconds();

        $amount = (float) $this->withdrawAmount;
        $transactionId = null;
        $meta = [
            'channel' => $this->selectedMethod,
        ];

        if ($this->selectedMethod === 'mpesa') {
            $meta['phone_number'] = $this->mpesaPhone;
        }

        if ($this->selectedMethod === 'bank') {
            $meta['bank_name'] = $this->bankName;
            $meta['account_number'] = $this->bankAccountNumber;
            $meta['account_holder_name'] = $this->bankAccountHolderName;
        }

        DB::transaction(function () use ($amount, $meta, &$transactionId): void {
            $wallet = Wallet::query()->whereKey($this->wallet->id)->lockForUpdate()->firstOrFail();

            if ((float) $wallet->available_balance < $amount) {
                throw ValidationException::withMessages([
                    'withdrawAmount' => [__('Insufficient available balance for this withdrawal.')],
                ]);
            }

            if (Schema::hasColumn('wallets', 'withdrawal_hold_amount')) {
                $wallet->withdrawal_hold_amount = (float) $wallet->withdrawal_hold_amount + $amount;
            } else {
                $wallet->used_amount = (float) $wallet->used_amount + $amount;
            }
            $wallet->save();

            $transaction = WalletTransaction::query()->create([
                'wallet_id' => $wallet->id,
                'user_id' => auth()->id(),
                'type' => 'withdrawal_request',
                'status' => 'pending',
                'amount' => $amount,
                'currency_code' => app(DefaultCurrencyService::class)->code(),
                'description' => 'Wallet withdrawal request',
                'meta' => $meta,
            ]);

            $transactionId = $transaction->id;
        });

        $transaction = $transactionId ? WalletTransaction::query()->find($transactionId) : null;

        if ($transaction) {
            app(MemberNotificationService::class)->notifyWithdrawalRequested($transaction);
        }

        $adminWithdrawalMessage = 'Withdrawal request submitted by '.auth()->user()->name.' : '.app(DefaultCurrencyService::class)->code().' '.number_format($amount, 2);
        app(SmsNotifier::class)->sendToAdmins($adminWithdrawalMessage, 'admin_transaction');
        app(SmsNotifier::class)->sendToPhone(
            $this->selectedMethod === 'mpesa' ? $this->mpesaPhone : auth()->user()->phone,
            'Withdrawal request submitted successfully and is pending approval.',
            'user_transaction'
        );

        $adminEmail = config('mail.admin_address');
        if (filled($adminEmail)) {
            try {
                SendMail::dispatch((string) $adminEmail, mailable: new AdminTransactionMail(
                    auth()->user()->name,
                    auth()->user()->email,
                    $amount,
                    app(DefaultCurrencyService::class)->code(),
                    'withdrawal',
                    'submitted',
                    $this->selectedMethod,
                    null,
                ), notificationType: 'admin_transaction');
            } catch (\Throwable $exception) {
                \Illuminate\Support\Facades\Log::warning('Admin withdrawal email dispatch failed.', [
                    'transaction_id' => $transaction?->id,
                    'message' => $exception->getMessage(),
                ]);
            }
        }
        try {
            SendMail::dispatch((string) auth()->user()->email, mailable: new WalletTransactionMail(
                auth()->user()->name,
                'withdrawal_request',
                app(DefaultCurrencyService::class)->code(),
                $amount,
                'pending',
                $this->selectedMethod === 'mpesa' ? $this->mpesaPhone : auth()->user()->phone,
                $transaction?->transaction_code,
                $transaction?->created_at?->format('M j, Y g:i A'),
            ), notificationType: 'user_transaction');
        } catch (\Throwable $exception) {
            \Illuminate\Support\Facades\Log::warning('Member withdrawal email dispatch failed.', [
                'transaction_id' => $transaction?->id,
                'message' => $exception->getMessage(),
            ]);
        }

        $this->submittedAmount = $amount;
        $this->step = 'result';
        $this->finalToast = [
            'title' => __('Withdrawal Submitted!'),
            'description' => __('Your withdrawal of :amount has been submitted successfully.', ['amount' => $this->currencyCode . ' ' . number_format($amount, 2)]),
        ];
    }

    public function finishFlow(): void
    {
        $this->redirectRoute('member.account', navigate: true);
    }

    public function clearFinalToast(): void
    {
        $this->finalToast = null;
    }

    protected function canSubmitWithdrawal(): bool
    {
        $this->updatedWithdrawAmount();
        if ($this->getErrorBag()->has('withdrawAmount')) {
            throw ValidationException::withMessages([
                'withdrawAmount' => $this->getErrorBag()->get('withdrawAmount'),
            ]);
        }

        $amount = (float) $this->withdrawAmount;

        if ($this->selectedMethod === '') {
            $this->notifyError(__('Select a withdrawal method first.'));

            return false;
        }

        if ($this->selectedMethod === 'mpesa') {
            $this->validate([
                'mpesaPhone' => ['required', 'string', 'max:13', 'regex:/^(?:\+254|254|0)(?:7|1)\d{8}$/'],
            ]);
        }

        if ($this->selectedMethod === 'bank') {
            $this->validate([
                'bankName' => ['required', 'string', 'max:255'],
                'bankAccountNumber' => ['required', 'string', 'max:100'],
                'bankAccountHolderName' => ['required', 'string', 'max:255'],
            ]);
        }

        return true;
    }

    #[Computed]
    public function mpesaDisplayPhone(): string
    {
        $digits = preg_replace('/\D+/', '', $this->mpesaPhone);

        if (str_starts_with($digits, '254')) {
            return '0' . substr($digits, 3);
        }

        if (str_starts_with($digits, '07') || str_starts_with($digits, '01')) {
            return $digits;
        }

        return $this->mpesaPhone;
    }

    protected function view($data = [])
    {
        return app('view')->file('/home/ziidfelj/home.ziidi.trade/storage/framework/views/livewire/views/f4d2c2b9.blade.php', $data);
    }
}; 