<?php

namespace App\Http\Controllers\Api\Pos;

use App\Http\Controllers\Controller;
use App\Models\Order;
use App\Models\Address;
use App\Models\OrderItem;
use App\Models\Product;
use App\Models\ShippingMethod;
use App\Models\StockMovement;
use App\Models\VariantStockMovement;
use App\Models\User;
use App\Services\ProfitService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use App\Models\WhatsappTemplates;
use App\Utility\WhatsappUtility;
use App\Services\OtpService;
use App\Models\Setting;
use PDF;

class OrderController extends Controller
{
    protected ProfitService $profitService;
    protected OtpService $otpService;

    public function __construct(ProfitService $profitService, OtpService $otpService)
    {
        $this->profitService = $profitService;
        $this->otpService = $otpService;
    }
    /**
     * Create POS order.
     * Supports JSON (store_pickup) or multipart/form-data (courier with payment_screenshot).
     */
    public function store(Request $request): JsonResponse
    {
        // Normalize input: multipart may send order as JSON string
        $requestData = $request->all();
        if ($request->hasFile('payment_screenshot')) {
            $requestData['payment_screenshot'] = $request->file('payment_screenshot');
            $orderJson = $request->input('order');
            if (is_string($orderJson)) {
                $decoded = json_decode($orderJson, true);
                if (is_array($decoded)) {
                    $requestData = array_merge($requestData, $decoded);
                }
            }
        }

        $deliveryType = $requestData['delivery_type'] ?? $request->input('delivery_type', 'store_pickup');

        $rules = [
            'user_id' => 'required|exists:users,id',
            'salesman_id' => 'nullable|exists:users,id',
            'items' => 'required|array|min:1',
            'items.*.product_id' => 'required|exists:products,id',
            'items.*.qty' => 'required|integer|min:1',
            'items.*.price' => 'required|numeric|min:0',
            'items.*.discount_percentage' => 'nullable|numeric|min:0|max:100',
            'payment_method' => 'nullable|string|in:cash,card,offline,card_upi,card/upi,upi,online,digital_wallet,cod',
            'cash_amount' => 'nullable|numeric|min:0',
            'card_amount' => 'nullable|numeric|min:0',
            'subtotal' => 'required|numeric|min:0',
            'discount_total' => 'nullable|numeric|min:0',
            'total_payable' => 'required|numeric|min:0',
            'delivery_type' => 'required|in:store_pickup,courier',
            'address_id' => 'nullable|required_if:delivery_type,courier|exists:addresses,id',
            'customization_charges' => 'nullable|numeric|min:0',
            'payment_screenshot' => 'nullable|file|image|mimes:jpeg,png,gif,webp|max:5120',
        ];

        $validator = Validator::make($requestData, $rules);

        if (isset($requestData['payment_method'])) {
            $requestData['payment_method'] = $this->normalizePosPaymentMethod($requestData['payment_method']);
        }

        // Courier: require offline payment and screenshot
        if ($deliveryType === 'courier') {
            if (($requestData['payment_method'] ?? '') !== 'offline') {
                return response()->json([
                    'success' => false,
                    'message' => 'Courier delivery requires Offline payment only.',
                    'errors' => ['payment_method' => ['Courier orders must use Offline payment.']],
                ], 422);
            }
            if (! $request->hasFile('payment_screenshot')) {
                return response()->json([
                    'success' => false,
                    'message' => 'Payment screenshot is required for courier (offline) orders.',
                    'errors' => ['payment_screenshot' => ['Please upload the payment screenshot.']],
                ], 422);
            }
        } else {
            // Store pickup: at least one payment amount must be provided
            $cashAmount = $requestData['cash_amount'] ?? 0;
            $cardAmount = $requestData['card_amount'] ?? 0;
            $totalPayable = $requestData['total_payable'] ?? 0;
            if ($cashAmount == 0 && $cardAmount == 0) {
                return response()->json([
                    'success' => false,
                    'message' => 'At least one payment amount (cash or card) must be provided',
                    'errors' => ['payment' => ['At least one payment amount must be provided']],
                ], 422);
            }
            $totalPaid = $cashAmount + $cardAmount;
            if ($totalPaid < $totalPayable) {
                return response()->json([
                    'success' => false,
                    'message' => 'Total payment amount is less than total payable amount',
                    'errors' => ['payment' => ['Total payment amount is less than total payable']],
                ], 422);
            }
        }

        if ($validator->fails()) {
            return response()->json([
                'success' => false,
                'message' => 'Validation failed',
                'errors' => $validator->errors(),
            ], 422);
        }

        try {
            DB::beginTransaction();

            $user = User::findOrFail($requestData['user_id']);
            $isStaff = $user->isAdmin() || $user->isStaff();
            if ($isStaff) {
                return response()->json([
                    'success' => false,
                    'message' => 'Cannot create order for staff/admin user',
                ], 400);
            }

            $currentUser = auth()->user();
            $role = $currentUser->roles()->first();
            $maxDiscountPercentage = $role ? $role->max_discount_percentage : null;

            $subtotal = $requestData['subtotal'];
            $discountTotal = 0;
            foreach ($requestData['items'] as $item) {
                $itemSubtotal = $item['price'] * $item['qty'];
                $itemDiscountPercentage = $item['discount_percentage'] ?? 0;
                
                // Validate discount percentage against role limit
                if ($maxDiscountPercentage !== null && $itemDiscountPercentage > $maxDiscountPercentage) {
                    DB::rollBack();
                    return response()->json([
                        'success' => false,
                        'message' => "Discount percentage ({$itemDiscountPercentage}%) exceeds maximum allowed ({$maxDiscountPercentage}%) for your role.",
                    ], 400);
                }
                
                $itemDiscount = $itemSubtotal * ($itemDiscountPercentage / 100);
                $discountTotal += $itemDiscount;
            }
            $customizationCharges = $requestData['customization_charges'] ?? 0;
            $totalPayable = $requestData['total_payable'];

            $deliveryType = $requestData['delivery_type'] ?? 'store_pickup';
            $addressSnapshot = [
                'name' => $user->name,
                'mobile' => $user->mobile,
                'email' => $user->email ?? '',
                'delivery_type' => $deliveryType,
            ];

            $deliveryAddressId = null;
            if ($deliveryType === 'courier') {
                $address = Address::with(['country', 'stateRelation', 'cityRelation'])
                    ->where('id', $requestData['address_id'])
                    ->where('user_id', $user->id)
                    ->firstOrFail();

                $deliveryAddressId = $address->id;
                $addressSnapshot = array_merge($addressSnapshot, [
                    'address_id' => $address->id,
                    'line1' => $address->line1,
                    'line2' => $address->line2,
                    'city' => $address->cityRelation?->name ?? $address->city,
                    'state' => $address->stateRelation?->name ?? $address->state,
                    'country' => $address->country?->name,
                    'country_id' => $address->country_id,
                    'state_id' => $address->state_id,
                    'city_id' => $address->city_id,
                    'pin' => $address->pin,
                ]);
            }

            // Calculate shipping for courier orders
            $shippingTotal = 0;
            if ($deliveryType === 'courier') {
                try {
                    $shippingMethod = ShippingMethod::where('code', 'standard')
                        ->where('is_active', true)
                        ->first();
                    
                    if ($shippingMethod) {
                        $shippingTotal = (float) $shippingMethod->price;
                    }
                } catch (\Exception $e) {
                    // Shipping methods table doesn't exist or error occurred, use default
                    $shippingTotal = 0;
                }
            }

            $orderStatus = $deliveryType === 'store_pickup'
                ? Order::STATUS_DELIVERED
                : Order::STATUS_PLACED;

            $cashAmount = $requestData['cash_amount'] ?? 0;
            $cardAmount = $requestData['card_amount'] ?? 0;
            $paymentMethod = null;
            $paymentScreenshotPath = null;

            if ($deliveryType === 'courier') {
                $finalPaymentMethod = 'offline';
                $cashAmount = 0;
                $cardAmount = 0;
                $file = $request->file('payment_screenshot');
                if ($file) {
                    $path = $file->store('order-payments', 'public');
                    $paymentScreenshotPath = $path;
                }
            } else {
                if ($cashAmount > 0 && $cardAmount > 0) {
                    $paymentMethod = 'card';
                } elseif ($cashAmount > 0) {
                    $paymentMethod = 'cash';
                } elseif ($cardAmount > 0) {
                    $paymentMethod = 'card';
                }
                $finalPaymentMethod = $this->normalizePosPaymentMethod($requestData['payment_method'] ?? $paymentMethod);
            }

            $orderSource = $deliveryType === 'courier' ? 'pos_to_online' : 'pos';

            $order = Order::create([
                'user_id' => $user->id,
                'staff_id' => $requestData['salesman_id'] ?: auth()->id(),
                'source' => $orderSource,
                'address_snapshot' => $addressSnapshot,
                'subtotal' => $subtotal,
                'discount_total' => $discountTotal,
                'tax_total' => 0,
                'shipping_total' => $shippingTotal,
                'customization_charges' => $customizationCharges,
                'total_payable' => $totalPayable,
                'payment_status' => Order::PAYMENT_PAID,
                'payment_method' => $finalPaymentMethod,
                'cash_amount' => $cashAmount > 0 ? $cashAmount : null,
                'card_amount' => $cardAmount > 0 ? $cardAmount : null,
                'payment_screenshot_path' => $paymentScreenshotPath,
                'status' => $orderStatus,
                'placed_at' => now(),
            ]);

            // Process order items and reduce stock
            $cartItems = [];
            foreach ($requestData['items'] as $item) {
                // Unified model: product_id is the actual sellable product (single or variant)
                $product = Product::lockForUpdate()->findOrFail($item['product_id']);

                // Check product stock (unified logic for all products)
                if ($product->current_stock < $item['qty']) {
                    DB::rollBack();
                    return response()->json([
                        'success' => false,
                        'message' => "Insufficient stock for product: {$product->name}. Available: {$product->current_stock}, Requested: {$item['qty']}",
                    ], 400);
                }

                // Create stock movement: single products use StockMovement, variant products use VariantStockMovement (matches Product::recalculateStock)
                $movementPayload = [
                    'product_id' => $product->id,
                    'quantity' => -$item['qty'], // Negative for sale
                    'notes' => "POS Order #{$order->id}",
                    'created_by' => auth()->id(),
                ];
                if ($product->isVariant()) {
                    VariantStockMovement::create(array_merge($movementPayload, ['type' => VariantStockMovement::TYPE_SALE]));
                } else {
                    StockMovement::create(array_merge($movementPayload, ['type' => StockMovement::TYPE_SALE]));
                }

                // Recalculate stock
                $product->recalculateStock();

                // Prepare cart item data for profit service
                $cartItemData = [
                    'product_id' => $item['product_id'],
                    'qty' => $item['qty'],
                    'price' => $item['price'],
                    'discount_percentage' => $item['discount_percentage'] ?? 0,
                ];

                $cartItems[] = $cartItemData;
            }

            // Use ProfitService to create order items with profit calculations and tax calculation
            $this->profitService->allocateOrderCosts($order, $cartItems);
            
            // Refresh order to get calculated tax_total
            $order->refresh();
            
            // Recalculate total_payable
            // Prices are tax-inclusive, so total = subtotal - discount + shipping + customization_charges
            $order->update([
                'total_payable' => $order->subtotal - $order->discount_total + $order->shipping_total + $order->customization_charges
            ]);
            
            // Change amount only for store_pickup (cash/card overpayment)
            if ($deliveryType === 'store_pickup') {
                $totalPaid = $cashAmount + $cardAmount;
                $changeAmount = 0;
                if ($totalPaid > $order->total_payable) {
                    $changeAmount = $totalPaid - (float) $order->total_payable;
                }
                $order->update(['change_amount' => $changeAmount]);
            }

            DB::commit();

            // Send WhatsApp invoice PDF
            $this->sendWhatsAppInvoicePdf($order);

            return response()->json([
                'success' => true,
                'message' => 'Order created successfully',
                'data' => [
                    'order_id' => $order->id,
                    'order_number' => '#' . str_pad($order->id, 6, '0', STR_PAD_LEFT),
                ],
            ], 201);
        } catch (\Exception $e) {
            DB::rollBack();
            return response()->json([
                'success' => false,
                'message' => 'Failed to create order: ' . $e->getMessage(),
            ], 500);
        }
    }

