<?php

namespace App\Http\Controllers;

use App\Models\Transaction;
use App\Models\Brand;
use App\Models\Merchant;
use App\Mail\PaymentFailedMail;
use Illuminate\Support\Facades\Auth;
use Illuminate\Contracts\View\View;
use Ihasan\Bkash\Facades\Bkash;
use Illuminate\Http\Request;
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Log;
use App\Mail\PaymentSuccessMail;
use Illuminate\Support\Facades\Validator;

class PaymentController extends Controller
{
    public function initiateGatewayPayment(Request $request)
    {
        $validated = $request->validate([
            'method' => 'required|string',
            'amount' => 'required|numeric',
            'brand_id' => 'required|exists:brands,id',
            'system_trx_id' => 'required|string'
        ]);

        $method = $validated['method'];
        $amount = $validated['amount'];
        $systemTrxId = $validated['system_trx_id'];
        $brandId = $validated['brand_id'];

        $methodParts = explode('-', $method);
        if ($methodParts[0] === 'bkash' && isset($methodParts[1]) && $methodParts[1] === 'merchant') {
            return $this->initiateBkashPayment($amount, $systemTrxId, $brandId, 'merchant');
        }

        return redirect()->back()->with('error', 'Invalid payment gateway specified.');
    }

    public function redirectToGateway(Request $request)
    {
        try {
            Log::info('Payment redirect request received', [
                'method' => $request->input('method'),
                'amount' => $request->input('amount'),
                'brand_id' => $request->input('brand_id'),
                'brand_key' => $request->input('brand_key'),
                'system_trx_id' => $request->input('system_trx_id'),
                'ip' => $request->ip(),
            ]);

            $validated = $request->validate([
                'method' => 'required|string',
                'amount' => 'required|numeric|min:1',
                'brand_id' => 'required_without:brand_key|nullable|exists:brands,id',
                'brand_key' => 'required_without:brand_id|nullable|string|exists:brands,brand_key',
                'system_trx_id' => 'required|string'
            ]);

            $method = $validated['method'];
            $amount = $validated['amount'];
            $systemTrxId = $validated['system_trx_id'];

            // Resolve brand id either directly or via brand_key
            if (!empty($validated['brand_key'])) {
                $brand = Brand::where('brand_key', $validated['brand_key'])->first();
                $brandId = $brand->id;
            } else {
                $brandId = $validated['brand_id'];
                $brand = Brand::find($brandId);
            }
            if (!$brand) {
                return redirect()->back()->with('error', 'Brand not found for the provided key or ID.');
            }

            Log::info('Payment redirect initiated', [
                'method' => $method,
                'amount' => $amount,
                'system_trx_id' => $systemTrxId
            ]);

            // For bKash merchant payments
            $methodParts = explode('-', $method);
            if ($methodParts[0] === 'bkash' && isset($methodParts[1]) && $methodParts[1] === 'merchant') {
                return $this->initiateBkashPayment($amount, $systemTrxId, $brandId, 'merchant');
            }

            // For other payment methods
            if (str_contains($method, 'nagad-merchant')) {
                return $this->initiateNagadPayment($amount, $systemTrxId, $brandId);
            }

            if (str_contains($method, 'rocket-merchant')) {
                return $this->initiateRocketPayment($amount, $systemTrxId, $brandId);
            }

            // Default: redirect to PIN page for personal/agent payments
            return view('frontend.payment-pin', [
                'amount' => $amount,
                'method' => $method,
                'brand' => $brand,
                'transactionId' => $systemTrxId
            ]);

        } catch (\Exception $e) {
            Log::error('Payment redirect failed: ' . $e->getMessage());
            return redirect()->back()->with('error', 'Payment initiation failed: ' . $e->getMessage());
        }
    }

