<?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\OrderPlacementNotificationService;
use App\Services\ProfitService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use App\Models\Setting;
use App\Services\OtpService;

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',
            'request_fingerprint' => 'nullable|string|max:64',
            '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);
        }

        $idempotencyFingerprint = $requestData['request_fingerprint'] ?? $this->buildOrderFingerprint($requestData);
        $dedupeCacheKey = null;
        if ($idempotencyFingerprint) {
            $dedupeCacheKey = 'pos_order_dedupe:' . auth()->id() . ':' . $idempotencyFingerprint;
            $existingOrderId = Cache::get($dedupeCacheKey);
            if ($existingOrderId) {
                $existingOrder = Order::whereIn('source', ['pos', 'pos_to_online'])->find($existingOrderId);
                if ($existingOrder) {
                    Log::warning('POS duplicate order request blocked', [
                        'order_id' => $existingOrder->id,
                        'fingerprint' => $idempotencyFingerprint,
                        'staff_id' => auth()->id(),
                    ]);

                    return response()->json([
                        'success' => true,
                        'message' => 'Order already processed. Returning existing order.',
                        'data' => [
                            'order_id' => $existingOrder->id,
                            'order_number' => '#' . str_pad($existingOrder->id, 6, '0', STR_PAD_LEFT),
                        ],
                    ], 200);
                }
            }
        }

        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 = $shippingMethod->calculateCharge((float) $subtotal);
                    }
                } 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(),
            ]);

            if ($dedupeCacheKey !== null) {
                // 45s window catches accidental repeat clicks/retries after slow response.
                Cache::put($dedupeCacheKey, $order->id, now()->addSeconds(45));
            }

            // 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();

            app(OrderPlacementNotificationService::class)->dispatchAfterPlacement($order, OrderPlacementNotificationService::CHANNEL_POS);

            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);
        }
    }

    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(Request $request): JsonResponse
    {
        try {
            $shippingMethod = ShippingMethod::where('code', 'standard')
                ->where('is_active', true)
                ->first();
            
            $shippingCharges = 0;
            if ($shippingMethod) {
                $subtotal = $request->has('subtotal') ? (float) $request->query('subtotal') : null;
                $shippingCharges = $subtotal !== null
                    ? $shippingMethod->calculateCharge($subtotal)
                    : (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,
        };
    }

    private function buildOrderFingerprint(array $requestData): string
    {
        $items = collect($requestData['items'] ?? [])
            ->map(fn ($item) => [
                'product_id' => (int) ($item['product_id'] ?? 0),
                'qty' => (int) ($item['qty'] ?? 0),
                'price' => (string) ($item['price'] ?? '0'),
                'discount_percentage' => (string) ($item['discount_percentage'] ?? '0'),
            ])
            ->sortBy(fn ($item) => implode(':', [$item['product_id'], $item['qty'], $item['price'], $item['discount_percentage']]))
            ->values()
            ->all();

        $payload = [
            'user_id' => (int) ($requestData['user_id'] ?? 0),
            'salesman_id' => (int) ($requestData['salesman_id'] ?? 0),
            'delivery_type' => (string) ($requestData['delivery_type'] ?? 'store_pickup'),
            'address_id' => (int) ($requestData['address_id'] ?? 0),
            'cash_amount' => (string) ($requestData['cash_amount'] ?? '0'),
            'card_amount' => (string) ($requestData['card_amount'] ?? '0'),
            'subtotal' => (string) ($requestData['subtotal'] ?? '0'),
            'discount_total' => (string) ($requestData['discount_total'] ?? '0'),
            'customization_charges' => (string) ($requestData['customization_charges'] ?? '0'),
            'total_payable' => (string) ($requestData['total_payable'] ?? '0'),
            'items' => $items,
        ];

        return hash('sha256', json_encode($payload));
    }
}
