<?php
declare(strict_types=1);

if (session_status() !== PHP_SESSION_ACTIVE) {
    session_start();
}

require_once __DIR__ . '/../db.php';

if (!function_exists('permissions_pdo')) {
    function permissions_pdo(): PDO
    {
        $pdo = db();
        if (!$pdo instanceof PDO) {
            throw new RuntimeException('DB not ready');
        }

        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);

        return $pdo;
    }
}

if (!function_exists('current_user_id')) {
    function current_user_id(): int
    {
        return (int)($_SESSION['user_id'] ?? 0);
    }
}

if (!function_exists('current_role')) {
    function current_role(): string
    {
        return strtoupper((string)($_SESSION['role'] ?? 'STAFF'));
    }
}

if (!function_exists('is_owner')) {
    function is_owner(): bool
    {
        return current_role() === 'OWNER';
    }
}

if (!function_exists('is_admin')) {
    function is_admin(): bool
    {
        return current_role() === 'ADMIN';
    }
}

if (!function_exists('is_staff')) {
    function is_staff(): bool
    {
        return current_role() === 'STAFF';
    }
}

if (!function_exists('ensure_permissions_table')) {
    function ensure_permissions_table(?PDO $pdo = null): void
    {
        $pdo ??= permissions_pdo();

        $pdo->exec("
            CREATE TABLE IF NOT EXISTS employee_permissions (
                id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
                user_id INT UNSIGNED NOT NULL,
                permission_code VARCHAR(150) NOT NULL,
                created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
                UNIQUE KEY uniq_user_perm (user_id, permission_code),
                KEY idx_user (user_id)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
        ");
    }
}

if (!function_exists('has_permission')) {
    function has_permission(string $code, ?int $userId = null, ?PDO $pdo = null): bool
    {
        $pdo ??= permissions_pdo();
        ensure_permissions_table($pdo);

        if (is_owner() || is_admin()) {
            return true;
        }

        $userId ??= current_user_id();
        if ($userId <= 0 || $code === '') {
            return false;
        }

        $st = $pdo->prepare("
            SELECT 1
            FROM employee_permissions
            WHERE user_id = ?
              AND permission_code = ?
            LIMIT 1
        ");
        $st->execute([$userId, $code]);

        return (bool)$st->fetchColumn();
    }
}

if (!function_exists('has_any_permission')) {
    function has_any_permission(array $codes, ?int $userId = null, ?PDO $pdo = null): bool
    {
        foreach ($codes as $code) {
            if (has_permission((string)$code, $userId, $pdo)) {
                return true;
            }
        }
        return false;
    }
}

if (!function_exists('has_all_permissions')) {
    function has_all_permissions(array $codes, ?int $userId = null, ?PDO $pdo = null): bool
    {
        foreach ($codes as $code) {
            if (!has_permission((string)$code, $userId, $pdo)) {
                return false;
            }
        }
        return true;
    }
}

if (!function_exists('get_user_permissions')) {
    function get_user_permissions(?int $userId = null, ?PDO $pdo = null): array
    {
        $pdo ??= permissions_pdo();
        ensure_permissions_table($pdo);

        if (is_owner() || is_admin()) {
            return ['*'];
        }

        $userId ??= current_user_id();
        if ($userId <= 0) {
            return [];
        }

        $st = $pdo->prepare("
            SELECT permission_code
            FROM employee_permissions
            WHERE user_id = ?
            ORDER BY permission_code ASC
        ");
        $st->execute([$userId]);

        return array_map('strval', $st->fetchAll(PDO::FETCH_COLUMN));
    }
}

if (!function_exists('grant_permission')) {
    function grant_permission(int $userId, string $code, ?PDO $pdo = null): bool
    {
        $pdo ??= permissions_pdo();
        ensure_permissions_table($pdo);

        if ($userId <= 0 || $code === '') {
            return false;
        }

        $st = $pdo->prepare("
            INSERT IGNORE INTO employee_permissions (user_id, permission_code)
            VALUES (?, ?)
        ");

        return $st->execute([$userId, $code]);
    }
}

if (!function_exists('revoke_permission')) {
    function revoke_permission(int $userId, string $code, ?PDO $pdo = null): bool
    {
        $pdo ??= permissions_pdo();

        if ($userId <= 0 || $code === '') {
            return false;
        }

        $st = $pdo->prepare("
            DELETE FROM employee_permissions
            WHERE user_id = ?
              AND permission_code = ?
        ");

        return $st->execute([$userId, $code]);
    }
}

if (!function_exists('replace_user_permissions')) {
    function replace_user_permissions(int $userId, array $codes, ?PDO $pdo = null): bool
    {
        $pdo ??= permissions_pdo();
        ensure_permissions_table($pdo);

        if ($userId <= 0) {
            return false;
        }

        $codes = array_values(array_unique(array_filter(array_map('strval', $codes))));
        $pdo->beginTransaction();

        try {
            $del = $pdo->prepare("DELETE FROM employee_permissions WHERE user_id = ?");
            $del->execute([$userId]);

            if (!empty($codes)) {
                $ins = $pdo->prepare("
                    INSERT INTO employee_permissions (user_id, permission_code)
                    VALUES (?, ?)
                ");
                foreach ($codes as $code) {
                    $ins->execute([$userId, $code]);
                }
            }

            $pdo->commit();
            return true;
        } catch (Throwable $e) {
            if ($pdo->inTransaction()) {
                $pdo->rollBack();
            }
            return false;
        }
    }
}

if (!function_exists('require_permission')) {
    function require_permission(string $code, ?PDO $pdo = null, ?string $message = null): void
    {
        if (has_permission($code, null, $pdo)) {
            return;
        }

        http_response_code(403);
        exit($message ?: 'غير مصرح');
    }
}

if (!function_exists('require_any_permission')) {
    function require_any_permission(array $codes, ?PDO $pdo = null, ?string $message = null): void
    {
        if (has_any_permission($codes, null, $pdo)) {
            return;
        }

        http_response_code(403);
        exit($message ?: 'غير مصرح');
    }
}