<?php

declare(strict_types=1);

namespace App\Controllers\Api;

use App\Models\ShopModel;

final class ShopApiController extends BaseApiController
{
    private ShopModel $shop;

    public function __construct()
    {
        parent::__construct();
        $this->shop = new ShopModel();
    }

    // GET /api/v1/settings
    public function settings(): void
    {
        $s = $this->shop->settings();
        $modes = $this->db->fetchAll(
            "SELECT name FROM payment_modes WHERE is_active = 1 ORDER BY sort_order, id"
        );

        $this->ok([
            'restaurant_name'    => $s['restaurant_name']    ?? '',
            'restaurant_phone'   => $s['restaurant_phone']   ?? '',
            'restaurant_address' => $s['restaurant_address'] ?? '',
            'restaurant_logo'    => !empty($s['restaurant_logo']) ? upload_url($s['restaurant_logo']) : null,
            'restaurant_lat'     => $s['restaurant_lat']     ?? null,
            'restaurant_lng'     => $s['restaurant_lng']     ?? null,
            'currency_symbol'    => $s['currency_symbol']    ?? 'Rs.',
            'delivery_charge_type'  => $s['delivery_charge_type']  ?? 'fixed',
            'delivery_fixed_fee'    => (float)($s['delivery_fixed_fee']    ?? $s['delivery_fee'] ?? 0),
            'delivery_min_fee'      => (float)($s['delivery_min_fee']      ?? 0),
            'delivery_per_km_rate'  => (float)($s['delivery_per_km_rate']  ?? 0),
            'delivery_free_above'   => (float)($s['delivery_free_above']   ?? 0),
            'tax_rate'           => (float)($s['tax_rate']      ?? 0),
            'min_order_amount'   => (float)($s['min_order_amount'] ?? 0),
            'show_rider_location'=> ($s['show_rider_location'] ?? '0') === '1',
            'delivery_radius_km' => (float)($s['delivery_radius_km'] ?? 0),
            'hours'              => $this->shop->hoursStatus($s),
            'payment_modes'      => array_column($modes, 'name'),
            'first_order_discount' => [
                'enabled'      => ($s['first_order_discount_enabled'] ?? '0') === '1',
                'type'         => $s['first_order_discount_type'] ?? 'percentage',
                'value'        => (float)($s['first_order_discount_value'] ?? 0),
                'min_amount'   => (float)($s['first_order_min_amount'] ?? 0),
                'max_discount' => (float)($s['first_order_max_discount'] ?? 0),
            ],
            'order_discount' => [
                'enabled'      => ($s['order_discount_enabled'] ?? '0') === '1',
                'type'         => $s['order_discount_type'] ?? 'percentage',
                'value'        => (float)($s['order_discount_value'] ?? 0),
                'min_amount'   => (float)($s['order_discount_min_amount'] ?? 0),
                'max_discount' => (float)($s['order_discount_max_discount'] ?? 0),
            ],
        ]);
    }

    // GET /api/v1/categories
    public function categories(): void
    {
        $cats = $this->shop->activeCategories();
        foreach ($cats as &$c) {
            $c['icon_url'] = !empty($c['icon_image']) ? upload_url($c['icon_image']) : null;
        }
        $this->ok($cats);
    }

    // GET /api/v1/items?cat=&search=
    public function items(): void
    {
        $catId  = (int)$this->get('cat');
        $search = trim((string)$this->get('search'));

        if ($catId) {
            $items = $this->shop->itemsByCategory($catId);
        } else {
            $items = $this->shop->allActiveItems();
        }

        if ($search) {
            $items = array_values(array_filter($items, fn($i) =>
                stripos($i['name'], $search) !== false ||
                stripos($i['description'] ?? '', $search) !== false
            ));
        }

        $offers = $this->shop->activeOffers();
        foreach ($items as &$item) {
            $item['image_url'] = !empty($item['item_image']) ? upload_url($item['item_image']) : null;
            $this->annotateItemDiscount($item, $offers);
        }
        $this->ok($items);
    }

