<?php
// app/Helpers/FeatureGate.php
// Central helper for package feature limits, subscription expiry, and currency

class FeatureGate {

    // ── Cache to avoid repeated DB queries within same request ──
    private static $cache = [];

    /**
     * Get the full feature gate info for a restaurant.
     * Returns: [
     *   'expired'          => bool,
     *   'package_name'     => string|null,
     *   'days_left'        => int|null,
     *   'expiry_date'      => string|null,
     *   'features'         => [ max_floors, max_tables, max_cashiers, max_waiters, qr_ordering, kitchen_display, is_trial ],
     *   'current_floors'   => int,
     *   'current_tables'   => int,
     *   'current_cashiers' => int,
     *   'current_waiters'  => int,
     *   'currency'         => string,
     * ]
     */
    public static function check($restaurantId, $cashierId = null) {
        $cacheKey = $restaurantId . '_' . ($cashierId !== null ? $cashierId : 'global');
        if (isset(self::$cache[$cacheKey])) {
            return self::$cache[$cacheKey];
        }

        $db = getDB();

        // Get restaurant info
        $r = $db->prepare('SELECT currency, package_id, subscription_ends_at, extra_features FROM restaurants WHERE id = ?');
        $r->execute([$restaurantId]);
        $rest = $r->fetch();

        // Default features (no package = most restrictive)
        $defaults = [
            'max_floors'       => 1,
            'max_tables'       => 5,
            'max_cashiers'     => 1,
            'max_waiters'      => 2,
            'max_others'       => 5,
            'qr_ordering'      => false,
            'kitchen_display'  => false,
            'is_trial'         => false,
            'is_lifetime'      => false,
            'inventory'        => false,
            'staff_management' => false,
        ];

        $result = [
            'expired'          => true,
            'package_name'     => null,
            'days_left'        => null,
            'expiry_date'      => null,
            'expiry_timestamp' => $rest['subscription_ends_at'] ?? null,
            'features'         => $defaults,
            'currency'         => $rest['currency'] ?? '$',
        ];

        // Get active subscription
        $sub = $db->prepare('
            SELECT s.*, p.name AS package_name, p.features AS package_features
            FROM subscriptions s
            JOIN packages p ON p.id = s.package_id
            WHERE s.restaurant_id = ? AND s.status = "active"
            ORDER BY s.end_date DESC
            LIMIT 1
        ');
        $sub->execute([$restaurantId]);
        $subscription = $sub->fetch();

        if ($subscription) {
            $expiry   = strtotime($subscription['end_date']);
            $today    = strtotime(date('Y-m-d'));
            $daysLeft = (int)(($expiry - $today) / 86400);
            $expired  = $daysLeft < 0;

            $features = json_decode($subscription['package_features'], true);
            if (!$features) $features = $defaults;



            $isLifetime = !empty($features['is_lifetime']);
            if ($isLifetime) {
                $result['expired']      = false;
                $result['package_name'] = $subscription['package_name'];
                $result['days_left']    = 99999;
                $result['expiry_date']  = 'Lifetime';
            } else {
                $result['expired']      = $expired;
                $result['package_name'] = $subscription['package_name'];
                $result['days_left']    = $daysLeft;
                $result['expiry_date']  = $subscription['end_date'];
            }
            $result['features']     = array_merge($defaults, $features);
        }

        // Apply restaurant-specific overrides (extra_features)
        $extraFeatures = json_decode($rest['extra_features'] ?? '{}', true) ?: [];
        $result['features'] = array_merge($result['features'], $extraFeatures);
        
        // If restaurant override has is_lifetime set, override expiry
        if (!empty($extraFeatures['is_lifetime'])) {
            $result['expired']     = false;
            $result['days_left']   = 99999;
            $result['expiry_date'] = 'Lifetime';
        }

        // Get current counts
        if ($cashierId !== null) {
            $stmt = $db->prepare('SELECT COUNT(*) FROM floors WHERE restaurant_id = ? AND cashier_id = ?');
            $stmt->execute([$restaurantId, $cashierId]);
            $result['current_floors'] = (int)$stmt->fetchColumn();

            $stmt = $db->prepare('SELECT COUNT(*) FROM tables t JOIN floors f ON t.floor_id = f.id WHERE f.restaurant_id = ? AND f.cashier_id = ?');
            $stmt->execute([$restaurantId, $cashierId]);
            $result['current_tables'] = (int)$stmt->fetchColumn();
        } else {
            $stmt = $db->prepare('SELECT COUNT(*) FROM floors WHERE restaurant_id = ?');
            $stmt->execute([$restaurantId]);
            $result['current_floors'] = (int)$stmt->fetchColumn();

            $stmt = $db->prepare('SELECT COUNT(*) FROM tables WHERE restaurant_id = ?');
            $stmt->execute([$restaurantId]);
            $result['current_tables'] = (int)$stmt->fetchColumn();
        }

        $stmt = $db->prepare('SELECT COUNT(*) FROM users WHERE restaurant_id = ? AND role = "cashier"');
        $stmt->execute([$restaurantId]);
        $result['current_cashiers'] = (int)$stmt->fetchColumn();

        $stmt = $db->prepare('SELECT COUNT(*) FROM users WHERE restaurant_id = ? AND role = "waiter" AND email NOT LIKE "%@restpo.local"');
        $stmt->execute([$restaurantId]);
        $result['current_waiters'] = (int)$stmt->fetchColumn();

        $stmt = $db->prepare('SELECT COUNT(*) FROM users WHERE restaurant_id = ? AND role = "waiter" AND email LIKE "%@restpo.local"');
        $stmt->execute([$restaurantId]);
        $result['current_others'] = (int)$stmt->fetchColumn();

        self::$cache[$cacheKey] = $result;
        return $result;
    }

    /**
     * Check if subscription is expired
     */
    public static function isExpired($restaurantId) {
        $gate = self::check($restaurantId);
        return $gate['expired'];
    }

    /**
     * Get the restaurant currency symbol
     */
    public static function currency($restaurantId) {
        $db = getDB();
        $stmt = $db->prepare('SELECT currency FROM restaurants WHERE id = ?');
        $stmt->execute([$restaurantId]);
        return $stmt->fetchColumn() ?: '$';
    }

    /**
     * Check if can add more floors
     */
    public static function canAddFloor($restaurantId, $cashierId = null) {
        $gate = self::check($restaurantId, $cashierId);
        $max  = $gate['features']['max_floors'];
        if ($max === null) return ['allowed' => true, 'remaining' => '∞', 'current' => $gate['current_floors']];
        $remaining = $max - $gate['current_floors'];
        return ['allowed' => $remaining > 0, 'remaining' => max(0, $remaining), 'current' => $gate['current_floors'], 'max' => $max];
    }

    /**
     * Check if can add more tables
     */
    public static function canAddTable($restaurantId, $count = 1, $cashierId = null) {
        $gate = self::check($restaurantId, $cashierId);
        $max  = $gate['features']['max_tables'];
        if ($max === null) return ['allowed' => true, 'remaining' => '∞', 'current' => $gate['current_tables']];
        $remaining = $max - $gate['current_tables'];
        return ['allowed' => $remaining >= $count, 'remaining' => max(0, $remaining), 'current' => $gate['current_tables'], 'max' => $max];
    }

    /**
     * Check if can add more cashiers
     */
    public static function canAddCashier($restaurantId) {
        $gate = self::check($restaurantId);
        $max  = $gate['features']['max_cashiers'];
        if ($max === null) return ['allowed' => true, 'remaining' => '∞', 'current' => $gate['current_cashiers']];
        $remaining = $max - $gate['current_cashiers'];
        return ['allowed' => $remaining > 0, 'remaining' => max(0, $remaining), 'current' => $gate['current_cashiers'], 'max' => $max];
    }

    /**
     * Check if can add more waiters
     */
    public static function canAddWaiter($restaurantId) {
        $gate = self::check($restaurantId);
        $max  = $gate['features']['max_waiters'];
        if ($max === null) return ['allowed' => true, 'remaining' => '∞', 'current' => $gate['current_waiters']];
        $remaining = $max - $gate['current_waiters'];
        return ['allowed' => $remaining > 0, 'remaining' => max(0, $remaining), 'current' => $gate['current_waiters'], 'max' => $max];
    }

    /**
     * Check if can add more other staff
     */
    public static function canAddOther($restaurantId) {
        $gate = self::check($restaurantId);
        $max  = $gate['features']['max_others'];
        if ($max === null) return ['allowed' => true, 'remaining' => '∞', 'current' => $gate['current_others'] ?? 0];
        $remaining = $max - ($gate['current_others'] ?? 0);
        return ['allowed' => $remaining > 0, 'remaining' => max(0, $remaining), 'current' => $gate['current_others'] ?? 0, 'max' => $max];
    }

    /**
     * Check if QR ordering is available
     */
    public static function hasQrOrdering($restaurantId) {
        $gate = self::check($restaurantId);
        return !$gate['expired'] && ($gate['features']['qr_ordering'] ?? false);
    }

    /**
     * Check if Kitchen Display is available
     */
    public static function hasKitchen($restaurantId) {
        $gate = self::check($restaurantId);
        return !$gate['expired'] && ($gate['features']['kitchen_display'] ?? false);
    }

    /**
     * Check if Inventory is enabled
     */
    public static function hasInventory($restaurantId) {
        $gate = self::check($restaurantId);
        return !$gate['expired'] && !empty($gate['features']['inventory']);
    }

    /**
     * Check if Staff Management is enabled
     */
    public static function hasStaffManagement($restaurantId) {
        $gate = self::check($restaurantId);
        return !$gate['expired'] && !empty($gate['features']['staff_management']);
    }

    /**
     * Enforce Inventory module access
     */
    public static function enforceInventory($restaurantId) {
        if (isset($_SESSION['role']) && $_SESSION['role'] === 'superadmin') {
            return;
        }
        self::enforceSubscription($restaurantId);
        if (!self::hasInventory($restaurantId)) {
            if (isset($_SESSION['role']) && $_SESSION['role'] === 'admin') {
                header('Location: /admin/dashboard?error=' . urlencode('Inventory module is not included in your subscription package.'));
            } else {
                unset($_SESSION['user_id']);
                unset($_SESSION['role']);
                $_SESSION['error'] = 'Inventory module is not included in your subscription package.';
                header('Location: /login');
            }
            exit;
        }
    }

    /**
     * Enforce Staff Management module access
     */
    public static function enforceStaffManagement($restaurantId) {
        if (isset($_SESSION['role']) && $_SESSION['role'] === 'superadmin') {
            return;
        }
        self::enforceSubscription($restaurantId);
        if (!self::hasStaffManagement($restaurantId)) {
            if (isset($_SESSION['role']) && $_SESSION['role'] === 'admin') {
                header('Location: /admin/dashboard?error=' . urlencode('Staff Management module is not included in your subscription package.'));
            } else {
                unset($_SESSION['user_id']);
                unset($_SESSION['role']);
                $_SESSION['error'] = 'Staff Management module is not included in your subscription package.';
                header('Location: /login');
            }
            exit;
        }
    }

    /**
     * Enforce Kitchen Display module access
     */
    public static function enforceKitchen($restaurantId) {
        if (isset($_SESSION['role']) && $_SESSION['role'] === 'superadmin') {
            return;
        }
        self::enforceSubscription($restaurantId);
        if (!self::hasKitchen($restaurantId)) {
            if (isset($_SESSION['role']) && $_SESSION['role'] === 'admin') {
                header('Location: /admin/dashboard?error=' . urlencode('Kitchen Display module is not included in your subscription package.'));
            } else {
                unset($_SESSION['user_id']);
                unset($_SESSION['role']);
                $_SESSION['error'] = 'Kitchen Display module is not included in your subscription package.';
                header('Location: /login');
            }
            exit;
        }
    }

    /**
     * Enforce QR Ordering module access
     */
    public static function enforceQrOrdering($restaurantId) {
        if (isset($_SESSION['role']) && $_SESSION['role'] === 'superadmin') {
            return;
        }
        self::enforceSubscription($restaurantId);
        if (!self::hasQrOrdering($restaurantId)) {
            if (isset($_SESSION['role']) && $_SESSION['role'] === 'admin') {
                header('Location: /admin/dashboard?error=' . urlencode('QR Ordering module is not included in your subscription package.'));
            } else {
                unset($_SESSION['user_id']);
                unset($_SESSION['role']);
                $_SESSION['error'] = 'QR Ordering module is not included in your subscription package.';
                header('Location: /login');
            }
            exit;
        }
    }

    /**
     * Enforce subscription — redirect to expired page if expired.
     * Call at the top of protected controllers.
     */
    public static function enforceSubscription($restaurantId) {
        if (isset($_SESSION['role']) && $_SESSION['role'] === 'superadmin') {
            return;
        }
        if (self::isExpired($restaurantId)) {
            header('Location: /expired');
            exit;
        }
    }

    /**
     * Get the next sequential order number based on restaurant settings
     */
    public static function getNextOrderNumber($restaurantId, $sessionId = null, $cashierId = null) {
        $db = getDB();
        
        $flow = 'global';
        try {
            $stmt = $db->prepare('SELECT setting_value FROM restaurant_settings WHERE restaurant_id = ? AND setting_key = "order_id_flow"');
            $stmt->execute([$restaurantId]);
            $flow = $stmt->fetchColumn() ?: 'global';
        } catch (\Exception $e) {}

        if ($flow === 'session' && $sessionId) {
            $numStmt = $db->prepare('SELECT COALESCE(MAX(order_number), 0) + 1 FROM orders WHERE session_id = ? FOR UPDATE');
            $numStmt->execute([$sessionId]);
            return (int)$numStmt->fetchColumn();
        } elseif ($flow === 'cashier' && $cashierId) {
            $numStmt = $db->prepare('SELECT COALESCE(MAX(order_number), 0) + 1 FROM orders WHERE cashier_id = ? FOR UPDATE');
            $numStmt->execute([$cashierId]);
            return (int)$numStmt->fetchColumn();
        } else {
            $numStmt = $db->prepare('SELECT COALESCE(MAX(order_number), 0) + 1 FROM orders WHERE restaurant_id = ? FOR UPDATE');
            $numStmt->execute([$restaurantId]);
            return (int)$numStmt->fetchColumn();
        }
    }

    /**
     * Check if a staff user is allowed to login or operate under the active subscription limits.
     */
    public static function isUserAllowed($userId, $restaurantId, $role) {
        if ($role === 'superadmin' || $role === 'admin') {
            return true;
        }
        $gate = self::check($restaurantId);
        $db = getDB();

        if ($role === 'cashier') {
            $max = $gate['features']['max_cashiers'];
            if ($max === null) return true;
            $stmt = $db->prepare('SELECT id FROM users WHERE restaurant_id = ? AND role = "cashier" AND status = "active" ORDER BY id ASC');
            $stmt->execute([$restaurantId]);
            $cashiers = $stmt->fetchAll(PDO::FETCH_COLUMN);
            $index = array_search($userId, $cashiers);
            if ($index === false || $index >= $max) {
                return false;
            }
            return true;
        }

        if ($role === 'waiter') {
            $max = $gate['features']['max_waiters'];
            if ($max === null) return true;
            $stmt = $db->prepare('SELECT id FROM users WHERE restaurant_id = ? AND role = "waiter" AND email NOT LIKE "%@restpo.local" AND status = "active" ORDER BY id ASC');
            $stmt->execute([$restaurantId]);
            $waiters = $stmt->fetchAll(PDO::FETCH_COLUMN);
            $index = array_search($userId, $waiters);
            if ($index === false || $index >= $max) {
                return false;
            }
            return true;
        }

        if ($role === 'waiter_other') {
            $max = $gate['features']['max_others'];
            if ($max === null) return true;
            $stmt = $db->prepare('SELECT id FROM users WHERE restaurant_id = ? AND role = "waiter" AND email LIKE "%@restpo.local" AND status = "active" ORDER BY id ASC');
            $stmt->execute([$restaurantId]);
            $others = $stmt->fetchAll(PDO::FETCH_COLUMN);
            $index = array_search($userId, $others);
            if ($index === false || $index >= $max) {
                return false;
            }
            return true;
        }

        return true;
    }

    /**
     * Get the list of floor IDs allowed under subscription limits.
     */
    public static function getAllowedFloorIds($restaurantId) {
        $gate = self::check($restaurantId);
        $max = $gate['features']['max_floors'];
        $db = getDB();
        $stmt = $db->prepare('SELECT id FROM floors WHERE restaurant_id = ? ORDER BY id ASC');
        $stmt->execute([$restaurantId]);
        $allIds = $stmt->fetchAll(PDO::FETCH_COLUMN);
        if ($max === null) return $allIds;
        return array_slice($allIds, 0, $max);
    }

    /**
     * Get the list of table IDs allowed under subscription limits (from the allowed floors).
     */
    public static function getAllowedTableIds($restaurantId) {
        $gate = self::check($restaurantId);
        $max = $gate['features']['max_tables'];
        $db = getDB();
        $allowedFloors = self::getAllowedFloorIds($restaurantId);
        if (empty($allowedFloors)) return [];
        $placeholders = implode(',', array_fill(0, count($allowedFloors), '?'));
        $stmt = $db->prepare("SELECT id FROM tables WHERE floor_id IN ($placeholders) ORDER BY id ASC");
        $stmt->execute($allowedFloors);
        $allIds = $stmt->fetchAll(PDO::FETCH_COLUMN);
        if ($max === null) return $allIds;
        return array_slice($allIds, 0, $max);
    }
}
