<?php

namespace App\Services\Payments;

use App\Jobs\Vrm\SendMail;
use App\Mail\AdminDepositMail;
use App\Mail\AdminTransactionMail;
use App\Mail\DepositRequestRejectedMail;
use App\Mail\WalletTransactionMail;
use App\Mail\WithdrawalRequestStatusMail;
use App\Models\InvestmentPackage;
use App\Models\User;
use App\Models\Wallet;
use App\Models\WalletTransaction;
use App\Services\Investment\SubscriptionService;
use App\Services\MemberNotificationService;
use App\Services\Sms\SmsNotifier;
use App\Services\Vrm\InvestmentEmailService;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Throwable;

class PaymentSettlementService
{
    /**
     * Settle a transaction (auto-detect type).
     */
    public function settle(WalletTransaction $transaction, ?string $transactionCode = null): bool
    {
        if (in_array($transaction->type, ['deposit_request', 'mpesa_deposit_request'], true)) {
            return $this->settleDeposit($transaction, $transactionCode);
        }

        if ($transaction->type === 'withdrawal_request') {
            return $this->settleWithdrawal($transaction);
        }

        return false;
    }

    /**
     * Settle a successful deposit.
     */
    public function settleDeposit(WalletTransaction $transaction, ?string $transactionCode = null): bool
    {
        if ($transaction->status === 'approved') {
            return true; // Already processed
        }

        try {
            DB::beginTransaction();

            $wallet = $transaction->wallet;
            if (! $wallet) {
                Log::error('Settlement Error: Wallet not found', ['transaction_id' => $transaction->id]);

                return false;
            }

            // Update wallet balance for deposit
            $wallet->balance = (float) $wallet->balance + (float) $transaction->amount;
            $wallet->save();

            // Update transaction status
            $transaction->status = 'approved';
            $transaction->transaction_code = $transactionCode ?: $transaction->transaction_code;
            $transaction->approved_at = now();
            $transaction->processed_at = now();
            $transaction->save();

            DB::commit();

            $this->sendNotifications($transaction, 'approved');
            $this->fulfillSubscriptionIntent($transaction);

            return true;
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Settlement Exception: '.$e->getMessage(), ['transaction_id' => $transaction->id]);

            return false;
        }
    }

    /**
     * Settle a withdrawal request.
     */
    public function settleWithdrawal(WalletTransaction $transaction): bool
    {
        if ($transaction->status === 'approved') {
            return true; // Already processed
        }

        try {
            DB::beginTransaction();

            $wallet = $transaction->wallet;
            if (! $wallet) {
                Log::error('Settlement Error: Wallet not found', ['transaction_id' => $transaction->id]);

                return false;
            }

            // Handle withdrawal logic
            if (\Illuminate\Support\Facades\Schema::hasColumn('wallets', 'withdrawal_hold_amount')) {
                $wallet->withdrawal_hold_amount = max(0, (float) $wallet->withdrawal_hold_amount - (float) $transaction->amount);
            } else {
                $wallet->used_amount = max(0, (float) $wallet->used_amount - (float) $transaction->amount);
            }
            $wallet->balance = max(0, (float) $wallet->balance - (float) $transaction->amount);
            $wallet->save();

            // Update transaction status
            $transaction->status = 'approved';
            $transaction->approved_at = now();
            $transaction->processed_at = now();
            $transaction->save();

            DB::commit();

            $this->sendNotifications($transaction, 'approved');

            return true;
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Withdrawal Settlement Exception: '.$e->getMessage(), ['transaction_id' => $transaction->id]);

            return false;
        }
    }

    /**
     * Mark a transaction as failed/rejected.
     */
    public function failTransaction(WalletTransaction $transaction, string $reason, ?array $providerResponse = null): void
    {
        if ($transaction->status !== 'pending') {
            return;
        }

        try {
            DB::beginTransaction();

            if ($transaction->type === 'withdrawal_request') {
                $wallet = $transaction->wallet;
                if ($wallet) {
                    if (\Illuminate\Support\Facades\Schema::hasColumn('wallets', 'withdrawal_hold_amount')) {
                        $wallet->withdrawal_hold_amount = max(0, (float) $wallet->withdrawal_hold_amount - (float) $transaction->amount);
                    } else {
                        $wallet->used_amount = max(0, (float) $wallet->used_amount - (float) $transaction->amount);
                    }
                    $wallet->save();
                }
            }

            $transaction->status = 'failed';
            if ($transaction->type === 'mpesa_deposit_request' || $transaction->provider === 'mpesa') {
                $transaction->status = 'failed';
            } else {
                // For admin rejection, we might use 'rejected'
                $transaction->status = 'rejected';
            }

            $transaction->provider_status = $reason;
            if ($providerResponse) {
                $transaction->provider_response = $providerResponse;
            }
            $transaction->processed_at = now();
            $transaction->save();

            DB::commit();

            $this->sendNotifications($transaction, $transaction->status);
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Fail Transaction Exception: '.$e->getMessage(), ['transaction_id' => $transaction->id]);
        }
    }