    // GET /api/v1/items/{id}
    public function itemDetail(array $params = []): void
    {
        $id   = (int)($params['id'] ?? 0);
        $item = $this->shop->findItem($id);
        if (!$item) $this->fail('Item not found.', 404);

        $item['image_url'] = !empty($item['item_image']) ? upload_url($item['item_image']) : null;
        $item['groups']    = $this->shop->itemChoiceGroups($id);
        $this->annotateItemDiscount($item, $this->shop->activeOffers());
        $this->ok($item);
    }

    /// Adds discount_price / discount_percent to an item when an offer applies.
    private function annotateItemDiscount(array &$item, array $offers): void
    {
        $item['discount_price']   = null;
        $item['discount_percent'] = null;
        $bp = (float)($item['base_price'] ?? 0);
        if ($bp <= 0 || empty($offers)) return;
        $disc = $this->shop->itemDisplayDiscount($bp, (int)$item['id'],
            isset($item['category_id']) ? (int)$item['category_id'] : null, $offers);
        if ($disc > 0) {
            $item['discount_price']   = round($bp - $disc, 2);
            $item['discount_percent'] = (int)round($disc / $bp * 100);
        }
    }

    // GET /api/v1/deals
    public function deals(): void
    {
        $this->ok($this->shop->activeDeals());
    }

    // GET /api/v1/discount-policies   (customer-facing active discounts)
    public function discountPolicies(): void
    {
        $this->ok($this->shop->discountPolicies());
    }

    // POST /api/v1/delivery-fee
    public function deliveryFee(): void
    {
        $orderType = (string)$this->post('order_type', 'delivery');
        $subtotal  = (float)$this->post('subtotal', 0);
        $lat       = $this->post('lat') !== '' ? (float)$this->post('lat') : null;
        $lng       = $this->post('lng') !== '' ? (float)$this->post('lng') : null;
        $settings  = $this->shop->settings();
        $fee       = $this->shop->calculateDeliveryFee($orderType, $subtotal, $lat, $lng, $settings);
        $this->ok(['fee' => $fee]);
    }

    // POST /api/v1/voucher/validate
    public function validateVoucher(): void
    {
        $code  = strtoupper(trim((string)$this->post('code')));
        $total = (float)$this->post('total', 0);
        $user  = $this->authUser();
        $cid   = $user ? $this->customerId($user) : null;
        $this->ok($this->shop->validateVoucher($code, $total, $cid));
    }

    /// Compute all order amounts (single source of truth for quote + placeOrder).
    private function computeTotals(array $cartItems, string $orderType,
        ?float $lat, ?float $lng, string $voucherCode, ?int $customerId,
        array $settings): array
    {
        $subtotal = array_sum(array_map(
            fn($i) => (float)($i['unit_price'] ?? 0) * (int)($i['qty'] ?? 1), $cartItems));
        $deliveryFee = $this->shop->calculateDeliveryFee($orderType, $subtotal, $lat, $lng, $settings);
        $taxAmount   = round($subtotal * (float)($settings['tax_rate'] ?? 0) / 100, 2);

        $voucherDiscount = 0.0; $voucherId = null;
        if ($voucherCode !== '') {
            $r = $this->shop->validateVoucher($voucherCode, $subtotal, $customerId);
            if ($r['valid']) { $voucherDiscount = (float)$r['discount']; $voucherId = $r['voucher']['id']; }
        }
        // Store-wide order discount (settings) + active discount offers.
        $orderDiscount = $this->shop->orderDiscount($subtotal, $settings)
                       + $this->shop->offersDiscount($subtotal, $cartItems);
        $orderDiscount = min($orderDiscount, $subtotal);
        $firstOrderDiscount = ($customerId && $this->shop->isFirstOrder($customerId))
            ? $this->shop->firstOrderDiscount($subtotal, $settings) : 0.0;

        $discountTotal = min($voucherDiscount + $orderDiscount + $firstOrderDiscount, $subtotal);
        $total = max(0, $subtotal + $deliveryFee + $taxAmount - $discountTotal);

        return [
            'subtotal'             => round($subtotal, 2),
            'delivery_fee'         => round($deliveryFee, 2),
            'tax'                  => round($taxAmount, 2),
            'order_discount'       => round($orderDiscount, 2),
            'first_order_discount' => round($firstOrderDiscount, 2),
            'voucher_discount'     => round($voucherDiscount, 2),
            'discount_total'       => round($discountTotal, 2),
            'total'                => round($total, 2),
            'voucher_id'           => $voucherId,
        ];
    }

