<?php

namespace App\Http\Controllers;

use App\Models\User;
use App\Support\PaymentGatewayInspector;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\URL;
use App\Services\PayFastBdGateway;
use App\Services\VoucherTransactionService;

class AddMoneyController extends Controller
{
    /* --------------------------- Settings Helpers --------------------------- */

    /**
     * Brand Key আগে .env, তারপর general_settings (id=1) থেকে নেবে।
     */
    private function brandKey(): string
    {
        $envKey = trim((string) config('services.payfastbd.brand_key', ''));
        if ($envKey !== '') {
            return $envKey;
        }

        $key = trim((string) (DB::table('general_settings')->where('id', 1)->value('bondhonpay_api_key') ?? ''));
        if ($key === '') {
            abort(503, 'PayFastBD API Key is not configured in .env or general_settings.');
        }

        return $key;
    }

    private function gateway(): PayFastBdGateway
    {
        return app(PayFastBdGateway::class);
    }

    private function callbackBaseUrl(): string
    {
        $configured = trim((string) config('services.payfastbd.callback_base_url', ''));
        if ($configured !== '') {
            return rtrim($configured, '/');
        }

        $currentRoot = request()->root();
        if (is_string($currentRoot) && $currentRoot !== '') {
            return rtrim($currentRoot, '/');
        }

        return rtrim((string) config('app.url'), '/');
    }

    private function callbackUrl(string $routeName, array $parameters = [], bool $signed = false): string
    {
        $path = $signed
            ? URL::signedRoute($routeName, $parameters, null, false)
            : route($routeName, $parameters, false);

        return $this->callbackBaseUrl() . '/' . ltrim($path, '/');
    }

    private function voucherRedirect(string $flashType, string $message)
    {
        if (auth()->check()) {
            return $flashType === 'success'
                ? redirect()->route('user.voucher')->with('success', $message)
                : redirect()->route('user.voucher')->withErrors($message);
        }

        session(['url.intended' => route('user.voucher')]);

        return $flashType === 'success'
            ? redirect()->route('login.form')->with('success', $message)
            : redirect()->route('login.form')->withErrors($message);
    }

    /* --------------------------- TrxID Helpers --------------------------- */

    // "TrxID XXXXX" / "TxnID XXXXX" / "TrxID: XXXXX" ধরার রেগেক্স
    private function extractTrxFromString(?string $text): ?string
    {
        return PaymentGatewayInspector::extractTrxId($text);
    }

    // যেকোনো nested array/json/string থেকে TrxID খুঁজে আনে
    private function extractTrxFromMixed($mixed): ?string
    {
        return PaymentGatewayInspector::extractTrxId($mixed);
    }

    private function walletAddMoneyTransactionExists(string $transactionId): bool
    {
        if (!Schema::hasTable('voucher_transactions')) {
            return true;
        }

        return DB::table('voucher_transactions')
            ->where('source', 'wallet_addmoney_credit')
            ->where('reference_type', 'payment')
            ->where('reference_id', $transactionId)
            ->exists();
    }

    private function recordWalletAddMoneyCredit(
        int $userId,
        float $amount,
        float $before,
        float $after,
        string $transactionId,
        ?string $trxId,
        ?string $paymentMethod,
        string $status,
        string $description,
        array $metadata = []
    ): void {
        $transactionId = trim($transactionId);

        if ($transactionId === '' || $amount <= 0 || $this->walletAddMoneyTransactionExists($transactionId)) {
            return;
        }

        VoucherTransactionService::record(
            userId: $userId,
            direction: 'credit',
            amount: $amount,
            before: $before,
            after: $after,
            source: 'wallet_addmoney_credit',
            description: $description,
            referenceType: 'payment',
            referenceId: $transactionId,
            metadata: array_merge([
                'trx_id' => $trxId,
                'payment_method' => $paymentMethod,
                'status' => $status,
            ], $metadata)
        );
    }

