<?php
declare(strict_types=1);

/** Migration 008 — email registration + 2FA tables (SQLite dev + MariaDB ALTER). */
final class AuthEmailSchema {
    private static bool $ensured = false;

    private function __construct() {
    }

    public static function ensure(PDO $pdo): void {
        if (self::$ensured) {
            return;
        }
        if (!Database::tableExists($pdo, 'users')) {
            self::$ensured = true;
            return;
        }
        Database::withMigrationLock($pdo, 'auth_email_schema', static function () use ($pdo): void {
            self::ensureUserColumns($pdo);
            self::ensureAuthTokens($pdo);
            self::ensureAuthFactors($pdo);
            self::ensureAuthAttempts($pdo);
        });
        self::$ensured = true;
    }

    private static function ensureUserColumns(PDO $pdo): void {
        $existing = Database::columnNames($pdo, 'users');
        $cols = [
            'email_normalized' => Database::isMysql() ? 'VARCHAR(254) NULL' : 'TEXT NULL',
            'email_verified_at' => Database::isMysql() ? 'TIMESTAMP NULL' : 'TEXT NULL',
            'status' => Database::isMysql()
                ? "ENUM('pending','active','locked','disabled') NOT NULL DEFAULT 'active'"
                : "TEXT NOT NULL DEFAULT 'active'",
            'recovery_email_normalized' => Database::isMysql() ? 'VARCHAR(254) NULL' : 'TEXT NULL',
        ];
        foreach ($cols as $name => $ddl) {
            if (in_array($name, $existing, true)) {
                continue;
            }
            $pdo->exec('ALTER TABLE users ADD COLUMN ' . $name . ' ' . $ddl);
            $existing[] = $name;
        }
    }

    private static function ensureAuthTokens(PDO $pdo): void {
        if (Database::tableExists($pdo, 'auth_tokens')) {
            return;
        }
        if (Database::isMysql()) {
            $pdo->exec(
                "CREATE TABLE auth_tokens (
                    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
                    user_id INT UNSIGNED NULL,
                    purpose ENUM('verify_email','reset_password','login_magic') NOT NULL,
                    token_hash CHAR(64) NOT NULL,
                    email_normalized VARCHAR(254) NULL,
                    expires_at TIMESTAMP NOT NULL,
                    used_at TIMESTAMP NULL,
                    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                    KEY idx_auth_tokens_hash (token_hash),
                    KEY idx_auth_tokens_email (email_normalized)
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
            );
            return;
        }
        $pdo->exec(
            "CREATE TABLE auth_tokens (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                user_id INTEGER NULL,
                purpose TEXT NOT NULL,
                token_hash TEXT NOT NULL,
                email_normalized TEXT NULL,
                expires_at TEXT NOT NULL,
                used_at TEXT NULL,
                created_at TEXT NOT NULL DEFAULT (datetime('now'))
            )"
        );
        $pdo->exec('CREATE INDEX IF NOT EXISTS idx_auth_tokens_hash ON auth_tokens (token_hash)');
    }

    private static function ensureAuthFactors(PDO $pdo): void {
        if (Database::tableExists($pdo, 'auth_factors')) {
            return;
        }
        if (Database::isMysql()) {
            $pdo->exec(
                "CREATE TABLE auth_factors (
                    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
                    user_id INT UNSIGNED NOT NULL,
                    type ENUM('totp','webauthn','backup_code') NOT NULL,
                    secret_encrypted TEXT NULL,
                    label VARCHAR(64) NULL,
                    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                    KEY idx_auth_factors_user (user_id)
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
            );
            return;
        }
        $pdo->exec(
            "CREATE TABLE auth_factors (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                user_id INTEGER NOT NULL,
                type TEXT NOT NULL,
                secret_encrypted TEXT NULL,
                label TEXT NULL,
                created_at TEXT NOT NULL DEFAULT (datetime('now'))
            )"
        );
    }

    private static function ensureAuthAttempts(PDO $pdo): void {
        if (Database::tableExists($pdo, 'auth_attempts')) {
            return;
        }
        if (Database::isMysql()) {
            $pdo->exec(
                "CREATE TABLE auth_attempts (
                    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
                    ip_hash CHAR(64) NOT NULL,
                    username_hash CHAR(64) NULL,
                    outcome VARCHAR(32) NOT NULL,
                    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                    KEY idx_auth_attempts_ip (ip_hash, created_at)
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
            );
            return;
        }
        $pdo->exec(
            "CREATE TABLE auth_attempts (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                ip_hash TEXT NOT NULL,
                username_hash TEXT NULL,
                outcome TEXT NOT NULL,
                created_at TEXT NOT NULL DEFAULT (datetime('now'))
            )"
        );
    }
}
