<?php

declare(strict_types=1);

namespace App\Controllers;

use App\Helpers\ActivityLogger;
use App\Models\ShopModel;
use Core\Request;
use Core\Session;

final class ShopController extends BaseController
{
    private ShopModel $shop;

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

    // ── Shared data for shop layout ───────────────────────────────────────────

    private function shopData(array $extra = []): array
    {
        $this->tryAutoLogin();   // restore session from remember-me cookie on every shop page

        $cart      = Session::get('cart', []);
        $cartCount = array_sum(array_column($cart, 'qty'));
        $cartTotal = array_sum(array_map(fn($i) => $i['unit_price'] * $i['qty'], $cart));

        return array_merge([
            'shopSettings' => $this->shop->settings(),
            'cartCount'    => $cartCount,
            'cartTotal'    => $cartTotal,
            'layout'       => 'shop',
        ], $extra);
    }

    private function shopView(string $view, array $data = []): void
    {
        // Prevent browser from caching shop pages so cart/login state is always fresh
        header('Cache-Control: no-store, no-cache, must-revalidate');
        header('Pragma: no-cache');
        $this->view($view, $this->shopData($data));
    }

    // ── Item JSON for modal ───────────────────────────────────────────────────

    public function itemJson(array $params = []): void
    {
        $id   = (int)($params['id'] ?? 0);
        $item = $this->shop->findItem($id);
        if (!$item) { $this->json(['error' => 'not found'], 404); }
        $item['groups'] = $this->shop->itemChoiceGroups($id);
        $this->json($item);
    }

    // ── Auth guard for customers ──────────────────────────────────────────────

    // ── Remember-me cookie auto-login ────────────────────────────────────────

    private const REMEMBER_COOKIE  = 'shop_remember';
    private const REMEMBER_DAYS    = 30;

    private function tryAutoLogin(): void
    {
        if (Session::get('customer_id')) return;          // already logged in
        $token = $_COOKIE[self::REMEMBER_COOKIE] ?? null;
        if (!$token || strlen($token) !== 64) return;

        $user = $this->db->fetchOne(
            "SELECT u.*, c.id AS customer_id, c.blacklisted
               FROM users u
               JOIN customers c ON c.user_id = u.id
              WHERE u.remember_token = ?
                AND u.remember_token_expires > NOW()
                AND c.blacklisted = 0
                AND u.is_active = 1",
            [$token]
        );
        if (!$user) { $this->clearRememberCookie(); return; }

        // Refresh session
        Session::set('customer_id',      $user['customer_id']);
        Session::set('customer_name',    $user['name']);
        Session::set('customer_user_id', $user['id']);

        // Roll the token (prevents replay if cookie is stolen)
        $this->issueRememberCookie((int)$user['id']);
    }

    private function issueRememberCookie(int $userId): void
    {
        $token   = bin2hex(random_bytes(32));           // 64 hex chars
        $expires = date('Y-m-d H:i:s', strtotime('+' . self::REMEMBER_DAYS . ' days'));
        $this->db->query(
            "UPDATE users SET remember_token = ?, remember_token_expires = ? WHERE id = ?",
            [$token, $expires, $userId]
        );
        setcookie(self::REMEMBER_COOKIE, $token, [
            'expires'  => time() + self::REMEMBER_DAYS * 86400,
            'path'     => '/',
            'httponly' => true,
            'samesite' => 'Lax',
        ]);
    }

    private function clearRememberCookie(): void
    {
        setcookie(self::REMEMBER_COOKIE, '', ['expires' => time() - 3600, 'path' => '/']);
    }

    private function requireCustomer(): void
    {
        $this->tryAutoLogin();
        if (!Session::get('customer_id')) {
            Session::flash('error', 'Please login to continue.');
            $this->redirect('/shop/login');
        }
    }

    private function customerId(): int
    {
        return (int) Session::get('customer_id');
    }

    // ── Home ──────────────────────────────────────────────────────────────────

    public function home(): void
    {
        $categories = $this->shop->activeCategories();
        $deals      = $this->shop->activeDeals();
        $items      = $this->shop->allActiveItems();

        // Group items by category
        $byCategory = [];
        foreach ($items as $item) {
            $byCategory[(int)$item['category_id']][] = $item;
        }

        $this->shopView('shop/home', [
            'title'      => 'Order Online',
            'categories' => $categories,
            'deals'      => $deals,
            'byCategory' => $byCategory,
        ]);
    }

    // ── Menu (filter by category) ─────────────────────────────────────────────

