<?php

declare(strict_types=1);
namespace Controllers;

use Config\Database;
use Helpers\Auth;
use Helpers\Request;
use Helpers\Response;
use Repositories\CartRepository;
use Repositories\AddressRepository;

final class CheckoutController
{
    /**
     * Pan-India fallback tiers, keyed by PIN prefix length 2.
     * Explicit rows in delivery_zones always take priority over this table.
     * Origin: Nashik, Maharashtra.
     */
    private static function fallbackZone(string $pin): array
    {
        $p2 = substr($pin, 0, 2);
        $p1 = substr($pin, 0, 1);

        // Tier 4: Remote / Northeast / J&K / Ladakh / Andaman & Nicobar
        $remotePrefixes2 = ['78', '79', '17', '18', '19'];
        if (in_array($p2, $remotePrefixes2, true) || $pin[0] . $pin[1] . $pin[2] === '744') {
            return ['tier' => 4, 'city' => 'Remote/NE Region', 'state' => 'Remote Zone', 'delivery_charge' => 150, 'delivery_estimate_days' => 8];
        }

        // Tier 1: Maharashtra (local, origin state)
        if ($p1 === '4' && $pin >= '400000' && $pin <= '445999') {
            return ['tier' => 1, 'city' => 'Maharashtra', 'state' => 'Maharashtra', 'delivery_charge' => 40, 'delivery_estimate_days' => 2];
        }

        // Tier 2: Nearby states - Gujarat, MP, Goa, Karnataka, Telangana, Andhra Pradesh
        $nearbyPrefixes2 = ['36', '37', '38', '39', '45', '46', '47', '48', '40', '56', '57', '58', '59', '50', '51', '52', '53'];
        if (in_array($p2, $nearbyPrefixes2, true)) {
            return ['tier' => 2, 'city' => 'Nearby State', 'state' => 'Nearby Zone', 'delivery_charge' => 70, 'delivery_estimate_days' => 4];
        }

        // Tier 3: Rest of India (everything else)
        return ['tier' => 3, 'city' => 'India', 'state' => 'Rest of India', 'delivery_charge' => 100, 'delivery_estimate_days' => 6];
    }

    /**
     * Looks up an explicit delivery_zones row first; falls back to pan-India tier if absent.
     * Returns null only if explicitly blocked (is_serviceable = 0) by an admin.
     */
    private static function resolveZone(\PDO $db, string $pin): ?array
    {
        $stmt = $db->prepare('SELECT pincode, city, state, country, is_serviceable, delivery_charge, delivery_estimate_days FROM delivery_zones WHERE pincode=:p LIMIT 1');
        $stmt->execute(['p' => $pin]);
        $row = $stmt->fetch();

        if ($row) {
            if (!(int)$row['is_serviceable']) return null; // explicitly blocked by admin
            return $row;
        }

        if (!preg_match('/^\d{6}$/', $pin)) return null; // invalid format, never fallback

        $fallback = self::fallbackZone($pin);
        return [
            'pincode' => $pin,
            'city' => $fallback['city'],
            'state' => $fallback['state'],
            'country' => 'India',
            'is_serviceable' => 1,
            'delivery_charge' => $fallback['delivery_charge'],
            'delivery_estimate_days' => $fallback['delivery_estimate_days'],
        ];
    }

    public function validate(): never
    {
        try {
            $u = Auth::requireUser();
            $d = Request::input();
            $addressId = (int)($d['address_id'] ?? 0);
            $addr = (new AddressRepository())->find((int)$u['id'], $addressId);
            if (!$addr) Response::error('Select a valid address', 422);

            $items = (new CartRepository())->get((int)$u['id'], '');
            if (!$items) Response::error('Cart is empty', 422);

            $db = Database::connection();
            $zone = self::resolveZone($db, (string)$addr['pincode']);
            if (!$zone) Response::error('Delivery is not serviceable for selected PIN code', 422);

            $subtotal = 0;
            foreach ($items as $i) {
                if ((int)$i['stock_quantity'] < (int)$i['quantity']) Response::error($i['name'] . ' is out of stock', 422);
                $subtotal += (float)$i['price'] * (int)$i['quantity'];
            }

            $delivery = (float)$zone['delivery_charge'];
            $tax = 0;
            $discount = 0;

            Response::success([
                'items' => $items,
                'address' => $addr,
                'subtotal' => $subtotal,
                'discount' => $discount,
                'delivery_charge' => $delivery,
                'tax' => $tax,
                'grand_total' => $subtotal + $delivery + $tax - $discount,
                'delivery_estimate_days' => (int)$zone['delivery_estimate_days'],
                'estimated_delivery_date' => date('Y-m-d', strtotime('+' . (int)$zone['delivery_estimate_days'] . ' days')),
            ]);
        } catch (\Throwable $e) {
            Response::error($e->getMessage(), $e->getMessage() === 'Authentication required' ? 401 : 400);
        }
    }

    public function pincode(string $pin): never
    {
        $db = Database::connection();
        $zone = self::resolveZone($db, $pin);
        if (!$zone) Response::error('Delivery is not serviceable for this PIN code', 404);
        Response::success($zone);
    }
}