<?php
declare(strict_types=1);

/** Hashed login/register attempts + rate limit (task 3.x / A6). */
final class AuthAttempts {
    private function __construct() {
    }

    public static function ensureSchema(): void {
        AuthEmailSchema::ensure(Database::pdo());
    }

    public static function record(string $outcome, ?string $username = null): void {
        self::ensureSchema();
        $ipHash = self::hashIp(self::clientIp());
        $userHash = $username !== null && $username !== '' ? self::hashUsername($username) : null;
        Database::pdo()->prepare(
            'INSERT INTO auth_attempts (ip_hash, username_hash, outcome, created_at) VALUES (?, ?, ?, ?)'
        )->execute([$ipHash, $userHash, $outcome, date('Y-m-d H:i:s')]);
    }

    public static function isRateLimited(?string $username): bool {
        self::ensureSchema();
        $max = max(3, (int) cfg('auth.max_attempts', 8));
        $windowSec = max(60, (int) cfg('auth.lockout_window_minutes', 15) * 60);
        $since = date('Y-m-d H:i:s', time() - $windowSec);
        $pdo = Database::pdo();
        $ipHash = self::hashIp(self::clientIp());
        $st = $pdo->prepare(
            "SELECT COUNT(*) FROM auth_attempts WHERE ip_hash = ? AND outcome LIKE '%_fail' AND created_at >= ?"
        );
        $st->execute([$ipHash, $since]);
        if ((int) $st->fetchColumn() >= $max) {
            return true;
        }
        if ($username !== null && $username !== '') {
            $userHash = self::hashUsername($username);
            $st = $pdo->prepare(
                "SELECT COUNT(*) FROM auth_attempts WHERE username_hash = ? AND outcome LIKE '%_fail' AND created_at >= ?"
            );
            $st->execute([$userHash, $since]);
            if ((int) $st->fetchColumn() >= $max) {
                return true;
            }
        }
        return false;
    }

    public static function recentFailureCount(string $username): int {
        self::ensureSchema();
        $windowSec = max(60, (int) cfg('auth.lockout_window_minutes', 15) * 60);
        $since = date('Y-m-d H:i:s', time() - $windowSec);
        $st = Database::pdo()->prepare(
            "SELECT COUNT(*) FROM auth_attempts WHERE username_hash = ? AND outcome LIKE '%_fail' AND created_at >= ?"
        );
        $st->execute([self::hashUsername($username), $since]);
        return (int) $st->fetchColumn();
    }

    public static function clearForUser(int $userId): int {
        self::ensureSchema();
        $st = Database::pdo()->prepare('SELECT username FROM users WHERE id = ? LIMIT 1');
        $st->execute([$userId]);
        $username = $st->fetchColumn();
        if (!is_string($username) || $username === '') {
            return 0;
        }
        return self::clearForUsername($username);
    }

    public static function clearForUsername(string $username): int {
        self::ensureSchema();
        $hash = self::hashUsername($username);
        $st = Database::pdo()->prepare('DELETE FROM auth_attempts WHERE username_hash = ?');
        $st->execute([$hash]);
        $deleted = $st->rowCount();
        $pdo = Database::pdo();
        $pdo->prepare(
            "UPDATE users SET status = 'active' WHERE username = ? AND status = 'locked'"
        )->execute([$username]);
        return $deleted;
    }

    public static function clientIp(): string {
        $xff = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? '';
        if (is_string($xff) && $xff !== '') {
            $parts = explode(',', $xff);
            return trim($parts[0]);
        }
        return (string) ($_SERVER['REMOTE_ADDR'] ?? '0.0.0.0');
    }

    public static function hashIp(string $ip): string {
        return hash_hmac('sha256', trim($ip), self::pepper());
    }

    public static function hashUsername(string $username): string {
        return hash_hmac('sha256', strtolower(trim($username)), self::pepper());
    }

    private static function pepper(): string {
        $p = (string) cfg('auth.encryption_key', '');
        if ($p === '') {
            $p = 'dev-change-me-set-auth.encryption_key-in-config.php';
        }
        return $p;
    }
}