    /**
     * Request OTP for discount/complete-order. Sends to Store MD/Manager mobile if configured, else staff mobile.
     */
    public function requestDiscountOtp(Request $request): JsonResponse
    {
        $user = auth()->user();
        $recipientMobile = \App\Models\Setting::get('sms_store_manager_mobile');
        if (empty($recipientMobile) && $user && !empty($user->mobile)) {
            $recipientMobile = $user->mobile;
        }

        if (empty($recipientMobile)) {
            return response()->json([
                'success' => false,
                'message' => 'No mobile number configured for OTP. Set Store MD/Manager mobile in Admin > Settings > SMS Configuration, or set your profile mobile.',
            ], 400);
        }

        try {
            $otp = $this->otpService->generateOtp();
            $sent = $this->otpService->sendDiscountOtp($recipientMobile, $otp);
            $this->otpService->storeOtp($recipientMobile, $otp, 'discount');

            return response()->json([
                'success' => true,
                'message' => 'OTP sent successfully',
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => $e->getMessage(),
            ], 500);
        }
    }

    /**
     * Verify OTP for discount/complete-order. Uses same recipient as request (Store MD/Manager or staff mobile).
     */
    public function verifyDiscountOtp(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'otp' => 'required|string|size:6',
        ]);

        $user = auth()->user();
        $recipientMobile = \App\Models\Setting::get('sms_store_manager_mobile');
        if (empty($recipientMobile) && $user && !empty($user->mobile)) {
            $recipientMobile = $user->mobile;
        }

        if (empty($recipientMobile)) {
            return response()->json([
                'success' => false,
                'message' => 'No mobile number configured for OTP verification.',
            ], 400);
        }

        $verified = $this->otpService->verifyOtp($recipientMobile, $validated['otp'], 'discount');

        if ($verified) {
            return response()->json([
                'success' => true,
                'message' => 'OTP verified successfully',
            ]);
        } else {
            return response()->json([
                'success' => false,
                'message' => 'Invalid or expired OTP',
            ], 400);
        }
    }

    protected function sendWhatsAppInvoicePdf(Order $order): void
    {
        // Send Invoice PDF via WhatsApp
        $WhatsAppTemplate = WhatsappTemplates::where('identifier', 'pos_order_placement')->first();
        
        if ($WhatsAppTemplate && $WhatsAppTemplate->is_active == 1) {
            try {
                $customer = User::findOrFail($order->user_id); 
                
                if ($customer && $customer->mobile != '9999999999' && !empty($customer->mobile)) {
                    $pdf = PDF::loadView('admin.orders.thermal_invoice', [
                        'order' => $order
                    ]);

                    $pdfDir = public_path('storage/invoices');
                    if (!is_dir($pdfDir)) {
                        mkdir($pdfDir, 0755, true);
                    }

                    $pdfPath = $pdfDir . DIRECTORY_SEPARATOR . 'order-' . $order->id . '.pdf';
                    $pdf->save($pdfPath);

                    $pdfUrl = asset('storage/invoices/order-' . $order->id . '.pdf');
                    WhatsappUtility::pos_order_placement($customer, $order, $pdfUrl);

                    // For wholesale customers, also send a copy to site owner (General Settings > Site Phone).
                    $ownerCopyEnabled = Setting::get('whatsapp_pos_owner_copy_enabled', '0') === '1';
                    if ($ownerCopyEnabled && ($customer->customer_type ?? 'retail') === 'wholesale') {
                        $ownerMobile = $this->extractPrimaryMobile((string) Setting::get('site_phone', ''));
                        if ($ownerMobile !== '' && $this->normalizeMobileForCompare($ownerMobile) !== $this->normalizeMobileForCompare((string) $customer->mobile)) {
                            WhatsappUtility::pos_order_placement($customer, $order, $pdfUrl, $ownerMobile);
                        }
                    }
                }
            } catch (\Exception $e) {
                \Log::error('WhatsApp PDF sending failed: ' . $e->getMessage());
            }
        }
    }

    private function extractPrimaryMobile(string $raw): string
    {
        if ($raw === '') {
            return '';
        }

        // Supports values like "+91XXXXXXXXXX, +91YYYYYYYYYY" and picks the first valid number.
        preg_match_all('/\+?\d{10,15}/', $raw, $matches);
        $numbers = $matches[0] ?? [];
        if (empty($numbers)) {
            return '';
        }

        return trim((string) $numbers[0]);
    }

    private function normalizeMobileForCompare(string $mobile): string
    {
        $digits = preg_replace('/\D+/', '', $mobile) ?: '';
        if (strlen($digits) > 10) {
            return substr($digits, -10);
        }

        return $digits;
    }

    public function posRecipt($id): JsonResponse
    {
        $order = Order::with(['user', 'items.product', 'staff'])
            ->whereIn('source', ['pos', 'pos_to_online'])
            ->findOrFail($id);

        return response()->json([
            'success' => true,
            'pos_recipt_view' => view('admin.orders.thermal_invoice', ['order' => $order])->render(),
        ]);
    }

    /**
     * Get shipping charges for courier delivery
     */
    public function getShippingCharges(): JsonResponse
    {
        try {
            $shippingMethod = ShippingMethod::where('code', 'standard')
                ->where('is_active', true)
                ->first();
            
            $shippingCharges = 0;
            if ($shippingMethod) {
                $shippingCharges = (float) $shippingMethod->price;
            }
            
            return response()->json([
                'success' => true,
                'shipping_charges' => $shippingCharges,
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'success' => true,
                'shipping_charges' => 0,
            ]);
        }
    }

    /**
     * Get POS order details
     */
    public function show($id): JsonResponse
    {
        $order = Order::with(['user', 'items.product', 'staff'])
            ->where('source', 'pos')
            ->findOrFail($id);

        return response()->json([
            'success' => true,
            'data' => [
                'id' => $order->id,
                'order_number' => '#' . str_pad($order->id, 6, '0', STR_PAD_LEFT),
                'user' => [
                    'id' => $order->user->id,
                    'name' => $order->user->name,
                    'mobile' => $order->user->mobile,
                    'email' => $order->user->email,
                    'customer_type' => $order->user->customer_type ?? 'retail',
                ],
                'staff' => $order->staff ? [
                    'id' => $order->staff->id,
                    'name' => $order->staff->name,
                ] : null,
                'items' => $order->items->map(function ($item) {
                    return [
                        'id' => $item->id,
                        'product_id' => $item->product_id,
                        'name' => $item->name,
                        'qty' => $item->qty,
                        'price' => (float) $item->price,
                        'line_total' => (float) $item->line_total,
                    ];
                }),
                'subtotal' => (float) $order->subtotal,
                'discount_total' => (float) $order->discount_total,
                'tax_total' => (float) $order->tax_total,
                'shipping_total' => (float) $order->shipping_total,
                'customization_charges' => (float) $order->customization_charges,
                'total_payable' => (float) $order->total_payable,
                'payment_method' => $order->payment_method,
                'cash_amount' => $order->cash_amount ? (float) $order->cash_amount : null,
                'card_amount' => $order->card_amount ? (float) $order->card_amount : null,
                'payment_status' => $order->payment_status,
                'status' => $order->status,
                'delivery_type' => $order->address_snapshot['delivery_type'] ?? 'store_pickup',
                'delivery_address' => isset($order->address_snapshot['delivery_type']) && $order->address_snapshot['delivery_type'] === 'courier' ? [
                    'line1' => $order->address_snapshot['line1'] ?? '',
                    'line2' => $order->address_snapshot['line2'] ?? '',
                    'city' => $order->address_snapshot['city'] ?? '',
                    'state' => $order->address_snapshot['state'] ?? '',
                    'pin' => $order->address_snapshot['pin'] ?? '',
                ] : null,
                'placed_at' => $order->placed_at?->toDateTimeString(),
                'created_at' => $order->created_at->toDateTimeString(),
            ],
        ]);
    }

    private function normalizePosPaymentMethod(?string $paymentMethod): ?string
    {
        if ($paymentMethod === null || $paymentMethod === '') {
            return $paymentMethod;
        }

        return match (strtolower(trim($paymentMethod))) {
            'cash', 'cod' => 'cash',
            'card', 'card_upi', 'card/upi', 'upi', 'online', 'digital_wallet' => 'card',
            'offline' => 'offline',
            default => $paymentMethod,
        };
    }
}
