<?php
declare(strict_types=1);

require_once __DIR__ . '/bootstrap.php';
require_once __DIR__ . '/admin_auth_lib.php';
require_once __DIR__ . '/admin_2fa_lib.php';
require_once __DIR__ . '/db.php';

mocker_admin_require_login();

$adminKind = (string) ($_SESSION['mocker_admin_kind'] ?? 'master');
$adminId = isset($_SESSION['mocker_admin_id']) ? (int) $_SESSION['mocker_admin_id'] : 0;
if ($adminKind !== 'staff' && $adminKind !== 'master') {
    http_response_code(403);
    echo json_encode(['ok' => false, 'error' => 'forbidden'], JSON_UNESCAPED_UNICODE);
    exit;
}

try {
    $pdo = mocker_db();
} catch (Throwable $e) {
    http_response_code(503);
    echo json_encode(['ok' => false, 'error' => 'db'], JSON_UNESCAPED_UNICODE);
    exit;
}

$load = static function () use ($pdo, $adminId, $adminKind): ?array {
    if ($adminKind === 'master') {
        $row = $pdo->query('SELECT twofa_enabled, twofa_secret, twofa_backup_codes_json FROM admin_master_2fa WHERE id = 1 LIMIT 1')->fetch(PDO::FETCH_ASSOC);
        if (!$row) {
            return null;
        }
        $hashes = json_decode((string) ($row['twofa_backup_codes_json'] ?? '[]'), true);
        return [
            'username' => 'master',
            'enabled' => (int) ($row['twofa_enabled'] ?? 0) === 1,
            'secret' => trim((string) ($row['twofa_secret'] ?? '')),
            'backupHashes' => is_array($hashes) ? $hashes : [],
        ];
    }
    $st = $pdo->prepare('SELECT username, twofa_enabled, twofa_secret, twofa_backup_codes_json FROM admin_operators WHERE id = :id LIMIT 1');
    $st->execute([':id' => $adminId]);
    $row = $st->fetch(PDO::FETCH_ASSOC);
    if (!$row) {
        return null;
    }
    $hashes = json_decode((string) ($row['twofa_backup_codes_json'] ?? '[]'), true);
    return [
        'username' => (string) ($row['username'] ?? ''),
        'enabled' => (int) ($row['twofa_enabled'] ?? 0) === 1,
        'secret' => trim((string) ($row['twofa_secret'] ?? '')),
        'backupHashes' => is_array($hashes) ? $hashes : [],
    ];
};

$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
if ($method === 'GET') {
    $row = $load();
    if (!$row) {
        http_response_code(404);
        echo json_encode(['ok' => false, 'error' => 'not_found'], JSON_UNESCAPED_UNICODE);
        exit;
    }
    echo json_encode([
        'ok' => true,
        'twofa' => [
            'enabled' => $row['enabled'],
            'backupCodesCount' => count($row['backupHashes']),
        ],
    ], JSON_UNESCAPED_UNICODE);
    exit;
}

if ($method !== 'POST') {
    http_response_code(405);
    echo json_encode(['ok' => false, 'error' => 'method'], JSON_UNESCAPED_UNICODE);
    exit;
}

mocker_require_csrf_post();

$input = json_decode((string) file_get_contents('php://input'), true) ?: [];
$action = (string) ($input['action'] ?? '');
$sessionDraft = $_SESSION['mocker_admin_2fa_setup'] ?? null;