    private function initiateBkashPayment($amount, $systemTrxId, $brandId, $channelType = 'merchant')
    {
        DB::beginTransaction();
        
        try {
            $brand = Brand::find($brandId);
            if (!$brand || !$brand->user) {
                throw new \Exception('Brand or brand owner not found.');
            }

            $merchant = Merchant::firstOrCreate(
                ['email' => $brand->user->email],
                ['name' => $brand->user->name, 'active' => true]
            );

            // Check for existing pending transaction
            $existingTransaction = Transaction::where('system_trx_id', $systemTrxId)
                ->whereIn('status', ['pending', 'completed'])
                ->first();
                
            if ($existingTransaction) {
                throw new \Exception('Transaction already exists with this ID.');
            }

            $transaction = Transaction::create([
                'user_id' => $brand->user_id,
                'merchant_id' => $merchant->id,
                'brand_id' => $brandId,
                'system_trx_id' => $systemTrxId,
                'payment_gateway' => 'bkash-merchant',
                'amount' => $amount,
                'currency' => 'BDT',
                'status' => 'pending',
                'trx_id' => $systemTrxId,
            ]);

            $paymentData = [
                'amount' => $amount,
                'currency' => 'BDT',
                'intent' => 'sale',
                'merchantInvoiceNumber' => $systemTrxId,
                'callbackURL' => route('payment.callback'),
            ];

            $response = Bkash::forTenant($brandId)->createPayment($paymentData);

            Log::info('bKash Create Payment API Response:', $response);
            
            if (isset($response['bkashURL'])) {
                // Save payment ID to transaction
                $transaction->payment_id = $response['paymentID'];
                $transaction->status_message = 'bKash payment initiated. Payment ID: ' . $response['paymentID'];
                $transaction->save();
                
                DB::commit();
                
                Log::info('Redirecting to bKash URL', ['url' => $response['bkashURL']]);
                return redirect()->away($response['bkashURL']);
            } else {
                throw new \Exception($response['statusMessage'] ?? 'Failed to create bKash payment.');
            }
            
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('bKash payment initiation failed: ' . $e->getMessage());
            
            $brandKey = request('brand_key');
            if (!$brandKey && isset($brand) && $brand) {
                $brandKey = $brand->brand_key;
            }

            $queryParams = array_filter([
                'amount' => $amount, 
                'brand_key' => $brandKey, 
                'invoice_number' => $systemTrxId
            ]);

            return redirect()->route('payment', $queryParams)
                             ->with('error', 'bKash Error: ' . $e->getMessage());
        }
    }

    /**
     * Show the payment PIN entry page.
     */
    public function showPinForm(Request $request): View 
    {
        $amount = $request->query('amount', 0);
        $method = $request->query('method');
        $brandId = $request->query('brand_id');
        $brand = null;
        $transactionId = $request->query('system_trx_id');

        if ($brandId) {
            $brand = Brand::find($brandId);
        } else {
            $brandKey = $request->query('brand_key');
            if ($brandKey) {
                $brand = Brand::where('brand_key', $brandKey)->first();
            }
        }

        return view('frontend.payment-pin', compact('amount', 'method', 'brand', 'transactionId'));
    }

