<?php
declare(strict_types=1);

namespace App\Modules\Auth;

use App\Core\Support\Session;
use App\Modules\Users\UserRepository;

final class AuthService
{
    private const SESSION_USER_KEY = 'auth_user';
    private const REMEMBER_COOKIE = 'remember_token';
    private const REMEMBER_DAYS = 30;

    public function __construct(private readonly UserRepository $users)
    {
    }

    public function attempt(string $email, string $password): bool
    {
        $storedUser = $this->users->findByEmail($email);
        if ($storedUser === null) {
            return false;
        }

        $storedHash = (string) ($storedUser['password_hash'] ?? '');
        if ($storedHash === '' || !password_verify($password, $storedHash)) {
            return false;
        }

        Session::regenerate();

        $_SESSION[self::SESSION_USER_KEY] = [
            'id' => (int) ($storedUser['id'] ?? 0),
            'name' => (string) ($storedUser['name'] ?? ''),
            'email' => strtolower((string) ($storedUser['email'] ?? '')),
            'login_at' => date(DATE_ATOM),
        ];

        return true;
    }

    public function check(): bool
    {
        return isset($_SESSION[self::SESSION_USER_KEY]) && is_array($_SESSION[self::SESSION_USER_KEY]);
    }

    /**
     * @return array<string, mixed>|null
     */
    public function user(): ?array
    {
        if (!$this->check()) {
            return null;
        }

        /** @var array<string, mixed> $user */
        $user = $_SESSION[self::SESSION_USER_KEY];
        return $user;
    }

    public function createRememberToken(int $userId): void
    {
        $token = bin2hex(random_bytes(32));
        $this->users->updateRememberToken($userId, hash('sha256', $token));

        $secure = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off';
        setcookie(self::REMEMBER_COOKIE, $token, [
            'expires' => time() + (self::REMEMBER_DAYS * 86400),
            'path' => '/',
            'httponly' => true,
            'secure' => $secure,
            'samesite' => 'Lax',
        ]);
    }

    public function loginFromRememberToken(): bool
    {
        $cookieToken = $_COOKIE[self::REMEMBER_COOKIE] ?? '';
        if ($cookieToken === '') {
            return false;
        }

        $tokenHash = hash('sha256', $cookieToken);
        $user = $this->users->findByRememberToken($tokenHash);
        if ($user === null) {
            $this->clearRememberCookie();
            return false;
        }

        Session::regenerate();

        $_SESSION[self::SESSION_USER_KEY] = [
            'id' => (int) $user['id'],
            'name' => (string) $user['name'],
            'email' => (string) $user['email'],
            'login_at' => date(DATE_ATOM),
        ];

        return true;
    }

    public function logout(): void
    {
        $user = $this->user();
        if ($user !== null) {
            $this->users->updateRememberToken((int) $user['id'], null);
        }
        $this->clearRememberCookie();

        unset($_SESSION[self::SESSION_USER_KEY]);
        Session::regenerate();
    }

    private function clearRememberCookie(): void
    {
        $secure = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off';
        setcookie(self::REMEMBER_COOKIE, '', [
            'expires' => time() - 3600,
            'path' => '/',
            'httponly' => true,
            'secure' => $secure,
            'samesite' => 'Lax',
        ]);
        unset($_COOKIE[self::REMEMBER_COOKIE]);
    }
}
