<?php
/**
 * permission_check.php
 *
 * Save as:
 * /rooms/includes/permission_check.php
 *
 * Usage inside any module page:
 *
 * require_once __DIR__ . '/../../includes/permission_check.php';
 * require_permission($pdo, 'pos', 'view');
 *
 * Examples:
 * require_permission($pdo, 'pos', 'view');                 // Open POS page
 * require_permission($pdo, 'pos_void', 'approve');         // Void POS receipt
 * require_permission($pdo, 'stock_movements', 'create');   // Add stock movement
 * require_permission($pdo, 'role_permissions', 'view');    // Manage roles
 */

if (!function_exists('permission_table_exists')) {
    function permission_table_exists(PDO $pdo, string $tableName): bool
    {
        try {
            $stmt = $pdo->prepare("
                SELECT COUNT(*)
                FROM information_schema.tables
                WHERE table_schema = DATABASE()
                  AND table_name = :table_name
            ");
            $stmt->execute([':table_name' => $tableName]);
            return (int)$stmt->fetchColumn() > 0;
        } catch (Throwable $e) {
            return false;
        }
    }
}


if (!function_exists('permission_column_exists')) {
    function permission_column_exists(PDO $pdo, string $tableName, string $columnName): bool
    {
        try {
            $stmt = $pdo->prepare("
                SELECT COUNT(*)
                FROM information_schema.columns
                WHERE table_schema = DATABASE()
                  AND table_name = :table_name
                  AND column_name = :column_name
            ");
            $stmt->execute([
                ':table_name' => $tableName,
                ':column_name' => $columnName,
            ]);
            return (int)$stmt->fetchColumn() > 0;
        } catch (Throwable $e) {
            return false;
        }
    }
}

if (!function_exists('active_permission_role_key')) {
    function active_permission_role_key(PDO $pdo): string
    {
        /*
         * Resolve the current role safely.
         * IMPORTANT: Do not call active_permission_role_key() from inside itself.
         * That recursion was causing permission checks to fail and the sidebar to hide everything.
         */
        $role = function_exists('current_role_key') ? current_role_key() : 'user';
        $role = strtolower(trim((string)$role));

        /*
         * If the session still only has the old integer/user fallback,
         * recover the dynamic role_key directly from users.role_key.
         */
        $userId = function_exists('current_user_id_for_permissions') ? current_user_id_for_permissions() : null;
        if ($userId && in_array($role, ['user', '10', ''], true)) {
            try {
                if (permission_table_exists($pdo, 'users') && permission_column_exists($pdo, 'users', 'role_key')) {
                    $stmt = $pdo->prepare("
                        SELECT role_key
                        FROM users
                        WHERE id = :user_id
                        LIMIT 1
                    ");
                    $stmt->execute([':user_id' => $userId]);
                    $dbRole = strtolower(trim((string)$stmt->fetchColumn()));

                    if ($dbRole !== '') {
                        $role = $dbRole;
                    }
                }
            } catch (Throwable $e) {
                // Keep session role fallback.
            }
        }

        $map = [
            '1' => 'super_admin',
            '2' => 'admin',
            '3' => 'manager',
            '4' => 'frontdesk',
            '5' => 'cashier',
            '6' => 'stores',
            '7' => 'accounts',
            '8' => 'housekeeping',
            '9' => 'owner',
            '10' => 'user',
            'superadmin' => 'super_admin',
            'super administrator' => 'super_admin',
            'administrator' => 'admin',
            'front desk' => 'frontdesk',
            'front_desk' => 'frontdesk',
            'cashier_user' => 'cashier',
            'store' => 'stores',
            'stores_clerk' => 'stores',
            'accountant' => 'accounts',
            'accounting' => 'accounts',
        ];

        $role = $map[$role] ?? $role;
        $role = preg_replace('/[^a-z0-9_]/', '_', $role);
        $role = trim(preg_replace('/_+/', '_', $role), '_');

        return $role !== '' ? $role : 'user';
    }
}

if (!function_exists('current_role_key')) {
    function current_role_key(): string
    {
        /*
         * Prefer the new dynamic role key saved at login.
         * Do not let old integer fields override custom roles like barman.
         */
        $role = (string)(
            $_SESSION['role_key']
            ?? $_SESSION['user_role_key']
            ?? $_SESSION['ss_role_key']
            ?? $_SESSION['role']
            ?? ''
        );

        if (trim($role) === '' && function_exists('current_user_role')) {
            $role = (string)current_user_role();
        }

        if (trim($role) === '') {
            $role = (string)(
                $_SESSION['user_role']
                ?? $_SESSION['type']
                ?? $_SESSION['ss_role_id']
                ?? $_SESSION['Role']
                ?? 'user'
            );
        }

        $role = strtolower(trim($role));

        $map = [
            '1' => 'super_admin',
            '2' => 'admin',
            '3' => 'manager',
            '4' => 'frontdesk',
            '5' => 'cashier',
            '6' => 'stores',
            '7' => 'accounts',
            '8' => 'housekeeping',
            '9' => 'owner',
            '10' => 'user',

            'superadmin' => 'super_admin',
            'super administrator' => 'super_admin',
            'administrator' => 'admin',
            'front desk' => 'frontdesk',
            'front_desk' => 'frontdesk',
            'cashier_user' => 'cashier',
            'store' => 'stores',
            'stores_clerk' => 'stores',
            'accountant' => 'accounts',
            'accounting' => 'accounts',
        ];

        $role = $map[$role] ?? $role;
        $role = preg_replace('/[^a-z0-9_]/', '_', $role);
        $role = trim(preg_replace('/_+/', '_', $role), '_');

        return $role !== '' ? $role : 'user';
    }
}

if (!function_exists('current_user_id_for_permissions')) {
    function current_user_id_for_permissions(): ?int
    {
        $id = $_SESSION['user_id'] ?? $_SESSION['ss_user_id'] ?? $_SESSION['id'] ?? $_SESSION['UserID'] ?? null;
        $id = (int)$id;
        return $id > 0 ? $id : null;
    }
}

if (!function_exists('ensure_permission_tables')) {
    function ensure_permission_tables(PDO $pdo): void
    {
        $pdo->exec("
            CREATE TABLE IF NOT EXISTS roles (
                id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
                role_key VARCHAR(80) NOT NULL,
                role_name VARCHAR(120) NOT NULL,
                description TEXT NULL,
                is_system TINYINT(1) NOT NULL DEFAULT 0,
                is_active TINYINT(1) NOT NULL DEFAULT 1,
                created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
                updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
                UNIQUE KEY uq_role_key (role_key)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
        ");

        $pdo->exec("
            CREATE TABLE IF NOT EXISTS role_permissions (
                id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
                role_key VARCHAR(80) NOT NULL,
                permission_key VARCHAR(120) NOT NULL,
                can_view TINYINT(1) NOT NULL DEFAULT 0,
                can_create TINYINT(1) NOT NULL DEFAULT 0,
                can_edit TINYINT(1) NOT NULL DEFAULT 0,
                can_delete TINYINT(1) NOT NULL DEFAULT 0,
                can_approve TINYINT(1) NOT NULL DEFAULT 0,
                created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
                updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
                UNIQUE KEY uq_role_permission (role_key, permission_key),
                INDEX idx_role_key (role_key),
                INDEX idx_permission_key (permission_key)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
        ");

        $pdo->exec("
            CREATE TABLE IF NOT EXISTS permission_access_log (
                id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
                user_id INT UNSIGNED NULL,
                role_key VARCHAR(80) NULL,
                permission_key VARCHAR(120) NULL,
                action_key VARCHAR(40) NULL,
                access_result ENUM('allowed','denied') NOT NULL DEFAULT 'denied',
                request_uri TEXT NULL,
                ip_address VARCHAR(80) NULL,
                created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
                INDEX idx_permission_access_user (user_id),
                INDEX idx_permission_access_role (role_key),
                INDEX idx_permission_access_result (access_result),
                INDEX idx_permission_access_created (created_at)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
        ");
    }
}

if (!function_exists('seed_minimum_role_permissions')) {
    function seed_minimum_role_permissions(PDO $pdo): void
    {
        ensure_permission_tables($pdo);

        $roles = [
            ['super_admin', 'Super Admin', 'Full system access'],
            ['admin', 'Admin', 'System administrator'],
            ['owner', 'Owner', 'Executive reporting and approvals'],
            ['manager', 'Manager', 'Operational management'],
            ['frontdesk', 'Front Desk', 'Reservations and front desk'],
            ['cashier', 'Cashier', 'POS cashier'],
            ['stores', 'Stores Clerk', 'Inventory and stores'],
            ['accounts', 'Accounts', 'Finance and reports'],
            ['housekeeping', 'Housekeeping', 'Housekeeping board'],
            ['user', 'User', 'Basic user'],
        ];

        $stmt = $pdo->prepare("
            INSERT INTO roles (role_key, role_name, description, is_system, is_active)
            VALUES (:role_key, :role_name, :description, 1, 1)
            ON DUPLICATE KEY UPDATE
                role_name = VALUES(role_name),
                description = VALUES(description),
                is_active = 1
        ");

        foreach ($roles as $r) {
            $stmt->execute([
                ':role_key' => $r[0],
                ':role_name' => $r[1],
                ':description' => $r[2],
            ]);
        }
    }
}

if (!function_exists('permission_column_for_action')) {
    function permission_column_for_action(string $action): string
    {
        $action = strtolower(trim($action));

        $map = [
            'view' => 'can_view',
            'read' => 'can_view',
            'list' => 'can_view',
            'create' => 'can_create',
            'add' => 'can_create',
            'post' => 'can_create',
            'edit' => 'can_edit',
            'update' => 'can_edit',
            'save' => 'can_edit',
            'delete' => 'can_delete',
            'remove' => 'can_delete',
            'void' => 'can_delete',
            'approve' => 'can_approve',
            'authorize' => 'can_approve',
            'manager' => 'can_approve',
        ];

        return $map[$action] ?? 'can_view';
    }
}

if (!function_exists('is_permission_super_user')) {
    function is_permission_super_user(?string $role = null): bool
    {
        $role = $role !== null ? strtolower(trim($role)) : current_role_key();
        return in_array($role, ['super_admin', 'admin'], true);
    }
}

if (!function_exists('has_permission')) {
    function has_permission(PDO $pdo, string $permissionKey, string $action = 'view'): bool
    {
        $permissionKey = strtolower(trim($permissionKey));
        $action = strtolower(trim($action));
        $role = active_permission_role_key($pdo);

        if ($permissionKey === '') {
            return false;
        }

        /*
         * Super admin and admin always pass.
         */
        if (is_permission_super_user($role)) {
            return true;
        }

        try {
            ensure_permission_tables($pdo);

            $column = permission_column_for_action($action);

            $stmt = $pdo->prepare("
                SELECT {$column}
                FROM role_permissions
                WHERE role_key = :role_key
                  AND permission_key = :permission_key
                LIMIT 1
            ");
            $stmt->execute([
                ':role_key' => $role,
                ':permission_key' => $permissionKey,
            ]);

            return (int)$stmt->fetchColumn() === 1;
        } catch (Throwable $e) {
            /*
             * Safe fallback:
             * If permission tables fail, only admin-like roles pass.
             */
            return false;
        }
    }
}

if (!function_exists('log_permission_access')) {
    function log_permission_access(PDO $pdo, string $permissionKey, string $action, bool $allowed): void
    {
        try {
            ensure_permission_tables($pdo);

            $stmt = $pdo->prepare("
                INSERT INTO permission_access_log
                (user_id, role_key, permission_key, action_key, access_result, request_uri, ip_address)
                VALUES
                (:user_id, :role_key, :permission_key, :action_key, :access_result, :request_uri, :ip_address)
            ");
            $stmt->execute([
                ':user_id' => current_user_id_for_permissions(),
                ':role_key' => active_permission_role_key($pdo),
                ':permission_key' => $permissionKey,
                ':action_key' => $action,
                ':access_result' => $allowed ? 'allowed' : 'denied',
                ':request_uri' => $_SERVER['REQUEST_URI'] ?? null,
                ':ip_address' => $_SERVER['REMOTE_ADDR'] ?? null,
            ]);
        } catch (Throwable $e) {
            /*
             * Never block user flow because logging failed.
             */
        }
    }
}

if (!function_exists('require_permission')) {
    function require_permission(PDO $pdo, string $permissionKey, string $action = 'view', bool $logAccess = true): void
    {
        $allowed = has_permission($pdo, $permissionKey, $action);

        if ($logAccess) {
            log_permission_access($pdo, $permissionKey, $action, $allowed);
        }

        if ($allowed) {
            return;
        }

        http_response_code(403);

        $message = 'You do not have permission to access this page.';
        $detail = 'Required permission: ' . htmlspecialchars($permissionKey, ENT_QUOTES, 'UTF-8') .
            ' / ' . htmlspecialchars($action, ENT_QUOTES, 'UTF-8');

        /*
         * If this is an AJAX/API request, return JSON.
         */
        $isAjax = (
            strtolower($_SERVER['HTTP_X_REQUESTED_WITH'] ?? '') === 'xmlhttprequest'
            || str_contains(strtolower($_SERVER['HTTP_ACCEPT'] ?? ''), 'application/json')
        );

        if ($isAjax) {
            header('Content-Type: application/json; charset=utf-8');
            echo json_encode([
                'ok' => false,
                'error' => 'access_denied',
                'message' => $message,
                'detail' => $detail,
            ]);
            exit;
        }

        echo '<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Access Denied</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
        body{font-family:Arial,sans-serif;background:#f8fafc;margin:0;padding:40px;color:#0f172a}
        .box{max-width:720px;margin:0 auto;background:#fff;border:1px solid #e2e8f0;border-radius:18px;padding:28px;box-shadow:0 18px 40px rgba(15,23,42,.08)}
        h1{margin:0 0 10px;font-size:26px}
        p{color:#475569;line-height:1.6}
        code{background:#f1f5f9;padding:4px 8px;border-radius:8px}
        a{display:inline-block;margin-top:14px;color:#2563eb;text-decoration:none;font-weight:700}
    </style>
</head>
<body>
    <div class="box">
        <h1>Access denied</h1>
        <p>' . htmlspecialchars($message, ENT_QUOTES, 'UTF-8') . '</p>
        <p><code>' . $detail . '</code></p>
        <a href="javascript:history.back()">Go back</a>
    </div>
</body>
</html>';
        exit;
    }
}

if (!function_exists('can_view_menu')) {
    function can_view_menu(PDO $pdo, string $permissionKey): bool
    {
        return has_permission($pdo, $permissionKey, 'view');
    }
}

if (!function_exists('permission_badge')) {
    function permission_badge(PDO $pdo, string $permissionKey, string $action = 'view'): string
    {
        return has_permission($pdo, $permissionKey, $action) ? '1' : '0';
    }
}

if (!function_exists('hide_if_no_permission')) {
    function hide_if_no_permission(PDO $pdo, string $permissionKey, string $action = 'view'): string
    {
        return has_permission($pdo, $permissionKey, $action) ? '' : 'd-none';
    }
}

/*
 * Optional helper for buttons.
 *
 * Example:
 * <?php if (can_do($pdo, 'pos_void', 'approve')): ?>
 *     <button>Void Receipt</button>
 * <?php endif; ?>
 */
if (!function_exists('can_do')) {
    function can_do(PDO $pdo, string $permissionKey, string $action = 'view'): bool
    {
        return has_permission($pdo, $permissionKey, $action);
    }
}

/*
 * Optional one-time seeding.
 * This creates the base roles if they do not exist.
 */
try {
    $permissionPdoForSeed = null;

    if (isset($pdo) && $pdo instanceof PDO) {
        $permissionPdoForSeed = $pdo;
    } elseif (isset($connect) && $connect instanceof PDO) {
        $permissionPdoForSeed = $connect;
    }

    if ($permissionPdoForSeed instanceof PDO) {
        seed_minimum_role_permissions($permissionPdoForSeed);
    }
} catch (Throwable $e) {
    /*
     * Do not break pages if seeding fails.
     */
}
