<?php

namespace Modules\Kiosk\Livewire\Kiosk;

use Livewire\Component;
use Modules\Kiosk\Services\KioskCartService;
use App\Models\Order;
use App\Models\OrderItem;
use App\Models\OrderTax;
use App\Models\OrderType;
use App\Models\Tax;
use App\Models\Kot;
use App\Models\KotItem;
use App\Models\Payment;
use App\Models\OrderCharge;
use App\Events\NewOrderCreated;
use App\Events\OrderUpdated;
use App\Models\Customer;
use App\Models\PaymentGatewayCredential;

// Conditionally load the loyalty trait if the module exists, otherwise create a stub trait
$loyaltyTraitPath = base_path('Modules/Loyalty/Traits/HasKioskLoyaltyIntegration.php');
if (file_exists($loyaltyTraitPath)) {
    require_once $loyaltyTraitPath;
} else {
    // Register autoloader FIRST (prepend) to intercept before Composer's autoloader
    spl_autoload_register(function ($class) {
        if ($class === 'Modules\Loyalty\Traits\HasKioskLoyaltyIntegration') {
            // Create stub trait on demand - this prevents autoloader from trying to include missing file
            if (!trait_exists($class, false)) {
                eval('
                    namespace Modules\Loyalty\Traits {
                        trait HasKioskLoyaltyIntegration
                        {
                            // Empty stub trait - all methods are checked with method_exists() before calling
                        }
                    }
                ');
            }
            return true; // Tell autoloader we handled it
        }
        return false; // Let other autoloaders handle other classes
    }, true, true); // prepend=true, so this runs before Composer's autoloader
    
    // Pre-create the stub trait immediately to ensure it exists before class definition
    // This prevents any autoloader from trying to load the missing file
    if (!trait_exists('Modules\Loyalty\Traits\HasKioskLoyaltyIntegration', false)) {
        eval('
            namespace Modules\Loyalty\Traits {
                trait HasKioskLoyaltyIntegration
                {
                    // Empty stub trait - all methods are checked with method_exists() before calling
                }
            }
        ');
    }
}

class PaymentMethod extends Component
{
    // Conditionally use loyalty integration trait if it exists
    // All trait method calls are wrapped with method_exists() checks for safety
    /** @phpstan-ignore-line */
    use \Modules\Loyalty\Traits\HasKioskLoyaltyIntegration;
    
    // Check if loyalty module is available
    protected function isLoyaltyModuleAvailable()
    {
        return function_exists('module_enabled') && 
               module_enabled('Loyalty') && 
               method_exists($this, 'isLoyaltyEnabled');
    }
    
    public $restaurant;
    public $shopBranch;
    public $paymentMethod;
    public $kioskId;
    public $paymentGateway;
    public bool $showQrCode = false;
    
    // Customer information properties
    public $customerName = null;
    public $customerEmail = null;
    public $customerPhone = null;
    public $pickupTime = '15';
    public $customerId = null;
    public $customer = null;

    protected $listeners = [
        'refreshPaymentMethod' => '$refresh'
    ];

    public function mount($restaurant, $shopBranch, $kioskId = null)
    {
        $this->paymentGateway = PaymentGatewayCredential::where('restaurant_id', $restaurant->id)->first();
        $this->restaurant = $restaurant;
        $this->shopBranch = $shopBranch;
        $this->paymentMethod = 'due';
        $this->kioskId = $kioskId ?: session('kiosk_id');
        
        // Load customer info from session
        $this->loadCustomerAndLoyalty();
    }
    
    protected function loadCustomerAndLoyalty()
    {
        $customerInfo = session('customerInfo', []);
        $this->customerName = $customerInfo['name'] ?? null;
        $this->customerEmail = $customerInfo['email'] ?? null;
        $this->customerPhone = $customerInfo['phone'] ?? null;
        $this->pickupTime = $customerInfo['pickup_time'] ?? '15';
        
        // Fix: If email is empty but phone looks like an email, swap them
        if (empty($this->customerEmail) && !empty($this->customerPhone) && filter_var($this->customerPhone, FILTER_VALIDATE_EMAIL)) {
            $this->customerEmail = $this->customerPhone;
            $this->customerPhone = null;
        }
        
        // Try to find customer to get ID for loyalty
        if (!empty($this->customerEmail) || !empty($this->customerPhone)) {
            // First try by email
            if (!empty($this->customerEmail)) {
                $this->customer = Customer::where('email', $this->customerEmail)
                    ->where('restaurant_id', $this->restaurant->id)
                    ->first();
            }
            
            // If not found by email, try by phone
            if (!$this->customer && !empty($this->customerPhone)) {
                $this->customer = Customer::where('phone', $this->customerPhone)
                    ->where('restaurant_id', $this->restaurant->id)
                    ->first();
            }
            
            // If still not found and phone looks like email, try phone as email
            if (!$this->customer && !empty($this->customerPhone) && filter_var($this->customerPhone, FILTER_VALIDATE_EMAIL)) {
                $this->customer = Customer::where('email', $this->customerPhone)
                    ->where('restaurant_id', $this->restaurant->id)
                    ->first();
                if ($this->customer) {
                    $this->customerEmail = $this->customerPhone;
                    $this->customerPhone = null;
                }
            }
            
            if ($this->customer) {
                $this->customerId = $this->customer->id;
                // Ensure email and phone are set from customer record if missing
                if (empty($this->customerEmail) && !empty($this->customer->email)) {
                    $this->customerEmail = $this->customer->email;
                }
                if (empty($this->customerPhone) && !empty($this->customer->phone)) {
                    $this->customerPhone = $this->customer->phone;
                }
                // Load loyalty points and stamps if enabled (using trait method)
                if ($this->isLoyaltyModuleAvailable() && method_exists($this, 'loadCustomerAndLoyaltyData')) {
                    $this->loadCustomerAndLoyaltyData();
                }
            }
        }
    }

    public function processPayment()
    {
        $kioskService = new KioskCartService();
        $cartItemList = $kioskService->getKioskCartSummary($this->shopBranch->id);

        if (empty($cartItemList['items'])) {
            return;
        }

        // Create or find customer
        $customer = $this->createOrFindCustomer();
        
        // Set customer ID for loyalty
        if ($customer) {
            $this->customerId = $customer->id;
            $this->customer = $customer;
        }

        $orderNumberData = Order::generateOrderNumber($this->shopBranch);

        $orderTypeModel = OrderType::where('is_default', 1)
            ->where('type', $cartItemList['order_type'])
            ->first();

        // Calculate totals with loyalty discount applied before tax (using trait method)
        if ($this->isLoyaltyModuleAvailable() && method_exists($this, 'calculateTotalsWithLoyaltyDiscount')) {
            $totals = $this->calculateTotalsWithLoyaltyDiscount($cartItemList);
        } else {
            // Fallback if loyalty module not available
            $totals = [
                'subtotal' => $cartItemList['sub_total'],
                'total' => $cartItemList['total'],
                'totalTaxAmount' => $cartItemList['total_tax_amount'] ?? 0,
                'taxBreakdown' => [],
                'discountedSubtotal' => $cartItemList['sub_total'],
            ];
        }
        $orderSubTotal = $totals['subtotal'];
        $discountedSubtotal = $totals['discountedSubtotal'];
        $orderTotal = $totals['total'];
        $orderTotalTaxAmount = $totals['totalTaxAmount'];
        $serviceTotal = $totals['serviceTotal'] ?? 0;
        $taxBase = $totals['taxBase'] ?? null;
        $taxMode = $cartItemList['tax_mode'];
        
        // Order structure:
        // - sub_total: original subtotal (before discounts)
        // - loyalty_discount_amount: points discount
        // - stamp_discount_amount: stamp discount
        // - total: final total after all discounts and taxes

        $order = Order::create([
            'order_number' => $orderNumberData['order_number'],
            'formatted_order_number' => $orderNumberData['formatted_order_number'],
            'branch_id' => $this->shopBranch->id,
            'table_id' => null,
            'date_time' => now(),
            'customer_id' => $customer->id ?? null,
            'sub_total' => $orderSubTotal, // Original subtotal before discounts
            'total' => $orderTotal, // Final total after discounts (calculated in calculateTotalsWithLoyaltyDiscount)
            'order_type' => $cartItemList['order_type'],
            'order_type_id' => $orderTypeModel->id ?? null,
            'custom_order_type_name' => $orderTypeModel->order_type_name ?? 'dine_in',
            'status' => 'pending_verification',
            'order_status' => $this->restaurant->auto_confirm_orders ? 'confirmed' : 'placed',
            'placed_via' => 'kiosk',
            'tax_mode' => $taxMode,
            'total_tax_amount' => $orderTotalTaxAmount,
            'tax_base' => $taxBase,
            'pickup_date' => $cartItemList['order_type'] === 'pickup' ? now() : null,
            'kiosk_id' => $this->kioskId,
            'loyalty_points_redeemed' => $this->loyaltyPointsRedeemed ?? 0,
            'loyalty_discount_amount' => $this->loyaltyDiscountAmount ?? 0,
            'stamp_discount_amount' => $this->stampDiscountAmount ?? 0, // Set from calculated discount
        ]);

        // Attach applicable service charges to order
        if (method_exists($this, 'getApplicableKioskCharges')) {
            $charges = $this->getApplicableKioskCharges($cartItemList['order_type'] ?? 'dine_in');
            if ($charges->isNotEmpty()) {
                $chargesData = $charges->map(fn($charge) => ['charge_id' => $charge->id])->toArray();
                $order->charges()->createMany($chargesData);
            }
        }
        
        $transactionId = uniqid('KIOSK_TXN_', true);

        $kot = Kot::create([
            'branch_id' => $this->shopBranch->id,
            'kot_number' => (Kot::generateKotNumber($this->shopBranch) + 1),
            'order_id' => $order->id,
            'note' => null,
            'token_number' => Kot::generateTokenNumber($this->shopBranch->id, $order->order_type_id),
            'transaction_id' => $transactionId,
        ]);

        // Create order items from cart BEFORE loyalty/stamp redemption
        // (stamp discount is already applied to cart item amounts)
        foreach ($cartItemList['items'] as $item) {
            $orderItem = OrderItem::create([
                'branch_id' => $this->shopBranch->id,
                'order_id' => $order->id,
                'menu_item_id' => $item['menu_item']['id'],
                'menu_item_variation_id' => $item['variation']['id'] ?? null,
                'quantity' => $item['quantity'],
                'price' => $item['price'],
                'amount' => $item['amount'],
                'transaction_id' => $transactionId,
                'note' => null,
                'tax_amount' => $item['tax_amount'] ?? null,
                'tax_percentage' => $item['tax_percentage'] ?? null,
                'tax_breakup' => !empty($item['tax_breakup']) ? json_encode($item['tax_breakup']) : null,
            ]);

            $kotItem = KotItem::create([
                'kot_id' => $kot->id,
                'menu_item_id' => $item['menu_item']['id'],
                'menu_item_variation_id' => $item['variation']['id'] ?? null,
                'quantity' => $item['quantity'],
                'transaction_id' => $transactionId,
                'note' => null,
            ]);

            if (!empty($item['modifiers'])) {
                $modifierOptionIds = collect($item['modifiers'])->pluck('id')->all();
                $kotItem->modifierOptions()->sync($modifierOptionIds);
                $orderItem->modifierOptions()->sync($modifierOptionIds);
            }
        }

        // Create order taxes BEFORE loyalty/stamp redemption
        if ($cartItemList['tax_mode'] === 'order') {
            $taxes = Tax::withoutGlobalScopes()->where('restaurant_id', $this->restaurant->id)->get();
            foreach ($taxes as $tax) {
                OrderTax::firstOrCreate([
                    'order_id' => $order->id,
                    'tax_id' => $tax->id,
                ]);
            }
        }

        // Redeem loyalty points if applied (using trait method)
        // This happens AFTER order items are created so free items can be added
        if ($this->isLoyaltyModuleAvailable() && method_exists($this, 'processLoyaltyRedemptionForOrder')) {
            $this->processLoyaltyRedemptionForOrder($order, $customer);
        }

        // Redeem stamps if selected (CRITICAL: after order items are created)
        // This adds free items or applies discounts, then recalculates totals
        if ($this->isLoyaltyModuleAvailable() && method_exists($this, 'processStampRedemptionForOrder')) {
            $this->processStampRedemptionForOrder($order, $customer);
        }

        // Refresh order to ensure payment amount is based on the final, potentially updated, total
        $order->refresh();
        $order->load(['items', 'taxes.tax']);

        Payment::create([
            'order_id' => $order->id,
            'branch_id' => $this->shopBranch->id,
            'payment_method' => $this->paymentMethod,
            'amount' => $order->total, // Use order's total from database
            'kiosk_id' => $this->kioskId,
        ]);

        NewOrderCreated::dispatch($order);
        event(new OrderUpdated($order, 'created'));

        $kioskService->clearKioskCart($this->shopBranch->id);


        // Notify kiosk UI to show the confirmation screen with dynamic details
        return $this->redirect(route('kiosk.order-confirmation', $order->uuid), true);
    }

    private function createOrFindCustomer()
    {
        $this->customerName = session('customerInfo')['name'] ?? null;
        $this->customerEmail = session('customerInfo')['email'] ?? null;
        $this->customerPhone = session('customerInfo')['phone'] ?? null;
        $this->pickupTime = session('customerInfo')['pickup_time'] ?? '';

        // If no customer information provided, return null
        if (empty($this->customerName) && empty($this->customerEmail) && empty($this->customerPhone)) {
            return null;
        }

        // Try to find existing customer by email or phone
        // Check all possible combinations to avoid creating duplicate customers
        $customer = null;

        // First, try to find by email (if email field is not empty and looks like email)
        if (!empty($this->customerEmail) && filter_var($this->customerEmail, FILTER_VALIDATE_EMAIL)) {
            $customer = Customer::where('email', $this->customerEmail)
                ->where('restaurant_id', $this->restaurant->id)
                ->first();
        }

        // If not found by email, try by phone
        if (!$customer && !empty($this->customerPhone)) {
            $customer = Customer::where('phone', $this->customerPhone)
                ->where('restaurant_id', $this->restaurant->id)
                ->first();
        }

        // Also check if phone might be an email (common in kiosk where users enter email in phone field)
        if (!$customer && !empty($this->customerPhone) && filter_var($this->customerPhone, FILTER_VALIDATE_EMAIL)) {
            $customer = Customer::where('email', $this->customerPhone)
                ->where('restaurant_id', $this->restaurant->id)
                ->first();
        }

        // Also check if email field contains phone number (reverse check)
        if (!$customer && !empty($this->customerEmail) && !filter_var($this->customerEmail, FILTER_VALIDATE_EMAIL)) {
            // If email field contains phone number, check phone column
            $customer = Customer::where('phone', $this->customerEmail)
                ->where('restaurant_id', $this->restaurant->id)
                ->first();
        }

        // Also check if customer exists with either email OR phone matching either field
        // This handles cases where email is in phone field or vice versa
        if (!$customer) {
            $query = Customer::where('restaurant_id', $this->restaurant->id);
            
            $query->where(function($q) {
                if (!empty($this->customerEmail)) {
                    $q->where('email', $this->customerEmail)
                      ->orWhere('phone', $this->customerEmail);
                }
                if (!empty($this->customerPhone)) {
                    $q->orWhere('email', $this->customerPhone)
                      ->orWhere('phone', $this->customerPhone);
                }
            });
            
            $customer = $query->first();
        }

        // If customer found, update their information if needed
        if ($customer) {
            $updated = false;

            if (!empty($this->customerName) && $customer->name !== $this->customerName) {
                $customer->name = $this->customerName ?? null;
                $updated = true;
            }

            if (!empty($this->customerEmail) && $customer->email !== $this->customerEmail) {
                $customer->email = $this->customerEmail ?? null;
                $updated = true;
            }

            if (!empty($this->customerPhone) && $customer->phone !== $this->customerPhone) {
                $customer->phone = $this->customerPhone ?? null;
                $updated = true;
            }

            if ($updated) {
                $customer->save();
            }
        } else {
            // Create new customer
            $customer = Customer::create([
                'restaurant_id' => $this->restaurant->id,
                'name' => $this->customerName ?? null,
                'email' => strlen($this->customerEmail) > 0 ? $this->customerEmail : null,
                'phone' => strlen($this->customerPhone) > 0 ? $this->customerPhone : null,
            ]);
        }

        return $customer;
    }

    public function selectPaymentMethod(string $method): void
    {
        $this->paymentMethod = $method;

        if ($method === 'upi') {
            $this->showQrCode = true;
        } else {
            $this->showQrCode = false;
        }
    }

    public function render()
    {
        $kioskService = new KioskCartService();
        $cartItemList = $kioskService->getKioskCartSummary($this->shopBranch->id);

        $subtotal = $cartItemList['sub_total'];
        $taxMode = $cartItemList['tax_mode'];
        $cartCount = $cartItemList['count'];
        
        // Ensure customer and loyalty data are loaded
        if (!$this->customerId && ($this->customerName || $this->customerEmail || $this->customerPhone)) {
            $this->loadCustomerAndLoyalty();
        }
        
        // Also try to load customer from session if not already loaded
        if (!$this->customerId) {
            $customerInfo = session('customerInfo', []);
            $email = $customerInfo['email'] ?? null;
            $phone = $customerInfo['phone'] ?? null;
            
            // Fix: If email is empty but phone looks like an email, use phone as email
            if (empty($email) && !empty($phone) && filter_var($phone, FILTER_VALIDATE_EMAIL)) {
                $email = $phone;
                $phone = null;
            }
            
            if (!empty($email) || !empty($phone)) {
                // First try by email
                if (!empty($email)) {
                    $this->customer = Customer::where('email', $email)
                        ->where('restaurant_id', $this->restaurant->id)
                        ->first();
                }
                
                // If not found by email, try by phone
                if (!$this->customer && !empty($phone)) {
                    $this->customer = Customer::where('phone', $phone)
                        ->where('restaurant_id', $this->restaurant->id)
                        ->first();
                }
                
                // If still not found and phone looks like email, try phone as email
                if (!$this->customer && !empty($phone) && filter_var($phone, FILTER_VALIDATE_EMAIL)) {
                    $this->customer = Customer::where('email', $phone)
                        ->where('restaurant_id', $this->restaurant->id)
                        ->first();
                }
                
                if ($this->customer) {
                    $this->customerId = $this->customer->id;
                    // Update component properties
                    if (empty($this->customerEmail) && !empty($this->customer->email)) {
                        $this->customerEmail = $this->customer->email;
                    }
                    if (empty($this->customerPhone) && !empty($this->customer->phone)) {
                        $this->customerPhone = $this->customer->phone;
                    }
                }
            }
        }
        
        // Load loyalty points and stamps if enabled and customer exists (using trait method)
        if ($this->isLoyaltyModuleAvailable() && method_exists($this, 'isLoyaltyEnabled') && $this->isLoyaltyEnabled() && $this->customerId) {
            // Load points if enabled for kiosk
            if (method_exists($this, 'isPointsEnabledForKiosk') && $this->isPointsEnabledForKiosk()) {
                // Only reload if not already redeemed (to preserve redemption)
                if ($this->loyaltyPointsRedeemed == 0 && $this->loyaltyDiscountAmount == 0) {
                    if (method_exists($this, 'loadLoyaltyPoints')) {
                        $this->loadLoyaltyPoints();
                    }
                }
            }
            
            // Load stamps if enabled for kiosk
            if (method_exists($this, 'isStampsEnabledForKiosk') && $this->isStampsEnabledForKiosk()) {
                if (empty($this->customerStamps)) {
                    if (method_exists($this, 'loadCustomerStamps')) {
                        $this->loadCustomerStamps();
                    }
                }
            }
        }
        
        // Calculate totals with loyalty discount (using trait method)
        if ($this->isLoyaltyModuleAvailable() && method_exists($this, 'calculateTotalsWithLoyaltyDiscount')) {
            $totals = $this->calculateTotalsWithLoyaltyDiscount($cartItemList);
        } else {
            // Fallback if loyalty module not available
            $totals = [
                'subtotal' => $cartItemList['sub_total'],
                'discountedSubtotal' => $cartItemList['sub_total'],
                'total' => $cartItemList['total'],
                'totalTaxAmount' => $cartItemList['total_tax_amount'] ?? 0,
                'taxBreakdown' => [],
            ];
        }
        $subtotal = $totals['subtotal']; // Original subtotal before discounts
        $discountedSubtotal = $totals['discountedSubtotal'] ?? $cartItemList['sub_total']; // Subtotal after stamp discount
        $total = $totals['total'];
        $totalTaxAmount = $totals['totalTaxAmount'];
        $taxBreakdown = $totals['taxBreakdown'];
        $serviceTotal = $totals['serviceTotal'] ?? 0;
        $chargeBreakdown = $totals['chargeBreakdown'] ?? [];

        return view('kiosk::livewire.kiosk.payment-method', [
            'cartItemList' => $cartItemList,
            'subtotal' => $subtotal,
            'discountedSubtotal' => $discountedSubtotal,
            'total' => $total,
            'totalTaxAmount' => $totalTaxAmount,
            'taxBreakdown' => $taxBreakdown,
            'serviceTotal' => $serviceTotal,
            'chargeBreakdown' => $chargeBreakdown,
            'taxMode' => $taxMode,
            'cartCount' => $cartCount,
        ]);
    }
}