    // POST /api/v1/order/quote   (live bill preview — auth optional)
    public function quote(): void
    {
        $user       = $this->authUser();
        $customerId = $user ? $this->customerId($user) : null;
        $orderType  = (string)$this->post('order_type', 'delivery');
        $lat        = $this->post('lat') !== '' ? (float)$this->post('lat') : null;
        $lng        = $this->post('lng') !== '' ? (float)$this->post('lng') : null;
        $voucher    = strtoupper(trim((string)$this->post('voucher_code')));
        $cartItems  = (array)$this->post('cart');

        $settings = $this->shop->settings();
        $t = $this->computeTotals($cartItems, $orderType, $lat, $lng, $voucher,
            $customerId, $settings);
        unset($t['voucher_id']);

        // Delivery range info for the checkout to gate ordering.
        $t['distance_km']  = $this->shop->distanceFromRestaurant($lat, $lng, $settings);
        $t['out_of_range'] = $orderType === 'delivery'
            && !$this->shop->withinDeliveryRange($lat, $lng, $settings);
        $this->ok($t);
    }

    // POST /api/v1/orders   (place order — auth OR guest)
    public function placeOrder(): void
    {
        $user       = $this->authUser();
        $customerId = $user ? $this->customerId($user) : null;

        // Authenticated user missing a customers row (e.g. created via admin
        // or before customer provisioning) — create it so they aren't treated
        // as a guest.
        if ($user && !$customerId) {
            $this->db->insert('customers', ['user_id' => $user['id']]);
            $customerId = $this->customerId($user);
        }

        $orderType = (string)$this->post('order_type', 'delivery');
        $address   = (string)$this->post('address');
        $lat       = $this->post('lat') !== '' ? (float)$this->post('lat') : null;
        $lng       = $this->post('lng') !== '' ? (float)$this->post('lng') : null;
        $payment   = (string)$this->post('payment_method', 'cash');
        $notes     = (string)$this->post('notes');
        $cartItems = (array)$this->post('cart');  // [{item_id, name, unit_price, qty, choices, choices_label}]

        if (empty($cartItems)) $this->fail('Cart is empty.');

        $settingsAll = $this->shop->settings();

        // Reject orders outside working hours.
        $hours = $this->shop->hoursStatus($settingsAll);
        if (!$hours['is_open']) {
            $this->fail('We are currently closed. Please order during working hours.', 422);
        }
        // Reject deliveries outside the delivery range.
        if ($orderType === 'delivery'
            && !$this->shop->withinDeliveryRange($lat, $lng, $settingsAll)) {
            $this->fail('Sorry, your location is outside our delivery area.', 422);
        }

        // Guest fields
        $guestData = [];
        if (!$customerId) {
            $guestName  = trim((string)$this->post('guest_name'));
            $guestPhone = trim((string)$this->post('guest_phone'));
            if (!$guestName || !$guestPhone) $this->fail('Name and phone are required for guest orders.');
            $guestData = [
                'name'  => $guestName,
                'phone' => $guestPhone,
                'email' => trim((string)$this->post('guest_email')),
            ];
        }

        $settings    = $this->shop->settings();
        $voucherCode = strtoupper(trim((string)$this->post('voucher_code')));
        $t = $this->computeTotals($cartItems, $orderType, $lat, $lng, $voucherCode, $customerId, $settings);
        $subtotal    = $t['subtotal'];
        $deliveryFee = $t['delivery_fee'];
        $taxAmount   = $t['tax'];
        $discount    = $t['discount_total'];
        $voucherId   = $t['voucher_id'];
        $total       = $t['total'];

        $orderId = $this->shop->placeOrder($customerId, $guestData, [
            'order_type'     => $orderType,
            'address'        => $address,
            'lat'            => $lat,
            'lng'            => $lng,
            'payment_method' => $payment,
            'notes'          => $notes,
            'subtotal'       => $subtotal,
            'discount'       => $discount,
            'delivery_fee'   => $deliveryFee,
            'tax'            => $taxAmount,
            'total'          => $total,
            'voucher_id'     => $voucherId,
        ], $cartItems);

        try { \App\Helpers\ActivityLogger::log('place_order', 'Order', $orderId, 'ORD', 'API order'); } catch (\Throwable) {}
        $this->ok(['order_id' => $orderId, 'total' => $total], 'Order placed successfully.');
    }