    public function menu(): void
    {
        $catId      = Request::int('cat');
        $categories = $this->shop->activeCategories();

        // Always load ALL items — category filtering is done client-side
        // so search always works across every category
        $items = $this->shop->allActiveItems();

        $this->shopView('shop/menu', [
            'title'      => 'Menu',
            'categories' => $categories,
            'items'      => $items,
            'activeCat'  => $catId,
        ]);
    }

    // ── Item detail / add to cart modal ──────────────────────────────────────

    public function item(array $params = []): void
    {
        $id   = (int)($params['id'] ?? 0);
        $item = $this->shop->findItem($id);
        if (!$item) { $this->redirect('/shop'); }

        $groups = $this->shop->itemChoiceGroups($id);

        $this->shopView('shop/item', [
            'title'  => $item['name'],
            'item'   => $item,
            'groups' => $groups,
        ]);
    }

    // ── Cart: add (AJAX) ──────────────────────────────────────────────────────

    public function cartAdd(): void
    {
        Request::verifyCsrf();

        $itemId = Request::int('item_id');
        $qty    = max(1, Request::int('qty', 1));
        $item   = $this->shop->findItem($itemId);

        if (!$item) { $this->json(['success' => false, 'message' => 'Item not found.']); }

        // Build choices from POST
        $choicesRaw  = $_POST['choices'] ?? [];
        $choiceLabel = [];
        $choicePrice = 0;
        $choiceData  = [];

        if (!empty($choicesRaw)) {
            $groups = $this->shop->itemChoiceGroups($itemId);
            foreach ($groups as $g) {
                $chosen = $choicesRaw[$g['id']] ?? null;
                if ($chosen === null) continue;
                foreach ($g['choices'] as $c) {
                    if ((string)$c['id'] === (string)$chosen) {
                        $choiceLabel[]          = $g['name'] . ': ' . $c['label'];
                        $choicePrice           += (float)$c['price_modifier'];
                        $choiceData[$g['name']] = ['label' => $c['label'], 'price' => (float)$c['price_modifier']];
                        break;
                    }
                }
            }
        }

        $unitPrice = (float)$item['base_price'] + $choicePrice;
        $cartKey   = $itemId . '_' . md5(serialize($choicesRaw));

        $cart = Session::get('cart', []);
        if (isset($cart[$cartKey])) {
            $cart[$cartKey]['qty'] += $qty;
        } else {
            $cart[$cartKey] = [
                'item_id'      => $itemId,
                'name'         => $item['name'],
                'image'        => $item['item_image'] ?? null,
                'unit_price'   => $unitPrice,
                'qty'          => $qty,
                'choices'      => $choiceData,
                'choices_label'=> implode(', ', $choiceLabel),
            ];
        }
        Session::set('cart', $cart);

        $count = array_sum(array_column($cart, 'qty'));
        $total = array_sum(array_map(fn($i) => $i['unit_price'] * $i['qty'], $cart));

        $this->json(['success' => true, 'cart_count' => $count, 'cart_total' => $total]);
    }

    // ── Cart: update qty (AJAX) ───────────────────────────────────────────────

    public function cartUpdate(): void
    {
        Request::verifyCsrf();

        $key  = Request::str('key');
        $qty  = Request::int('qty');
        $cart = Session::get('cart', []);

        if ($qty <= 0) {
            unset($cart[$key]);
        } elseif (isset($cart[$key])) {
            $cart[$key]['qty'] = $qty;
        }
        Session::set('cart', $cart);

        $count = array_sum(array_column($cart, 'qty'));
        $total = array_sum(array_map(fn($i) => $i['unit_price'] * $i['qty'], $cart));
        $this->json(['success' => true, 'cart_count' => $count, 'cart_total' => $total]);
    }

    // ── Cart page ─────────────────────────────────────────────────────────────

    public function cart(): void
    {
        $cart    = Session::get('cart', []);
        $subtotal = array_sum(array_map(fn($i) => $i['unit_price'] * $i['qty'], $cart));

        $this->shopView('shop/cart', [
            'title'    => 'Cart',
            'cart'     => $cart,
            'subtotal' => $subtotal,
        ]);
    }

    // ── Voucher validate (AJAX) ───────────────────────────────────────────────

    public function validateVoucher(): void
    {
        $code  = strtoupper(trim(Request::str('code')));
        $total = (float) Request::str('total', '0');
        $this->json($this->shop->validateVoucher($code, $total));
    }

    // ── Delivery fee AJAX ─────────────────────────────────────────────────────