    private function recoverCompletedWalletCreditHistory(
        int $userId,
        float $amount,
        string $transactionId,
        ?string $trxId,
        ?string $paymentMethod,
        string $status,
        string $recordedFrom
    ): void {
        $transactionId = trim($transactionId);

        if ($transactionId === '' || $userId <= 0 || $amount <= 0 || $this->walletAddMoneyTransactionExists($transactionId)) {
            return;
        }

        $userRow = DB::table('users')
            ->where('id', $userId)
            ->lockForUpdate()
            ->first(['id', 'voucher_balance']);

        if (!$userRow) {
            Log::warning('Completed wallet payment found but user missing for history recovery', [
                'transaction_id' => $transactionId,
                'user_id' => $userId,
            ]);
            return;
        }

        $after = round((float) ($userRow->voucher_balance ?? 0), 2);
        $before = round(max(0, $after - $amount), 2);

        $this->recordWalletAddMoneyCredit(
            userId: $userId,
            amount: $amount,
            before: $before,
            after: $after,
            transactionId: $transactionId,
            trxId: $trxId,
            paymentMethod: $paymentMethod,
            status: $status,
            description: 'আগে completed হওয়া ভাউচার রিচার্জের ট্রানজেকশন হিস্ট্রি যোগ করা হয়েছে। Transaction ID: ' . $transactionId,
            metadata: [
                'recovered_history' => true,
                'recorded_from' => $recordedFrom,
            ]
        );
    }

    /* --------------------------- Show form --------------------------- */

    public function form()
    {
        return view('addmoney.form');
    }

    /* ------------------ Create Payment (POST) ------------------ */

    public function createPayment(Request $request)
    {
        $request->validate([
            'amount' => ['required', 'numeric', 'min:20', 'max:1000000'],
        ]);

        $amount = (float) $request->input('amount');
        $userId = auth()->id();
        if (!$userId)
            return redirect()->route('login.form')->withErrors('Please login first');

        /** @var User $user */
        $user = User::findOrFail($userId);

        $brandKey = $this->brandKey();
        $gateway = $this->gateway();

        $cusName = $user->full_name ?: ($user->name ?: "User {$user->id}");
        $cusEmail = $user->email ?: "noemail@example.com";

        $payload = [
            "cus_name" => $cusName,
            "cus_email" => $cusEmail,
            "amount" => $amount,
            "success_url" => $this->callbackUrl('addmoney.success'),
            "cancel_url" => $this->callbackUrl('addmoney.cancel'),
            "webhook_url" => $this->callbackUrl('payfastbd.wallet.webhook', ['user_id' => $user->id], true),
        ];

        try {
            $resp = $gateway->createPayment($brandKey, $payload);

            if (!$resp->ok()) {
                Log::warning('AddMoney createPayment non-200', ['status' => $resp->status(), 'body' => $resp->body()]);
                return back()->withErrors('Payment server error. Try again.');
            }

            $result = $resp->json() ?? [];
            Log::info('AddMoney createPayment result', $result);
            $transactionId = $gateway->transactionId($result);

            if ($transactionId) {
                DB::table('payments')->updateOrInsert(
                    ['transaction_id' => $transactionId],
                    [
                        'user_id' => $user->id,
                        'amount' => $amount,
                        'status' => 'pending',
                        'payment_method' => $gateway->paymentMethod($result),
                        'trx_id' => null,
                        'raw_response' => json_encode($result, JSON_UNESCAPED_UNICODE),
                        'created_at' => now(),
                        'updated_at' => now(),
                    ]
                );
                session(['payfastbd_last_tid' => $transactionId]);
            }

            $paymentUrl = $gateway->paymentUrl($result);

            if ($gateway->requestSucceeded($result) && $paymentUrl) {
                return redirect($paymentUrl);
            }

            return back()->withErrors('❌ Payment initiation failed.');

        } catch (\Throwable $e) {
            Log::error('AddMoney createPayment exception', ['e' => $e]);
            return back()->withErrors('Network error: ' . $e->getMessage());
        }
    }

    /* ------------------ Success Callback (GET) ------------------ */