if ($action === 'start_setup') {
    $row = $load();
    if (!$row) {
        http_response_code(404);
        echo json_encode(['ok' => false, 'error' => 'not_found'], JSON_UNESCAPED_UNICODE);
        exit;
    }
    $secret = mocker_admin_twofa_generate_secret(20);
    $issuer = 'Mocker Admin';
    $account = $row['username'] !== '' ? $row['username'] : ('staff-' . $adminId);
    $uri = mocker_admin_twofa_make_otpauth_uri($issuer, $account, $secret);
    $_SESSION['mocker_admin_2fa_setup'] = [
        'secret' => $secret,
        'createdAt' => time(),
    ];
    echo json_encode([
        'ok' => true,
        'setup' => [
            'secret' => $secret,
            'otpauthUri' => $uri,
            'qrUrl' => 'https://api.qrserver.com/v1/create-qr-code/?size=220x220&data=' . rawurlencode($uri),
        ],
    ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

if ($action === 'confirm_setup') {
    if (!is_array($sessionDraft) || trim((string) ($sessionDraft['secret'] ?? '')) === '') {
        http_response_code(400);
        echo json_encode(['ok' => false, 'error' => 'setup_missing'], JSON_UNESCAPED_UNICODE);
        exit;
    }
    $secret = trim((string) $sessionDraft['secret']);
    $code = trim((string) ($input['code'] ?? ''));
    if (!mocker_admin_twofa_verify_code($secret, $code, 1)) {
        http_response_code(400);
        echo json_encode(['ok' => false, 'error' => 'twofa_invalid'], JSON_UNESCAPED_UNICODE);
        exit;
    }
    $backupCodes = mocker_admin_twofa_generate_backup_codes(8);
    $backupHashes = mocker_admin_twofa_hash_backup_codes($backupCodes);
    if ($adminKind === 'master') {
        $up = $pdo->prepare(
            'UPDATE admin_master_2fa
             SET twofa_enabled = 1, twofa_secret = :secret, twofa_backup_codes_json = :codes, updated_at = :u
             WHERE id = 1'
        );
        $up->execute([
            ':secret' => $secret,
            ':codes' => json_encode($backupHashes, JSON_UNESCAPED_UNICODE),
            ':u' => gmdate('c'),
        ]);
    } else {
        $up = $pdo->prepare(
            'UPDATE admin_operators
             SET twofa_enabled = 1, twofa_secret = :secret, twofa_backup_codes_json = :codes, updated_at = :u
             WHERE id = :id'
        );
        $up->execute([
            ':secret' => $secret,
            ':codes' => json_encode($backupHashes, JSON_UNESCAPED_UNICODE),
            ':u' => gmdate('c'),
            ':id' => $adminId,
        ]);
    }
    unset($_SESSION['mocker_admin_2fa_setup']);
    echo json_encode([
        'ok' => true,
        'backupCodes' => $backupCodes,
    ], JSON_UNESCAPED_UNICODE);
    exit;
}

if ($action === 'disable') {
    $code = trim((string) ($input['code'] ?? ''));
    $row = $load();
    if (!$row) {
        http_response_code(404);
        echo json_encode(['ok' => false, 'error' => 'not_found'], JSON_UNESCAPED_UNICODE);
        exit;
    }
    if (!$row['enabled'] || $row['secret'] === '') {
        echo json_encode(['ok' => true], JSON_UNESCAPED_UNICODE);
        exit;
    }
    $ok = mocker_admin_twofa_verify_code($row['secret'], $code, 1);
    if (!$ok) {
        [$used] = mocker_admin_twofa_consume_backup_code($row['backupHashes'], $code);
        $ok = $used;
    }
    if (!$ok) {
        http_response_code(400);
        echo json_encode(['ok' => false, 'error' => 'twofa_invalid'], JSON_UNESCAPED_UNICODE);
        exit;
    }
    if ($adminKind === 'master') {
        $up = $pdo->prepare(
            'UPDATE admin_master_2fa
             SET twofa_enabled = 0, twofa_secret = NULL, twofa_backup_codes_json = :codes, updated_at = :u
             WHERE id = 1'
        );
        $up->execute([
            ':codes' => json_encode([], JSON_UNESCAPED_UNICODE),
            ':u' => gmdate('c'),
        ]);
    } else {
        $up = $pdo->prepare(
            'UPDATE admin_operators
             SET twofa_enabled = 0, twofa_secret = NULL, twofa_backup_codes_json = :codes, updated_at = :u
             WHERE id = :id'
        );
        $up->execute([
            ':codes' => json_encode([], JSON_UNESCAPED_UNICODE),
            ':u' => gmdate('c'),
            ':id' => $adminId,
        ]);
    }
    unset($_SESSION['mocker_admin_2fa_setup']);
    echo json_encode(['ok' => true], JSON_UNESCAPED_UNICODE);
    exit;
}

if ($action === 'regenerate_backup') {
    $code = trim((string) ($input['code'] ?? ''));
    $row = $load();
    if (!$row || !$row['enabled'] || $row['secret'] === '') {
        http_response_code(400);
        echo json_encode(['ok' => false, 'error' => 'twofa_not_enabled'], JSON_UNESCAPED_UNICODE);
        exit;
    }
    if (!mocker_admin_twofa_verify_code($row['secret'], $code, 1)) {
        http_response_code(400);
        echo json_encode(['ok' => false, 'error' => 'twofa_invalid'], JSON_UNESCAPED_UNICODE);
        exit;
    }
    $backupCodes = mocker_admin_twofa_generate_backup_codes(8);
    $backupHashes = mocker_admin_twofa_hash_backup_codes($backupCodes);
    if ($adminKind === 'master') {
        $up = $pdo->prepare('UPDATE admin_master_2fa SET twofa_backup_codes_json = :codes, updated_at = :u WHERE id = 1');
        $up->execute([
            ':codes' => json_encode($backupHashes, JSON_UNESCAPED_UNICODE),
            ':u' => gmdate('c'),
        ]);
    } else {
        $up = $pdo->prepare('UPDATE admin_operators SET twofa_backup_codes_json = :codes, updated_at = :u WHERE id = :id');
        $up->execute([
            ':codes' => json_encode($backupHashes, JSON_UNESCAPED_UNICODE),
            ':u' => gmdate('c'),
            ':id' => $adminId,
        ]);
    }
    echo json_encode(['ok' => true, 'backupCodes' => $backupCodes], JSON_UNESCAPED_UNICODE);
    exit;
}

http_response_code(400);
echo json_encode(['ok' => false, 'error' => 'action'], JSON_UNESCAPED_UNICODE);