    // GET /api/v1/orders   (my orders — requires auth)
    public function myOrders(): void
    {
        $user = $this->requireAuth();
        $cid  = $this->customerId($user);
        $this->ok($this->shop->myOrders($cid, (string)($user['phone'] ?? '')));
    }

    // GET /api/v1/orders/{id}
    public function orderDetail(array $params = []): void
    {
        $id   = (int)($params['id'] ?? 0);
        $user = $this->authUser();

        if ($user) {
            $order = $this->shop->orderDetail($id, $this->customerId($user), (string)($user['phone'] ?? ''));
        } else {
            // Guest: must pass guest_token header = order id (trusting app session)
            $order = $this->shop->orderDetail($id);
        }
        if (!$order) $this->fail('Order not found.', 404);
        $order['map_url'] = (!empty($order['delivery_lat']) && !empty($order['delivery_lng']))
            ? 'https://www.google.com/maps?q=' . $order['delivery_lat'] . ',' . $order['delivery_lng']
            : null;

        // ── Rider live location + ETA (admin-controlled, only while en route) ──
        $settings   = $this->shop->settings();
        $shareRider = ($settings['show_rider_location'] ?? '0') === '1';
        $enRoute    = in_array($order['status'], ['picked_up', 'on_the_way'], true);

        $rlat = $order['rider_lat'] ?? null;   // raw values from the model query
        $rlng = $order['rider_lng'] ?? null;

        $order['show_rider_location'] = $shareRider;
        $order['rider_lat']   = null;
        $order['rider_lng']   = null;
        $order['eta_minutes'] = null;

        if ($shareRider && $enRoute && $rlat !== null && $rlng !== null) {
            $order['rider_lat'] = (float)$rlat;
            $order['rider_lng'] = (float)$rlng;
            if (!empty($order['delivery_lat']) && !empty($order['delivery_lng'])) {
                $km = $this->haversineKm((float)$rlat, (float)$rlng,
                    (float)$order['delivery_lat'], (float)$order['delivery_lng']);
                $order['distance_km']  = round($km, 2);
                $order['eta_minutes']  = max(1, (int)ceil($km / 22 * 60)); // ~22 km/h avg
            }
        } else {
            // Don't leak rider coordinates when not sharing / not en route.
            $order['rider_location_at'] = null;
        }

        $this->ok($order);
    }

    private function haversineKm(float $lat1, float $lng1, float $lat2, float $lng2): float
    {
        $r = 6371.0;
        $dLat = deg2rad($lat2 - $lat1);
        $dLng = deg2rad($lng2 - $lng1);
        $a = sin($dLat / 2) ** 2 +
             cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin($dLng / 2) ** 2;
        return $r * 2 * atan2(sqrt($a), sqrt(1 - $a));
    }

    // GET /api/v1/orders/{id}/status
    public function orderStatus(array $params = []): void
    {
        $id    = (int)($params['id'] ?? 0);
        $order = $this->db->fetchOne(
            "SELECT status, rider_id FROM orders WHERE id = ?", [$id]
        );
        if (!$order) $this->fail('Not found.', 404);
        $rider = null;
        if ($order['rider_id']) {
            $rider = $this->db->fetchOne(
                "SELECT name, phone FROM users WHERE id = ?", [$order['rider_id']]
            );
        }
        $this->ok(['status' => $order['status'], 'rider' => $rider]);
    }
}