    /**
     * Verify the transaction ID.
     */
    public function verifyTransaction(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'trx_id' => 'required|string|min:6',
            'amount' => 'required|numeric',
            'system_trx_id' => 'required|string',
            'brand_id' => 'sometimes|required_without:brand_key|exists:brands,id',
            'brand_key' => 'sometimes|required_without:brand_id|string|exists:brands,brand_key',
            'method' => 'required|string',
        ]);

        if ($validator->fails()) {
            return response()->json(['success' => false, 'message' => $validator->errors()->first()], 422);
        }

        $trxId = $request->input('trx_id');
        $systemTrxId = $request->input('system_trx_id');
        $brandId = $request->input('brand_id');
        $amount = $request->input('amount');
        
        // Resolve brand_id from brand_key if brand_id not provided
        if (empty($brandId) && $request->filled('brand_key')) {
            $brand = Brand::where('brand_key', $request->input('brand_key'))->first();
            if ($brand) {
                $brandId = $brand->id;
            }
        }

        // Test transaction IDs
        if (strtolower($trxId) === 'test') {
            return response()->json(['success' => false, 'message' => 'This is a test transaction ID and cannot be used.'], 400);
        }

        if (strtolower($trxId) === 'notfound') {
            return response()->json(['success' => false, 'message' => 'দুঃখিত, আপনার Transaction ID খুঁজে পাওয়া যায়নি।'], 404);
        }

        $brand = Brand::find($brandId);
        if (!$brand) {
            return response()->json(['success' => false, 'message' => 'Invalid Brand ID provided.'], 400);
        }

        // Check if transaction ID already used
        $existingTransaction = Transaction::where('trx_id', $trxId)->first();
        if ($existingTransaction) {
            return response()->json(['success' => false, 'message' => 'This Transaction ID has already been used.'], 409);
        }

        // Resolve merchant
        $merchant = null;
        if ($brand->user) {
            $merchant = Merchant::where('email', $brand->user->email)->first();
            if (!$merchant) {
                $merchant = Merchant::create([
                    'name' => $brand->user->name ?? ($brand->name ?? 'Unknown'),
                    'email' => $brand->user->email,
                    'active' => true,
                ]);
            }
        }

        if (!$merchant) {
            return response()->json(['success' => false, 'message' => 'Unable to resolve merchant for this brand.'], 400);
        }

        // Create transaction
        $transaction = Transaction::create([
            'user_id' => Auth::id(),
            'merchant_id' => $merchant->id,
            'brand_id' => $brandId,
            'system_trx_id' => $systemTrxId,
            'trx_id' => $trxId,
            'payment_gateway' => $request->input('method'),
            'amount' => $amount,
            'currency' => 'BDT',
            'status' => 'completed',
        ]);

        session(['last_transaction_id' => $transaction->id]);

        // Send success email
        $transaction->load('brand.user', 'user');
        $brandOwner = $transaction->brand->user ?? null;

        if ($brandOwner && $brandOwner->email) {
            Mail::to($brandOwner->email)->queue(new PaymentSuccessMail($transaction));
        }

        return response()->json([
            'success' => true, 
            'message' => 'Transaction verified successfully.', 
            'transaction_id' => $transaction->id
        ]);
    }

    private function initiateNagadPayment($amount, $systemTrxId, $brandId)
    {
        return redirect()->back()->with('error', 'Nagad payment is currently unavailable');
    }

    private function initiateRocketPayment($amount, $systemTrxId, $brandId)
    {
        return redirect()->back()->with('error', 'Rocket payment is currently unavailable');
    }

    public function handleGatewayCallback(Request $request)
    {
        $status = $request->query('status');
        $paymentID = $request->query('paymentID');

        Log::info('bKash Callback Received', ['status' => $status, 'paymentID' => $paymentID]);

        if ($status === 'failure' || $status === 'cancel') {
            $transaction = Transaction::where('payment_id', $paymentID)->first();
            if ($transaction) {
                $transaction->status = 'failed';
                $transaction->status_message = 'Payment was cancelled or failed by user.';
                $transaction->save();
                $this->sendPaymentFailedEmail($transaction);
            }
            return redirect()->route('payment.failed')->with('error', 'Payment was cancelled or failed.');
        }

        if ($status === 'success' && $paymentID) {
            try {
                $transaction = Transaction::where('payment_id', $paymentID)->first();

                if (!$transaction || !$transaction->brand_id) {
                    throw new \Exception("Transaction or associated brand not found for paymentID: {$paymentID}.");
                }

                // Execute payment
                $executeData = Bkash::forTenant($transaction->brand_id)->executePayment($paymentID); // Corrected: Added forTenant

                if (isset($executeData['statusCode']) && $executeData['statusCode'] !== '0000') {
                    Log::error('bKash Execute API failed', ['response' => $executeData]);
                    // Try query as fallback
                    $executeData = Bkash::forTenant($transaction->brand_id)->queryPayment($paymentID); // Corrected: Added forTenant
                }

                Log::info('bKash Execute/Query Response', $executeData);

                if (isset($executeData['statusCode']) && $executeData['statusCode'] === '0000' && $executeData['transactionStatus'] === 'Completed') {
                    // Security checks
                    if ((float)$executeData['amount'] != (float)$transaction->amount) {
                        $transaction->status = 'failed';
                        $transaction->status_message = 'Amount mismatch.';
                        $transaction->save();
                        $this->sendPaymentFailedEmail($transaction);
                        throw new \Exception('Payment amount mismatch.');
                    }

                    $invoiceId = $executeData['merchantInvoiceNumber'] ?? null;
                    if ($invoiceId !== $transaction->system_trx_id) {
                        $transaction->status = 'failed';
                        $transaction->status_message = 'Invoice ID mismatch.';
                        $transaction->save();
                        $this->sendPaymentFailedEmail($transaction);
                        throw new \Exception('Payment reference mismatch.');
                    }

                    // Payment successful
                    $transaction->status = 'completed';
                    $transaction->trx_id = $executeData['trxID'];
                    $transaction->status_message = 'Payment completed successfully.';
                    $transaction->save();

                    session(['last_transaction_id' => $transaction->id]);
                    $this->sendPaymentSuccessEmail($transaction);

                    return redirect()->route('payment.success');
                } else {
                    $transaction->status = 'failed';
                    $transaction->status_message = $executeData['statusMessage'] ?? 'Payment not completed.';
                    $transaction->save();                    
                    $this->sendPaymentFailedEmail($transaction);
                    throw new \Exception($executeData['statusMessage'] ?? 'Payment not completed.');
                }
            } catch (\Exception $e) {
                Log::error('bKash callback processing error: ' . $e->getMessage());
                return redirect()->route('payment.failed')->with('error', 'Payment verification failed. ' . $e->getMessage());
            }
        }

        return redirect()->route('payment.failed')->with('error', 'Invalid payment status.');
    }

    /**
     * Send payment success email
     */
    private function sendPaymentSuccessEmail(Transaction $transaction): void
    {
        $transaction->load('brand.user');
        $brandOwner = $transaction->brand->user ?? null;

        if ($brandOwner && $brandOwner->email) {
            Mail::to($brandOwner->email)->queue(new PaymentSuccessMail($transaction));
        }
    }

    /**
     * Send payment failure email
     */
    private function sendPaymentFailedEmail(Transaction $transaction): void
    {
        $transaction->load('brand.user');
        $brandOwner = $transaction->brand->user ?? null;

        if ($brandOwner && $brandOwner->email) {
            Mail::to($brandOwner->email)->queue(new PaymentFailedMail($transaction));
        }
    }

    // --- Client-Side bKash Checkout Flow ---

    public function createBkashPayment(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'amount' => 'required|numeric',
            'invoice' => 'required|string',
            'brand_id' => 'required|exists:brands,id',
        ]);

        if ($validator->fails()) {
            $errorMessage = $validator->errors()->first();
            // Check if brand_id is missing or invalid
            if ($validator->errors()->has('brand_id') && empty($request->input('brand_id'))) {
                $errorMessage = 'Brand information is missing. Please refresh the payment page and try again.'; // Custom message for brand_id
            }
            return response()->json(['error' => true, 'errorMessage' => $errorMessage, 'errors' => $validator->errors()], 422);
        }

        $validated = $validator->validated();
        $amount = (float) $validated['amount'];
        $systemTrxId = $validated['invoice'];
        $brandId = $validated['brand_id'];

        DB::beginTransaction();
        try {
            $brand = Brand::find($brandId);
            if (!$brand || !$brand->user) {
                throw new \Exception('Brand or brand owner not found for client-side creation.');
            }

            $merchant = Merchant::firstOrCreate(
                ['email' => $brand->user->email],
                ['name' => $brand->user->name, 'active' => true]
            );

            // Check for an existing transaction for this invoice
            $existingTransaction = Transaction::where('system_trx_id', $systemTrxId)
                ->where('brand_id', $brandId)
                ->first();

            if ($existingTransaction) {
                // If it's already completed, we should not proceed.
                if ($existingTransaction->status === 'completed') {
                    throw new \Exception('This transaction has already been completed.');
                }

                // If it's pending, check its status with bKash before creating a new one.
                if ($existingTransaction->status === 'pending' && $existingTransaction->payment_id) {
                    try {
                        // Use forTenant to set the context for this operation
                        $bkash = Bkash::forTenant($brandId);
                        // The token will now be generated using the correct tenant credentials
                        $queryResponse = $bkash->queryPayment($existingTransaction->payment_id);

                    } catch (\Exception $e) {
                        Log::error('bKash token generation failed during client-side create.', ['brand_id' => $brandId, 'error' => $e->getMessage()]);
                        throw new \Exception('Could not connect to payment gateway. Please ensure payment credentials are set up correctly for this brand.');
                    }
                    
                    // If the payment is still valid (not completed, not failed, not cancelled, not expired), reuse it.
                    if (isset($queryResponse['transactionStatus']) && !in_array($queryResponse['transactionStatus'], ['Completed', 'Failed', 'Cancelled', 'Expired'])) {
                        Log::info('Reusing existing pending bKash payment.', ['paymentID' => $existingTransaction->payment_id]);
                        $queryResponse['paymentID'] = $existingTransaction->payment_id; // Ensure paymentID is in the response
                        return response()->json($queryResponse);
                    }
                }
                // If the transaction is not 'completed' but is in a state we can't recover from,
                // or the bKash payment expired, we'll delete it and create a new one.
                $existingTransaction->delete();
            }

            $transaction = Transaction::create([
                'user_id' => $brand->user_id,
                'merchant_id' => $merchant->id,
                'brand_id' => $brandId,
                'system_trx_id' => $systemTrxId,
                'payment_gateway' => 'bkash-merchant',
                'amount' => $amount,
                'currency' => 'BDT',
                'status' => 'pending',
                'trx_id' => $systemTrxId,
            ]);

            $paymentData = [
                'amount' => $amount,
                'currency' => 'BDT',
                'intent' => 'sale',
                'merchantInvoiceNumber' => $systemTrxId,
                'callbackURL' => route('payment.callback'),
            ];

            // Use forTenant to ensure the correct credentials are used for creating the payment
            $bkash = Bkash::forTenant($brandId);
            $response = $bkash->createPayment($paymentData);

            if (isset($response['paymentID'])) {
                $transaction->payment_id = $response['paymentID'];
                $transaction->status_message = 'bKash payment initiated via client-side. Payment ID: ' . $response['paymentID'];
                $transaction->save();
                DB::commit();
                // For client-side, we return the response, not redirect.
                return response()->json($response);
            } else {
                throw new \Exception($response['statusMessage'] ?? 'Failed to create bKash payment.');
            }
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('bKash client-side create payment failed: ' . $e->getMessage());
            return response()->json(['error' => true, 'errorMessage' => $e->getMessage()], 500);
        }
    }

    public function clientSideBkashExecute(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'paymentID' => 'required|string',
            'brand_id' => 'required|exists:brands,id',
        ]);

        if ($validator->fails()) {
            return response()->json(['error' => true, 'errorMessage' => $validator->errors()->first()], 422);
        }

        $paymentID = $validator->validated()['paymentID']; // Use the validated paymentID

        try {
            $transaction = Transaction::where('payment_id', $paymentID)->first();

            if (!$transaction) {
                throw new \Exception('Transaction not found for the given Payment ID.');
            }

            $response = Bkash::forTenant($transaction->brand_id)->executePayment($paymentID); // Corrected: Added forTenant

            if (isset($response['statusCode']) && $response['statusCode'] === '0000' && $response['transactionStatus'] === 'Completed') {
                // Security checks
                if ((float)$response['amount'] != (float)$transaction->amount) {
                    throw new \Exception('Payment amount mismatch.');
                }

                if (($response['merchantInvoiceNumber'] ?? null) !== $transaction->system_trx_id) {
                    throw new \Exception('Payment reference mismatch.');
                }

                // Payment successful
                $transaction->status = 'completed';
                $transaction->trx_id = $response['trxID'];
                $transaction->status_message = 'Payment completed successfully via client-side.';
                $transaction->save();

                $this->sendPaymentSuccessEmail($transaction);

                // Add the internal transaction ID to the response
                $responseData = $response;
                $responseData['internal_transaction_id'] = $transaction->id;
                return response()->json($responseData);
            } else {
                $transaction->status = 'failed';
                $transaction->status_message = $response['statusMessage'] ?? 'Payment execution failed on client-side flow.';
                $transaction->save();
                $this->sendPaymentFailedEmail($transaction);
                throw new \Exception($response['statusMessage'] ?? 'Payment execution failed.');
            }
        } catch (\Exception $e) {
            Log::error('bKash client-side execute payment failed: ' . $e->getMessage());
            return response()->json(['error' => true, 'errorMessage' => $e->getMessage()], 500);
        }
    }

    /**
     * Query payment status
     */
    public function queryPayment(Request $request)
    {
        Log::info('Query Payment Called');
        
        try {
            // Simple test response for query
            $testResponse = [
                'paymentID' => $request->payment_id ?? 'TEST_PAYMENT',
                'transactionStatus' => 'Completed',
                'amount' => '500.00',
                'currency' => 'BDT',
                'merchantInvoiceNumber' => 'INV-' . time(),
                'statusCode' => '0000',
                'statusMessage' => 'Successful'
            ];
            
            return response()->json($testResponse);
            
        } catch (\Exception $e) {
            Log::error('bKash Query Error', [
                'message' => $e->getMessage()
            ]);
            return response()->json(['error' => 'Payment query failed.'], 500);
        }
    }

    /**
     * Show payment success page
     */
    public function paymentSuccess(Request $request)
    {
        $trxID = $request->query('trxID');
        $transactionId = $request->query('transaction_id', session('last_transaction_id')); // More reliable for client-side flow
        $transaction = $transactionId ? Transaction::find($transactionId) : null;
        
        Log::info('Payment Success Page Accessed', [
            'trxID' => $trxID,
            'transaction_id' => $transactionId
        ]);
        
        return view('frontend.payment-success', compact('trxID', 'transaction'));
    }

    /**
     * Show payment failed page
     */
    public function paymentFailed()
    {
        Log::info('Payment Failed Page Accessed');
        return view('frontend.payment-failed');
    }
}