    public function paymentSuccess(Request $request)
    {
        $transactionId = (string) ($request->query('transactionId') ?? $request->query('transaction_id') ?? '');
        if ($transactionId === '') {
            return redirect()->route('welcome')->withErrors('Invalid transaction.');
        }

        $brandKey = $this->brandKey();
        $verify = null;
        $gateway = $this->gateway();

        try {
            $r = $gateway->verifyPayment($brandKey, $transactionId);
            if ($r->ok())
                $verify = $r->json();
        } catch (\Throwable $e) {
            Log::error('AddMoney verify exception', ['e' => $e]);
        }

        Log::info('AddMoney success callback', ['query' => $request->all(), 'verify' => $verify]);

        $queryStatus = (string) ($request->query('status') ?? '');
        $hasVerifyData = is_array($verify) && $verify !== [];
        $verifiedCompleted = $hasVerifyData && $gateway->paymentCompleted($verify, $queryStatus);
        $statusV = $hasVerifyData ? $gateway->status($verify, $queryStatus) : '';
        $amountV = $hasVerifyData ? $gateway->amount($verify) : 0.0;
        $methodV = $hasVerifyData
            ? ($gateway->paymentMethod($verify) ?? $request->query('paymentMethod') ?? $request->query('payment_method'))
            : ($request->query('paymentMethod') ?? $request->query('payment_method'));
        $trxId = $this->extractTrxFromMixed($verify) ?? $this->extractTrxFromMixed($request->all());

        DB::transaction(function () use ($transactionId, $statusV, $amountV, $methodV, $trxId, $verify, $request, $verifiedCompleted, $hasVerifyData) {

            $existing = DB::table('payments')->where('transaction_id', $transactionId)->lockForUpdate()->first();
            $prevStatus = $existing->status ?? null;
            $existingAmount = $existing->amount ?? null;
            $userId = $existing->user_id ?? auth()->id();

            if ($verifiedCompleted || $prevStatus === 'completed') {
                $newStatus = 'completed';
            } elseif ($hasVerifyData) {
                $newStatus = $statusV === 'PENDING' ? 'pending' : 'failed';
            } else {
                $newStatus = $prevStatus ?: 'pending';
            }
            $amountToSave = $amountV > 0
                ? $amountV
                : ($existingAmount !== null ? (float) $existingAmount : (float) $request->query('paymentAmount', $request->query('amount', 0)));

            $raw = [
                'success_query' => $request->all(),
                'verify' => $verify,
            ];

            if ($existing) {
                DB::table('payments')->where('id', $existing->id)->update([
                    'user_id' => $userId,
                    'amount' => $amountToSave,
                    'status' => $newStatus,
                    'payment_method' => $methodV ?: $existing->payment_method,
                    'trx_id' => $existing->trx_id ?: $trxId,
                    'raw_response' => json_encode($raw, JSON_UNESCAPED_UNICODE),
                    'updated_at' => now(),
                ]);
            } else {
                DB::table('payments')->insert([
                    'transaction_id' => $transactionId,
                    'user_id' => $userId,
                    'amount' => $amountToSave,
                    'status' => $newStatus,
                    'payment_method' => $methodV,
                    'trx_id' => $trxId,
                    'raw_response' => json_encode($raw, JSON_UNESCAPED_UNICODE),
                    'created_at' => now(),
                    'updated_at' => now(),
                ]);
            }

            // একবারই ক্রেডিট: pending/failed → completed ট্রানজিশনে
            if ($newStatus === 'completed' && $prevStatus !== 'completed' && $userId && $amountToSave > 0) {
                $userRow = DB::table('users')->where('id', $userId)->lockForUpdate()->first(['id', 'voucher_balance']);
                if (!$userRow) {
                    Log::warning('Wallet success completed but user missing', [
                        'transaction_id' => $transactionId,
                        'user_id' => $userId,
                    ]);
                    return;
                }

                $before = (float) ($userRow->voucher_balance ?? 0);
                $after = $before + (float) $amountToSave;

                DB::table('users')->where('id', $userId)->update(['voucher_balance' => $after]);

                $this->recordWalletAddMoneyCredit(
                    userId: (int) $userId,
                    amount: (float) $amountToSave,
                    before: $before,
                    after: $after,
                    transactionId: $transactionId,
                    trxId: $trxId,
                    paymentMethod: $methodV,
                    status: $newStatus,
                    description: 'PayFastBD রিচার্জ সফল হয়েছে। Transaction ID: ' . $transactionId,
                    metadata: [
                        'recorded_from' => 'success_callback',
                    ]
                );

                Log::info('Voucher credited (success)', [
                    'user_id' => $userId,
                    'amount' => $amountToSave,
                    'tid' => $transactionId,
                ]);
            } elseif ($newStatus === 'completed' && $prevStatus === 'completed' && $userId && $amountToSave > 0) {
                $this->recoverCompletedWalletCreditHistory(
                    userId: (int) $userId,
                    amount: (float) $amountToSave,
                    transactionId: $transactionId,
                    trxId: $trxId,
                    paymentMethod: $methodV,
                    status: $newStatus,
                    recordedFrom: 'success_callback'
                );
            }
        });

        $currentStatus = DB::table('payments')->where('transaction_id', $transactionId)->value('status');

        if ($currentStatus === 'completed') {
            return $this->voucherRedirect('success', '✅ টাকা যুক্ত হয়েছে আপনার ভাউচার ব্যালেন্সে।');
        }

        if (!$hasVerifyData && $currentStatus === 'pending') {
            return $this->voucherRedirect('error', 'পেমেন্ট এখনো confirm হয়নি। webhook confirm হলে ব্যালেন্স আপডেট হবে।');
        }

        return $this->voucherRedirect('error', '❌ Payment verification failed.');
    }