    /**
     * Send notifications for settled deposit.
     */
    protected function sendNotifications(WalletTransaction $transaction, string $status): void
    {
        $user = $transaction->user;
        if (! $user) {
            return;
        }

        $type = in_array($transaction->type, ['deposit_request', 'mpesa_deposit_request'], true) ? 'deposit' : 'withdrawal';

        // SMS Notification (priority path, independent of email)
        $statusMsg = $status === 'approved' ? 'successful' : $status;
        app(SmsNotifier::class)->sendToPhone(
            $transaction->phone_number ?: $user->phone,
            ucfirst($type)." of {$transaction->currency_code} ".number_format($transaction->amount, 2)." {$statusMsg}. Ref: ".($transaction->transaction_code ?: $transaction->id),
            'user_transaction'
        );

        // User Email
        if ($user->email) {
            if ($type === 'deposit' && in_array($status, ['rejected', 'failed'], true)) {
                $this->dispatchMailSafely($user->email, new DepositRequestRejectedMail(
                    $user->name,
                    $transaction->currency_code,
                    (float) $transaction->amount,
                    $transaction->transaction_code,
                ), 'user_transaction');
            } elseif ($type === 'withdrawal' && in_array($status, ['approved', 'rejected', 'failed'], true)) {
                $this->dispatchMailSafely($user->email, new WithdrawalRequestStatusMail(
                    $user->name,
                    $transaction->currency_code,
                    (float) $transaction->amount,
                    $status,
                    $transaction->transaction_code,
                    $transaction->phone_number ?: $user->phone,
                    $transaction->processed_at?->format('M j, Y g:i A') ?: $transaction->updated_at?->format('M j, Y g:i A'),
                ), 'user_transaction');
            } else {
                $this->dispatchMailSafely($user->email, new WalletTransactionMail(
                    $user->name,
                    $type,
                    $transaction->currency_code,
                    (float) $transaction->amount,
                    $status,
                    $transaction->phone_number ?: $user->phone,
                    $transaction->transaction_code,
                    $transaction->updated_at?->format('M j, Y g:i A')
                ), 'user_transaction');
            }
        }

        // Admin Notification (mostly for approvals)
        if ($status === 'approved' && $type === 'deposit') {
            $this->dispatchMailSafely('admin@fxinvest.io', new AdminDepositMail(
                $user->name,
                $user->email,
                (float) $transaction->amount,
                $transaction->currency_code,
                $status,
                $transaction->provider ?: 'wallet',
                $transaction->transaction_code
            ), 'admin_deposit');

            app(SmsNotifier::class)->sendToAdmins(
                "Deposit approved for {$user->name}: {$transaction->currency_code} ".number_format((float) $transaction->amount, 2).'.',
                'admin_deposit'
            );
        }

        $memberNotifications = app(MemberNotificationService::class);

        if ($type === 'deposit' && in_array($status, ['approved', 'failed', 'rejected'], true)) {
            $memberNotifications->notifyDepositReviewed($transaction, $status);
        }

        if ($type === 'withdrawal' && in_array($status, ['approved', 'failed', 'rejected'], true)) {
            $memberNotifications->notifyWithdrawalReviewed($transaction, $status);
        }
    }

    /**
     * Fulfill subscription intent if present.
     */
    protected function fulfillSubscriptionIntent(WalletTransaction $transaction): void
    {
        $meta = $transaction->meta ?? [];
        if (($meta['intent'] ?? '') === SubscriptionService::META_INTENT_SUBSCRIPTION && ! empty($meta['package_id'])) {
            $package = InvestmentPackage::find($meta['package_id']);
            $user = $transaction->user;
            $wallet = $transaction->wallet;

            if ($package && $user && $wallet) {
                try {
                    app(SubscriptionService::class)->createSubscription($user, $wallet, $package, (float) ($meta['amount'] ?? $transaction->amount));
                    $investment = $user->investments()->latest('id')->first();

                    app(MemberNotificationService::class)->notifySubscriptionReviewed(
                        $user,
                        (float) ($meta['amount'] ?? $transaction->amount),
                        app(\App\Services\DefaultCurrencyService::class)->code(),
                        'approved',
                        (int) $package->id,
                    );

                    if ($investment) {
                        app(MemberNotificationService::class)->notifyInvestmentCreated($investment);
                        app(MemberNotificationService::class)->notifyInvestmentReviewed($investment, 'approved');
                    }

                    $transaction->update(['meta' => array_merge($meta, ['subscription_fulfilled' => true])]);

                    app(InvestmentEmailService::class)->sendSubscriptionCreated(
                        $user->email,
                        $user->name,
                        $package->name,
                        app(\App\Services\DefaultCurrencyService::class)->code(),
                        (float) ($meta['amount'] ?? $transaction->amount),
                    );

                    $adminEmail = config('mail.admin_address', 'admin@fxinvest.io');
                    if (filled($adminEmail)) {
                        SendMail::dispatch((string) $adminEmail, mailable: new AdminTransactionMail(
                            $user->name,
                            $user->email,
                            (float) ($meta['amount'] ?? $transaction->amount),
                            app(\App\Services\DefaultCurrencyService::class)->code(),
                            'investment',
                            'approved',
                            'subscription',
                            (string) ($transaction->transaction_code ?: $transaction->id),
                        ), notificationType: 'admin_transaction');
                    }

                    app(SmsNotifier::class)->sendToPhone(
                        $user->phone,
                        "Subscription created for {$package->name} successful.",
                        'user_transaction'
                    );
                } catch (\Throwable $e) {
                    Log::warning('Subscription fulfill failed', [
                        'transaction_id' => $transaction->id,
                        'message' => $e->getMessage(),
                    ]);
                }
            }
        }
    }

    protected function dispatchMailSafely(string $email, object $mailable, ?string $notificationType = null): void
    {
        try {
            SendMail::dispatch($email, mailable: $mailable, notificationType: $notificationType);
        } catch (Throwable $exception) {
            Log::warning('Payment email dispatch failed.', [
                'email' => $email,
                'message' => $exception->getMessage(),
            ]);
        }
    }
}