    public function calcDeliveryFee(): void
    {
        $cart      = Session::get('cart', []);
        $subtotal  = array_sum(array_map(fn($i) => $i['unit_price'] * $i['qty'], $cart));
        $orderType = Request::str('order_type', 'delivery');
        $lat       = Request::str('lat') !== '' ? (float) Request::str('lat') : null;
        $lng       = Request::str('lng') !== '' ? (float) Request::str('lng') : null;
        $settings  = $this->shop->settings();
        $fee       = $this->shop->calculateDeliveryFee($orderType, $subtotal, $lat, $lng, $settings);
        $taxRate   = (float)($settings['tax_rate'] ?? 0);
        $this->json(['fee' => $fee, 'fee_formatted' => 'Rs. ' . number_format($fee, 2), 'tax_rate' => $taxRate]);
    }

    // ── Checkout page ─────────────────────────────────────────────────────────

    public function checkout(): void
    {
        $cart = Session::get('cart', []);
        if (empty($cart)) { $this->redirect('/shop/cart'); }

        $subtotal = array_sum(array_map(fn($i) => $i['unit_price'] * $i['qty'], $cart));

        $settings    = $this->shop->settings();
        $deliveryFee = $this->shop->calculateDeliveryFee('delivery', $subtotal, null, null, $settings);
        $taxRate     = (float)($settings['tax_rate'] ?? 0);
        $taxAmount   = round($subtotal * $taxRate / 100, 2);
        $total       = $subtotal + $deliveryFee + $taxAmount;

        $chargeType  = $settings['delivery_charge_type'] ?? 'fixed';
        $perKm       = (float)($settings['delivery_per_km_rate'] ?? 0);
        $minFee      = (float)($settings['delivery_min_fee']     ?? 0);
        $freeAbove   = (float)($settings['delivery_free_above']  ?? 0);

        // ── Pre-fill from last order ──────────────────────────────────────────
        $prefill = [];
        $customerId = Session::get('customer_id');
        if ($customerId) {
            // Logged-in: get name/phone from users + last order details
            $user = $this->db->fetchOne(
                "SELECT u.name, u.phone
                   FROM customers c JOIN users u ON u.id = c.user_id
                  WHERE c.id = ?",
                [$customerId]
            );
            $lastOrder = $this->db->fetchOne(
                "SELECT delivery_address, delivery_lat, delivery_lng,
                        special_notes, payment_method, order_type
                   FROM orders
                  WHERE customer_id = ? AND delivery_address IS NOT NULL
                  ORDER BY placed_at DESC LIMIT 1",
                [$customerId]
            );
            $prefill = [
                'name'            => $user['name']             ?? '',
                'phone'           => $user['phone']            ?? '',
                'address'         => $lastOrder['delivery_address'] ?? '',
                'lat'             => $lastOrder['delivery_lat']     ?? '',
                'lng'             => $lastOrder['delivery_lng']     ?? '',
                'notes'           => $lastOrder['special_notes']    ?? '',
                'payment_method'  => $lastOrder['payment_method']   ?? 'cash',
                'order_type'      => $lastOrder['order_type']       ?? 'delivery',
            ];
        }

        $this->shopView('shop/checkout', [
            'title'         => 'Checkout',
            'cart'          => $cart,
            'subtotal'      => $subtotal,
            'deliveryFee'   => $deliveryFee,
            'taxAmount'     => $taxAmount,
            'total'         => $total,
            'isGuest'       => !$customerId,
            'chargeType'    => $chargeType,
            'perKmRate'     => $perKm,
            'minFee'        => $minFee,
            'freeAbove'     => $freeAbove,
            'taxRate'       => $taxRate,
            'prefill'       => $prefill,
        ]);
    }

    // ── Place order ───────────────────────────────────────────────────────────