    /* ------------------ Cancel (GET) ------------------ */

    public function paymentCancel(Request $request)
    {
        $transactionId = $request->query('transactionId')
            ?? $request->query('transaction_id')
            ?? ('cancel-' . time());

        DB::table('payments')->updateOrInsert(
            ['transaction_id' => $transactionId],
            [
                'user_id' => auth()->id(),
                'amount' => (float) ($request->query('paymentAmount', $request->query('amount', 0))),
                'status' => 'cancelled',
                'payment_method' => $request->query('paymentMethod') ?? $request->query('payment_method'),
                'trx_id' => null,
                'raw_response' => json_encode($request->all(), JSON_UNESCAPED_UNICODE),
                'created_at' => now(),
                'updated_at' => now(),
            ]
        );

        return $this->voucherRedirect('error', '❌ Payment Cancelled.');
    }

    /* ------------------ Webhook (POST) ------------------ */

    public function handleWebhook(Request $request)
    {
        $refId = (string) ($request->input('transaction_id') ?? $request->input('transactionId') ?? '');
        if ($refId === '') {
            return response()->json(['message' => 'Missing transaction_id'], 422);
        }

        $gateway = $this->gateway();
        $webhookData = $request->all();
        Log::info('PayFastBD Webhook (wallet)', ['transaction_id' => $refId]);

        $verifyData = [];

        try {
            $verifyResp = $gateway->verifyPayment($this->brandKey(), $refId);
        } catch (\Throwable $e) {
            Log::error('Wallet webhook verify exception', ['transaction_id' => $refId, 'error' => $e->getMessage()]);
            $verifyResp = null;
        }

        if ($verifyResp && !$verifyResp->ok()) {
            Log::warning('Wallet webhook verify non-200', [
                'transaction_id' => $refId,
                'status' => $verifyResp->status(),
                'body' => $verifyResp->body(),
            ]);
        }

        if ($verifyResp && $verifyResp->ok()) {
            $verifyData = $verifyResp->json() ?? [];
        }

        $resolvedData = is_array($verifyData) && $verifyData !== [] ? $verifyData : $webhookData;
        $status = $gateway->status($resolvedData);
        $amount = $gateway->amount($resolvedData);
        $meta = $gateway->metadata($resolvedData);
        $method = $gateway->paymentMethod($resolvedData);
        $trxId = $this->extractTrxFromMixed($verifyData) ?: $this->extractTrxFromMixed($webhookData);
        $signedUserId = $request->hasValidSignature(false) ? (int) $request->query('user_id', 0) : 0;

        DB::transaction(function () use ($refId, $status, $amount, $meta, $method, $trxId, $verifyData, $webhookData, $signedUserId) {
            $row = DB::table('payments')->where('transaction_id', $refId)->lockForUpdate()->first();

            $prevStatus = $row->status ?? null;
            $userId = (int) ($row->user_id ?? 0);

            if ($userId <= 0 && isset($meta['user_id']) && is_numeric($meta['user_id'])) {
                $candidateUserId = (int) $meta['user_id'];
                if ($candidateUserId > 0 && DB::table('users')->where('id', $candidateUserId)->exists()) {
                    $userId = $candidateUserId;
                }
            }

            if ($userId <= 0 && $signedUserId > 0 && DB::table('users')->where('id', $signedUserId)->exists()) {
                $userId = $signedUserId;
            }

            if (!$row && $userId <= 0) {
                Log::warning('Wallet webhook ignored for unknown transaction', ['transaction_id' => $refId]);
                return;
            }

            $newStatus = in_array($status, ['COMPLETED', 'SUCCESS', 'SUCCEEDED', 'PAID'], true)
                ? 'completed'
                : ($status === 'PENDING' ? 'pending' : 'failed');
            $amountToSave = $amount > 0 ? $amount : (float) ($row->amount ?? 0);
            $raw = [
                'webhook' => $webhookData,
                'verify' => $verifyData,
            ];

            if ($row) {
                DB::table('payments')->where('id', $row->id)->update([
                    'user_id' => $userId > 0 ? $userId : null,
                    'amount' => $amountToSave,
                    'status' => $newStatus,
                    'payment_method' => $method ?: $row->payment_method,
                    'trx_id' => $row->trx_id ?: $trxId,
                    'raw_response' => json_encode($raw, JSON_UNESCAPED_UNICODE),
                    'updated_at' => now(),
                ]);
            } else {
                DB::table('payments')->insert([
                    'transaction_id' => $refId,
                    'user_id' => $userId > 0 ? $userId : null,
                    'amount' => $amountToSave,
                    'status' => $newStatus,
                    'payment_method' => $method,
                    'trx_id' => $trxId,
                    'raw_response' => json_encode($raw, JSON_UNESCAPED_UNICODE),
                    'created_at' => now(),
                    'updated_at' => now(),
                ]);
            }

            // একবারই ক্রেডিট: completed ট্রানজিশনে
            if ($newStatus === 'completed' && $userId > 0 && $amountToSave > 0 && $prevStatus !== 'completed') {
                $userRow = DB::table('users')->where('id', $userId)->lockForUpdate()->first(['id', 'voucher_balance']);
                if (!$userRow) {
                    Log::warning('Wallet webhook completed but user missing', [
                        'transaction_id' => $refId,
                        'user_id' => $userId,
                    ]);
                    return;
                }

                $before = (float) ($userRow->voucher_balance ?? 0);
                $after = $before + (float) $amountToSave;

                DB::table('users')->where('id', $userId)->update(['voucher_balance' => $after]);

                $this->recordWalletAddMoneyCredit(
                    userId: (int) $userId,
                    amount: (float) $amountToSave,
                    before: $before,
                    after: $after,
                    transactionId: $refId,
                    trxId: $trxId,
                    paymentMethod: $method,
                    status: $newStatus,
                    description: 'Webhook দিয়ে ভাউচার রিচার্জ সফল হয়েছে। Transaction ID: ' . $refId,
                    metadata: [
                        'recorded_from' => 'webhook',
                    ]
                );

                Log::info('Voucher credited (webhook)', [
                    'user_id' => $userId,
                    'amount' => $amountToSave,
                    'tid' => $refId,
                ]);
            } elseif ($newStatus === 'completed' && $prevStatus === 'completed' && $userId > 0 && $amountToSave > 0) {
                $this->recoverCompletedWalletCreditHistory(
                    userId: (int) $userId,
                    amount: (float) $amountToSave,
                    transactionId: $refId,
                    trxId: $trxId,
                    paymentMethod: $method,
                    status: $newStatus,
                    recordedFrom: 'webhook'
                );
            }
        });

        return response()->json(['message' => 'OK']);
    }
}
