<?php

declare(strict_types=1);

final class HyroAdminAuth
{
    private const SESSION_KEY = 'hyro_admin_user';

    public static function startSession(): void
    {
        if (session_status() !== PHP_SESSION_NONE) {
            return;
        }

        session_name('HYROADMINSESSID');

        session_set_cookie_params([
            'lifetime' => 0,
            'path' => '/experience/admin/',
            'secure' => !empty($_SERVER['HTTPS']),
            'httponly' => true,
            'samesite' => 'Lax',
        ]);

        session_start();
    }

    public static function attempt(
        PDO $database,
        string $email,
        string $password
    ): bool {
        self::startSession();

        $statement = $database->prepare(
            '
            SELECT
                id,
                user_code,
                full_name,
                email,
                password_hash,
                role_code,
                status,
                failed_login_attempts,
                locked_until
            FROM hyro_admin_users
            WHERE email = :email
            LIMIT 1
            '
        );

        $statement->execute([
            'email' => strtolower(trim($email)),
        ]);

        $user = $statement->fetch();

        if (!is_array($user)) {
            return false;
        }

        if (($user['status'] ?? '') !== 'active') {
            return false;
        }

        if (!empty($user['locked_until'])) {
            $lockedUntil = strtotime(
                (string) $user['locked_until']
            );

            if ($lockedUntil !== false && $lockedUntil > time()) {
                return false;
            }
        }

        if (
            !password_verify(
                $password,
                (string) $user['password_hash']
            )
        ) {
            self::recordFailedAttempt(
                $database,
                (int) $user['id'],
                (int) $user['failed_login_attempts']
            );

            return false;
        }

        session_regenerate_id(true);

        $_SESSION[self::SESSION_KEY] = [
            'id' => (int) $user['id'],
            'user_code' => (string) $user['user_code'],
            'full_name' => (string) $user['full_name'],
            'email' => (string) $user['email'],
            'role_code' => (string) $user['role_code'],
        ];

        $update = $database->prepare(
            '
            UPDATE hyro_admin_users
            SET
                failed_login_attempts = 0,
                locked_until = NULL,
                last_login_at = :last_login_at,
                last_login_ip = :last_login_ip,
                updated_at = :updated_at
            WHERE id = :id
            '
        );

        $now = date('Y-m-d H:i:s');

        $update->execute([
            'last_login_at' => $now,
            'last_login_ip' =>
                $_SERVER['REMOTE_ADDR'] ?? null,
            'updated_at' => $now,
            'id' => (int) $user['id'],
        ]);

        hyro_log(
            'admin-auth',
            'Admin login successful.',
            [
                'user_id' => (int) $user['id'],
                'email' => (string) $user['email'],
            ]
        );

        return true;
    }

    public static function check(): bool
    {
        self::startSession();

        return isset($_SESSION[self::SESSION_KEY])
            && is_array($_SESSION[self::SESSION_KEY]);
    }

    public static function user(): ?array
    {
        self::startSession();

        $user = $_SESSION[self::SESSION_KEY] ?? null;

        return is_array($user)
            ? $user
            : null;
    }

    public static function requireLogin(): void
    {
        if (!self::check()) {
            hyro_redirect(
                '/experience/admin/login.php'
            );
        }
    }

    public static function logout(): void
    {
        self::startSession();

        unset($_SESSION[self::SESSION_KEY]);

        session_regenerate_id(true);
    }

    private static function recordFailedAttempt(
        PDO $database,
        int $userId,
        int $currentAttempts
    ): void {
        $newAttempts = $currentAttempts + 1;

        $lockedUntil = null;

        if ($newAttempts >= 5) {
            $lockedUntil = date(
                'Y-m-d H:i:s',
                strtotime('+15 minutes')
            );

            $newAttempts = 0;
        }

        $statement = $database->prepare(
            '
            UPDATE hyro_admin_users
            SET
                failed_login_attempts = :attempts,
                locked_until = :locked_until,
                updated_at = :updated_at
            WHERE id = :id
            '
        );

        $statement->execute([
            'attempts' => $newAttempts,
            'locked_until' => $lockedUntil,
            'updated_at' => date('Y-m-d H:i:s'),
            'id' => $userId,
        ]);
    }
}