    public function placeOrder(): void
    {
        Request::verifyCsrf();

        $cart = Session::get('cart', []);
        if (empty($cart)) { $this->redirect('/shop/cart'); }

        $customerId = Session::get('customer_id') ? $this->customerId() : null;

        // Block blacklisted customers
        if ($customerId) {
            $blacklisted = \Core\Database::getInstance()->fetchValue(
                "SELECT blacklisted FROM customers WHERE id = ?", [$customerId]
            );
            if ($blacklisted) {
                Session::flash('error', 'Your account has been suspended. You cannot place orders.');
                $this->redirect('/shop/cart');
            }
        }

        // Guest validation
        $guestData = [];
        if (!$customerId) {
            $guestName  = trim(Request::str('guest_name'));
            $guestPhone = trim(Request::str('guest_phone'));
            if (!$guestName || !$guestPhone) {
                Session::flash('error', 'Please enter your name and phone number.');
                $this->redirect('/shop/checkout');
            }
            $guestData = [
                'name'  => $guestName,
                'phone' => $guestPhone,
                'email' => trim(Request::str('guest_email')),
            ];
        }

        $orderType = Request::str('order_type', 'delivery');
        $lat       = Request::str('delivery_lat') !== '' ? (float) Request::str('delivery_lat') : null;
        $lng       = Request::str('delivery_lng') !== '' ? (float) Request::str('delivery_lng') : null;

        $settings    = $this->shop->settings();
        $subtotal    = array_sum(array_map(fn($i) => $i['unit_price'] * $i['qty'], $cart));
        $deliveryFee = $this->shop->calculateDeliveryFee($orderType, $subtotal, $lat, $lng, $settings);
        $taxRate     = (float)($settings['tax_rate'] ?? 0);
        $taxAmount   = round($subtotal * $taxRate / 100, 2);
        $discount    = 0;
        $voucherId   = null;

        // Voucher
        $voucherCode = strtoupper(trim(Request::str('voucher_code')));
        if ($voucherCode) {
            $result = $this->shop->validateVoucher($voucherCode, $subtotal);
            if ($result['valid']) {
                $discount  = $result['discount'];
                $voucherId = $result['voucher']['id'];
            }
        }

        $total = max(0, $subtotal + $deliveryFee + $taxAmount - $discount);

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

        Session::set('cart', []);
        ActivityLogger::log('place_order', 'Order', $orderId, 'ORD', 'Customer placed order');

        // Track guest orders in session for tracking access
        if (!$customerId) {
            $guestOrders = Session::get('guest_orders', []);
            $guestOrders[] = $orderId;
            Session::set('guest_orders', $guestOrders);
        }

        $this->redirect('/shop/orders/' . $orderId);
    }

    // ── My orders ─────────────────────────────────────────────────────────────

    public function myOrders(): void
    {
        if (!Session::get('customer_id')) {
            // Guest: redirect to last placed order or home
            $guestOrders = Session::get('guest_orders', []);
            if ($guestOrders) {
                $this->redirect('/shop/orders/' . end($guestOrders));
            }
            $this->redirect('/shop/login');
        }
        $orders = $this->shop->myOrders($this->customerId());
        $this->shopView('shop/orders', ['title' => 'My Orders', 'orders' => $orders]);
    }

    // ── Order detail + tracking ───────────────────────────────────────────────

    public function orderDetail(array $params = []): void
    {
        $id = (int)($params['id'] ?? 0);

        if (Session::get('customer_id')) {
            $order = $this->shop->orderDetail($id, $this->customerId());
        } else {
            $guestOrders = Session::get('guest_orders', []);
            if (!in_array($id, $guestOrders, true)) { $this->redirect('/shop'); }
            $order = $this->shop->orderDetail($id);
        }

        if (!$order) { $this->redirect('/shop/orders'); }
        $this->shopView('shop/order_detail', ['title' => 'Order #' . $order['order_number'], 'order' => $order]);
    }

    // ── Order tracking AJAX (poll) ────────────────────────────────────────────

    public function orderStatus(array $params = []): void
    {
        $id = (int)($params['id'] ?? 0);

        if (Session::get('customer_id')) {
            $order = $this->shop->orderDetail($id, $this->customerId());
        } else {
            $guestOrders = Session::get('guest_orders', []);
            if (!in_array($id, $guestOrders, true)) { $this->json(['error' => 'forbidden'], 403); }
            $order = $this->shop->orderDetail($id);
        }

        if (!$order) { $this->json(['error' => 'not found']); }
        $this->json(['status' => $order['status'], 'rider_name' => $order['rider_name'] ?? null, 'rider_phone' => $order['rider_phone'] ?? null]);
    }

    // ── Account ───────────────────────────────────────────────────────────────

    public function account(): void
    {
        $this->requireCustomer();
        $db  = \Core\Database::getInstance();
        $user = $db->fetchOne(
            "SELECT * FROM users WHERE id = (SELECT user_id FROM customers WHERE id = ?)",
            [$this->customerId()]
        );
        $stats = $db->fetchOne(
            "SELECT
                COUNT(*) AS total_orders,
                COALESCE(SUM(total_amount), 0) AS total_spent,
                SUM(status = 'delivered') AS delivered_orders
             FROM orders WHERE customer_id = ?",
            [$this->customerId()]
        );
        $this->shopView('shop/account', [
            'title'  => 'My Account',
            'user'   => $user,
            'stats'  => $stats,
            'tier'   => customer_tier((int)($stats['delivered_orders'] ?? 0)),
        ]);
    }

