<?php

namespace App\Services;

use App\Models\DiscountCode;
use App\Models\Order;
use App\Models\OrderItem;
use App\Models\Address;
use App\Models\Product;
use App\Models\ProductVariant;
use App\Services\MailService;
use Illuminate\Support\Facades\DB;

class OrderService
{
    public function __construct(
        private CartService $cartService,
        private MailService $mailService,
    ) {}

    public function createFromCart(array $data, $user = null): Order
    {
        return DB::transaction(function () use ($data, $user) {
            $items = $data['items'];

            // Stock validation — lock rows to prevent race conditions
            foreach ($items as $item) {
                if ($item['is_preorder'] ?? false) continue;

                $product = Product::lockForUpdate()->find($item['product_id']);
                if (!$product) continue;

                $variant = isset($item['variant_id']) ? ProductVariant::lockForUpdate()->find($item['variant_id']) : null;

                if ($variant) {
                    // Variant products: always track per-variant stock
                    $available = (int) $variant->stock_quantity;
                } elseif ($product->track_stock) {
                    // Simple products: track only if track_stock is enabled
                    $available = (int) $product->stock_quantity;
                } else {
                    continue; // Unlimited stock
                }

                if ($item['quantity'] > $available) {
                    $label = $item['name'];
                    if (!empty($item['variant_name'])) $label .= ' – ' . $item['variant_name'];
                    abort(422, "Only {$available} unit(s) of \"{$label}\" available in stock.");
                }
            }

            $subtotal = 0;
            foreach ($items as $item) {
                $disc = CartService::bulkDiscount($item['quantity']);
                $effectivePrice = $item['price'] * (1 - $disc);
                $subtotal += round($effectivePrice * $item['quantity'], 2);
            }

            // Discount
            $discountAmount = 0;
            $discountCode = $data['discount_code'] ?? null;
            if ($discountCode) {
                $discount = DiscountCode::where('code', strtoupper($discountCode))->first();
                if ($discount && $discount->isValid()) {
                    $discountAmount = $discount->calculateDiscount($subtotal);
                    $discount->increment('uses_count');
                }
            }

            // Loyalty points
            $loyaltyPointsUsed = 0;
            $loyaltyDiscount = 0;
            if ($user && isset($data['loyalty_points_to_use']) && $data['loyalty_points_to_use'] > 0) {
                $pointsToUse = min((int) $data['loyalty_points_to_use'], (int) $user->loyalty_points);
                $loyaltyDiscount = round($pointsToUse / 1000, 2);
                $loyaltyPointsUsed = $pointsToUse;
            }

            $shippingCost = (float) ($data['shipping_cost'] ?? 0);
            $total = max(0, $subtotal - $discountAmount - $loyaltyDiscount + $shippingCost);

            $order = Order::create([
                'order_number' => Order::generateOrderNumber(),
                'user_id' => $user?->id,
                'guest_email' => $user ? null : ($data['guest_email'] ?? null),
                'status' => 'pending',
                'subtotal' => $subtotal,
                'shipping_cost' => $shippingCost,
                'discount_amount' => $discountAmount + $loyaltyDiscount,
                'discount_code' => $discountCode,
                'tax_amount' => 0,
                'total' => $total,
                'currency' => 'GBP',
                'loyalty_points_used' => $loyaltyPointsUsed,
                'notes' => $data['notes'] ?? null,
                'utm_source'   => $data['utm_source'] ?? null,
                'utm_medium'   => $data['utm_medium'] ?? null,
                'utm_campaign' => $data['utm_campaign'] ?? null,
                'utm_content'  => $data['utm_content'] ?? null,
                'utm_term'     => $data['utm_term'] ?? null,
                'referrer_url' => $data['referrer_url'] ?? null,
                'landing_page' => $data['landing_page'] ?? null,
            ]);

            // Order items
            foreach ($items as $item) {
                $product = Product::find($item['product_id']);
                $variant = isset($item['variant_id']) ? ProductVariant::find($item['variant_id']) : null;

                OrderItem::create([
                    'order_id' => $order->id,
                    'product_id' => $item['product_id'],
                    'variant_id' => $item['variant_id'] ?? null,
                    'product_name' => $item['name'],
                    'variant_name' => $item['variant_name'] ?? null,
                    'sku' => $variant?->sku ?? null,
                    'quantity' => $item['quantity'],
                    'unit_price' => round($item['price'] * (1 - CartService::bulkDiscount($item['quantity'])), 2),
                    'total_price' => round($item['price'] * (1 - CartService::bulkDiscount($item['quantity'])) * $item['quantity'], 2),
                    'is_preorder' => $item['is_preorder'] ?? false,
                    'preorder_estimated_days' => ($item['is_preorder'] ?? false) ? ($item['preorder_estimated_days'] ?? null) : null,
                ]);

                // Deduct stock (never below zero)
                // Variant products: stock lives on the variant only
                // Simple products: stock lives on the product
                if (!($item['is_preorder'] ?? false)) {
                    if ($variant) {
                        $variantDeduct = min($item['quantity'], max(0, $variant->stock_quantity));
                        if ($variantDeduct > 0) {
                            $variant->decrement('stock_quantity', $variantDeduct);
                        }
                    } elseif ($product && $product->track_stock) {
                        $deduct = min($item['quantity'], max(0, $product->stock_quantity));
                        if ($deduct > 0) {
                            $product->decrement('stock_quantity', $deduct);
                        }
                    }
                }
            }

            // Shipping address (order-scoped only — user saves explicitly via checkout checkbox)
            Address::create(array_merge(
                $data['shipping_address'],
                ['order_id' => $order->id, 'type' => 'shipping']
            ));

            // Billing address
            $billingData = $data['billing_same'] ? $data['shipping_address'] : $data['billing_address'];
            Address::create(array_merge(
                $billingData,
                ['order_id' => $order->id, 'type' => 'billing']
            ));

            // Deduct loyalty points
            if ($user && $loyaltyPointsUsed > 0) {
                $user->decrement('loyalty_points', $loyaltyPointsUsed);
                \App\Models\LoyaltyTransaction::create([
                    'user_id' => $user->id,
                    'order_id' => $order->id,
                    'points' => -$loyaltyPointsUsed,
                    'type' => 'redeem',
                    'description' => "Redeemed for order {$order->order_number}",
                ]);
            }

            // Award loyalty points for this purchase
            if ($user) {
                $pointsEarned = $order->items->sum(function ($item) {
                    $product = \App\Models\Product::find($item->product_id);
                    if (!$product) return 0;
                    // Only award points if explicitly set on the product
                    $perUnit = (int) $product->loyalty_points_earn;
                    if ($perUnit <= 0) return 0;
                    return $perUnit * $item->quantity;
                });
                if ($pointsEarned > 0) {
                    $user->increment('loyalty_points', $pointsEarned);
                    \App\Models\LoyaltyTransaction::create([
                        'user_id' => $user->id,
                        'order_id' => $order->id,
                        'points' => $pointsEarned,
                        'type' => 'earn',
                        'description' => "Earned for order {$order->order_number}",
                    ]);
                }
            }

            $order->load(['items', 'shippingAddress', 'billingAddress', 'user']);

            // Send confirmation emails (non-blocking — errors are logged)
            $this->mailService->sendOrderConfirmed($order);
            $this->mailService->sendNewOrderAdmin($order);

            return $order;
        });
    }
}
