<?php
declare(strict_types=1);

/**
 * 登录态管理：SESSION 登录 + "记住我" 持久登录（selector/validator 模式，cookie 只存
 * "selector:明文validator"，库里只存 validator 的哈希，防止 cookie 泄露后能直接登录其它账号也防时间侧通道）
 */
final class Auth
{
    private const SESSION_KEY = 'user';
    private const PERMISSION_SESSION_KEY = 'permissions';
    private const COOKIE_NAME = 'remember_token';
    private const REMEMBER_DAYS = 30;

    /**
     * 当前用户的有效权限（登录时算好缓存在 SESSION 里的快照，角色/部门变更要下次登录才生效）
     * 内置超级管理员直接放行一切，不查表
     */
    public static function permissions(): array
    {
        return $_SESSION[self::PERMISSION_SESSION_KEY] ?? ['isSuperAdmin' => false, 'permissions' => [], 'depaIds' => []];
    }

    public static function isSuperAdmin(): bool
    {
        return self::id() === 0;
    }

    /**
     * 计算并缓存当前登录用户的权限快照，登录成功（包括"记住我"自动登录）时调用
     */
    private static function refreshPermissions(int $adminId): void
    {
        if ($adminId === 0) {
            $_SESSION[self::PERMISSION_SESSION_KEY] = ['isSuperAdmin' => true, 'permissions' => [], 'depaIds' => []];
            return;
        }

        $resolved = PermissionResolver::resolveForAdmin($adminId);
        $_SESSION[self::PERMISSION_SESSION_KEY] = [
            'isSuperAdmin' => false,
            'permissions'  => $resolved['permissions'],
            'depaIds'      => $resolved['depaIds'],
        ];
    }

    /**
     * 当前登录用户（SESSION 里的快照：admin_id/admin_username/admin_name），未登录返回 null
     */
    public static function user(): ?array
    {
        return $_SESSION[self::SESSION_KEY] ?? null;
    }

    public static function id(): ?int
    {
        return isset($_SESSION[self::SESSION_KEY]['admin_id']) ? (int) $_SESSION[self::SESSION_KEY]['admin_id'] : null;
    }

    /**
     * 登录成功后调用：写 SESSION，$remember 为真时额外发一个"记住我" cookie
     */
    public static function login(array $admin, bool $remember): void
    {
        session_regenerate_id(true);

        $_SESSION[self::SESSION_KEY] = [
            'admin_id'       => (int) $admin['admin_id'],
            'admin_username' => $admin['admin_username'],
            'admin_name'     => $admin['admin_name'],
        ];
        self::refreshPermissions((int) $admin['admin_id']);

        if ($remember) {
            self::issueRememberCookie((int) $admin['admin_id']);
        }
    }

    /**
     * 退出登录：清 SESSION，如果带了"记住我" cookie 顺便吊销数据库里对应的记录
     */
    public static function logout(): void
    {
        $cookie = $_COOKIE[self::COOKIE_NAME] ?? '';
        if ($cookie !== '' && str_contains($cookie, ':')) {
            [$selector] = explode(':', $cookie, 2);
            Db::instance()->delete('admin_remember_token', ['admin_remember_token_selector' => $selector]);
        }
        setcookie(self::COOKIE_NAME, '', time() - 3600, '/');

        $_SESSION = [];
        session_regenerate_id(true);
    }

    /**
     * 当前 SESSION 没有登录态时，尝试用"记住我" cookie 自动登录
     * 每次用过就把 validator 轮换一次（同一个 selector，新 validator），降低 cookie 被截获后长期重放的风险
     */
    public static function tryRememberLogin(): bool
    {
        if (self::user() !== null) {
            return true;
        }

        $cookie = $_COOKIE[self::COOKIE_NAME] ?? '';
        if ($cookie === '' || !str_contains($cookie, ':')) {
            return false;
        }
        [$selector, $validator] = explode(':', $cookie, 2);

        $db = Db::instance();
        $row = $db->get('admin_remember_token', '*', [
            'admin_remember_token_selector' => $selector,
        ]);

        if (!$row || strtotime($row['admin_remember_token_expire_time']) < time()) {
            setcookie(self::COOKIE_NAME, '', time() - 3600, '/');
            return false;
        }

        if (!hash_equals($row['admin_remember_token_validator_hash'], hash('sha256', $validator))) {
            // validator 不匹配，可能是 cookie 被窃取后重放，吊销这条记录更安全
            $db->delete('admin_remember_token', ['admin_remember_token_selector' => $selector]);
            setcookie(self::COOKIE_NAME, '', time() - 3600, '/');
            return false;
        }

        $admin = $db->get('admin_user', '*', ['admin_id' => $row['admin_remember_token_lnk_admin_id']]);
        if (!$admin || !$admin['admin_allow']) {
            return false;
        }

        session_regenerate_id(true);
        $_SESSION[self::SESSION_KEY] = [
            'admin_id'       => (int) $admin['admin_id'],
            'admin_username' => $admin['admin_username'],
            'admin_name'     => $admin['admin_name'],
        ];
        self::refreshPermissions((int) $admin['admin_id']);

        self::rotateRememberCookie((int) $row['admin_remember_token_id'], (int) $admin['admin_id']);

        return true;
    }

    private static function issueRememberCookie(int $adminId): void
    {
        $selector = bin2hex(random_bytes(16));
        $validator = bin2hex(random_bytes(32));
        $expire = date('Y-m-d H:i:s', time() + self::REMEMBER_DAYS * 86400);

        Db::instance()->insert('admin_remember_token', [
            'admin_remember_token_lnk_admin_id'   => $adminId,
            'admin_remember_token_selector'       => $selector,
            'admin_remember_token_validator_hash' => hash('sha256', $validator),
            'admin_remember_token_expire_time'    => $expire,
        ]);

        setcookie(
            self::COOKIE_NAME,
            $selector . ':' . $validator,
            time() + self::REMEMBER_DAYS * 86400,
            '/',
            '',
            !empty($_SERVER['HTTPS']),
            true
        );
    }

    private static function rotateRememberCookie(int $tokenId, int $adminId): void
    {
        $validator = bin2hex(random_bytes(32));
        $expire = date('Y-m-d H:i:s', time() + self::REMEMBER_DAYS * 86400);
        $db = Db::instance();

        $row = $db->get('admin_remember_token', ['admin_remember_token_selector'], ['admin_remember_token_id' => $tokenId]);
        if (!$row) {
            return;
        }

        $db->update('admin_remember_token', [
            'admin_remember_token_validator_hash' => hash('sha256', $validator),
            'admin_remember_token_expire_time'    => $expire,
        ], ['admin_remember_token_id' => $tokenId]);

        setcookie(
            self::COOKIE_NAME,
            $row['admin_remember_token_selector'] . ':' . $validator,
            time() + self::REMEMBER_DAYS * 86400,
            '/',
            '',
            !empty($_SERVER['HTTPS']),
            true
        );
    }
}