    // ── Auth: login ───────────────────────────────────────────────────────────

    public function loginForm(): void
    {
        if (Session::get('customer_id')) { $this->redirect('/shop'); }
        $this->shopView('shop/login', ['title' => 'Login']);
    }

    public function login(): void
    {
        Request::verifyCsrf();
        $email    = trim(Request::str('email'));
        $password = Request::str('password');

        $user = \Core\Database::getInstance()->fetchOne(
            "SELECT u.*, c.id AS customer_id, c.blacklisted
               FROM users u
               JOIN customers c ON c.user_id = u.id
               JOIN roles r     ON r.id = u.role_id
              WHERE u.email = ? AND r.name = 'customer'",
            [$email]
        );

        if (!$user || !\password_verify($password, $user['password_hash'])) {
            Session::flash('error', 'Invalid email or password.');
            $this->redirect('/shop/login');
        }

        if ($user['blacklisted']) {
            Session::flash('error', 'Your account has been suspended. Please contact support.');
            $this->redirect('/shop/login');
        }

        if (!$user['is_active']) {
            Session::flash('error', 'Your account is inactive. Please contact support.');
            $this->redirect('/shop/login');
        }

        Session::set('customer_id',      $user['customer_id']);
        Session::set('customer_name',    $user['name']);
        Session::set('customer_user_id', $user['id']);

        if (Request::str('remember') === '1') {
            $this->issueRememberCookie((int)$user['id']);
        }

        ActivityLogger::log('login', 'Customer', $user['id'], $user['name'], 'Customer login');
        $redirect = Session::getFlash('login_redirect', '/shop');
        $this->redirect($redirect);
    }

    // ── Auth: register ────────────────────────────────────────────────────────

    public function registerForm(): void
    {
        if (Session::get('customer_id')) { $this->redirect('/shop'); }
        $this->shopView('shop/register', ['title' => 'Create Account']);
    }

    public function register(): void
    {
        Request::verifyCsrf();
        $name     = trim(Request::str('name'));
        $email    = trim(Request::str('email'));
        $phone    = trim(Request::str('phone'));
        $password = Request::str('password');
        $confirm  = Request::str('confirm_password');

        if (strlen($name) < 2 || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
            Session::flash('error', 'Please fill all fields correctly.');
            $this->redirect('/shop/register');
        }
        if (strlen($password) < 6) {
            Session::flash('error', 'Password must be at least 6 characters.');
            $this->redirect('/shop/register');
        }
        if ($password !== $confirm) {
            Session::flash('error', 'Passwords do not match.');
            $this->redirect('/shop/register');
        }

        $db = \Core\Database::getInstance();
        if ($db->fetchValue("SELECT id FROM users WHERE email = ?", [$email])) {
            Session::flash('error', 'Email already registered.');
            $this->redirect('/shop/register');
        }

        $roleId = (int) $db->fetchValue("SELECT id FROM roles WHERE name = 'customer'");
        $hash   = \password_hash($password, PASSWORD_BCRYPT, ['cost' => BCRYPT_COST]);

        $userId = $db->insert('users', [
            'name'          => $name,
            'email'         => $email,
            'phone'         => $phone,
            'password_hash' => $hash,
            'role_id'       => $roleId,
            'is_active'     => 1,
        ]);
        $customerId = $db->insert('customers', ['user_id' => $userId]);

        Session::set('customer_id', $customerId);
        Session::set('customer_name', $name);
        Session::set('customer_user_id', $userId);
        ActivityLogger::log('register', 'Customer', $userId, $name, 'New customer registered');
        $this->redirect('/shop');
    }

    // ── Auth: logout ──────────────────────────────────────────────────────────

    public function logout(): void
    {
        // Revoke the remember-me token so the cookie can't auto-login again
        $uid = Session::get('customer_user_id');
        if ($uid) {
            $this->db->query(
                "UPDATE users SET remember_token = NULL, remember_token_expires = NULL WHERE id = ?",
                [$uid]
            );
        }
        $this->clearRememberCookie();

        Session::remove('customer_id');
        Session::remove('customer_name');
        Session::remove('customer_user_id');
        Session::remove('cart');
        $this->redirect('/shop/login');
    }
}
