<?php
declare(strict_types=1);

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

mocker_admin_require_super();

require_once __DIR__ . '/db.php';

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

$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
$keys = mocker_admin_permission_keys();

$normalizePerms = static function (array $raw) use ($keys): array {
    $out = [];
    foreach ($raw as $p) {
        $p = (string) $p;
        if (in_array($p, $keys, true)) {
            $out[] = $p;
        }
    }

    return array_values(array_unique($out));
};

if ($method === 'GET') {
    $rows = $pdo->query(
        'SELECT id, username, display_name, is_super, permissions_json, twofa_enabled, twofa_secret, created_at, updated_at FROM admin_operators ORDER BY id ASC'
    )->fetchAll(PDO::FETCH_ASSOC);
    $list = [];
    foreach ($rows as $r) {
        $pj = json_decode((string) $r['permissions_json'], true);
        $perms = is_array($pj) ? $normalizePerms($pj) : [];
        $isSuper = (int) ($r['is_super'] ?? 0) === 1;
        $list[] = [
            'id' => (int) $r['id'],
            'username' => (string) $r['username'],
            'displayName' => (string) $r['display_name'],
            'isSuper' => $isSuper,
            'permissions' => $isSuper ? $keys : $perms,
            'twofaEnabled' => (int) ($r['twofa_enabled'] ?? 0) === 1,
            'twofaCanEnable' => trim((string) ($r['twofa_secret'] ?? '')) !== '',
        ];
    }
    echo json_encode(['ok' => true, 'operators' => $list], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

$input = json_decode((string) file_get_contents('php://input'), true) ?: [];

mocker_require_csrf_post();

if ($method === 'POST') {
    $username = trim((string) ($input['username'] ?? ''));
    $password = (string) ($input['password'] ?? '');
    $displayName = trim((string) ($input['displayName'] ?? ''));
    $isSuper = !empty($input['isSuper']);
    $rawPerms = $input['permissions'] ?? [];
    if (!preg_match('/^[a-zA-Z0-9_]{2,32}$/', $username)) {
        http_response_code(400);
        echo json_encode(['ok' => false, 'error' => 'username'], JSON_UNESCAPED_UNICODE);
        exit;
    }
    if ($password === '' || strlen($password) < 8) {
        http_response_code(400);
        echo json_encode(['ok' => false, 'error' => 'password'], JSON_UNESCAPED_UNICODE);
        exit;
    }
    if ($displayName === '' || strlen($displayName) > 200) {
        http_response_code(400);
        echo json_encode(['ok' => false, 'error' => 'displayName'], JSON_UNESCAPED_UNICODE);
        exit;
    }
    $perms = is_array($rawPerms) ? $normalizePerms($rawPerms) : [];
    if (!$isSuper && $perms === []) {
        http_response_code(400);
        echo json_encode(['ok' => false, 'error' => 'permissions'], JSON_UNESCAPED_UNICODE);
        exit;
    }
    if ($isSuper) {
        $perms = [];
    }
    $hash = password_hash($password, PASSWORD_DEFAULT);
    $now = gmdate('c');
    $pj = json_encode($perms, JSON_UNESCAPED_UNICODE);
    try {
        $ins = $pdo->prepare(
            'INSERT INTO admin_operators (username, password_hash, display_name, is_super, permissions_json, created_at, updated_at)
             VALUES (:u, :p, :d, :s, :pj, :c, :u2)'
        );
        $ins->execute([
            ':u' => $username,
            ':p' => $hash,
            ':d' => $displayName,
            ':s' => $isSuper ? 1 : 0,
            ':pj' => $pj,
            ':c' => $now,
            ':u2' => $now,
        ]);
    } catch (PDOException $e) {
        if ($e->getCode() === '23000' || str_contains($e->getMessage(), 'UNIQUE')) {
            http_response_code(409);
            echo json_encode(['ok' => false, 'error' => 'duplicate'], JSON_UNESCAPED_UNICODE);
            exit;
        }
        throw $e;
    }
    echo json_encode(['ok' => true, 'id' => (int) $pdo->lastInsertId()], JSON_UNESCAPED_UNICODE);
    exit;
}

if ($method === 'PUT') {
    $id = (int) ($input['id'] ?? 0);
    if ($id <= 0) {
        http_response_code(400);
        echo json_encode(['ok' => false, 'error' => 'id'], JSON_UNESCAPED_UNICODE);
        exit;
    }
    $st = $pdo->prepare('SELECT id, username, display_name, is_super, permissions_json, twofa_enabled, twofa_secret FROM admin_operators WHERE id = :id LIMIT 1');
    $st->execute([':id' => $id]);
    $row = $st->fetch(PDO::FETCH_ASSOC);
    if (!$row) {
        http_response_code(404);
        echo json_encode(['ok' => false, 'error' => 'not_found'], JSON_UNESCAPED_UNICODE);
        exit;
    }
    $username = array_key_exists('username', $input) ? trim((string) $input['username']) : (string) $row['username'];
    if (!preg_match('/^[a-zA-Z0-9_]{2,32}$/', $username)) {
        http_response_code(400);
        echo json_encode(['ok' => false, 'error' => 'username'], JSON_UNESCAPED_UNICODE);
        exit;
    }
    $displayName = array_key_exists('displayName', $input) ? trim((string) $input['displayName']) : (string) $row['display_name'];
    if ($displayName === '' || strlen($displayName) > 200) {
        http_response_code(400);
        echo json_encode(['ok' => false, 'error' => 'displayName'], JSON_UNESCAPED_UNICODE);
        exit;
    }
    $isSuper = array_key_exists('isSuper', $input) ? !empty($input['isSuper']) : ((int) $row['is_super'] === 1);
    $perms = [];
    if (array_key_exists('permissions', $input)) {
        $rawPerms = $input['permissions'];
        $perms = is_array($rawPerms) ? $normalizePerms($rawPerms) : [];
    } else {
        $pj = json_decode((string) $row['permissions_json'], true);
        $perms = is_array($pj) ? $normalizePerms($pj) : [];
    }
    if ($isSuper) {
        $perms = [];
    } elseif ($perms === []) {
        http_response_code(400);
        echo json_encode(['ok' => false, 'error' => 'permissions'], JSON_UNESCAPED_UNICODE);
        exit;
    }
    $twofaReset = !empty($input['twofaReset']);
    $twofaEnabled = array_key_exists('twofaEnabled', $input) ? !empty($input['twofaEnabled']) : ((int) ($row['twofa_enabled'] ?? 0) === 1);
    if ($twofaReset) {
        $twofaEnabled = false;
    }
    $currentSecret = trim((string) ($row['twofa_secret'] ?? ''));
    $autoSetup = null;
    if ($twofaEnabled && $currentSecret === '') {
        $autoSecret = mocker_admin_twofa_generate_secret(20);
        $autoCodes = mocker_admin_twofa_generate_backup_codes(8);
        $autoHashes = mocker_admin_twofa_hash_backup_codes($autoCodes);
        $currentSecret = $autoSecret;
        $twofaSql = ', twofa_secret = :tfa_secret, twofa_backup_codes_json = :tfa_codes';
        $execExtra = [
            ':tfa_secret' => $autoSecret,
            ':tfa_codes' => json_encode($autoHashes, JSON_UNESCAPED_UNICODE),
        ];
        $autoSetup = [
            'secret' => $autoSecret,
            'backupCodes' => $autoCodes,
        ];
    } else {
        $execExtra = [];
    }
    $hashSql = '';
    $twofaSql = $twofaSql ?? '';
    $exec = [
        ':u' => $username,
        ':d' => $displayName,
        ':s' => $isSuper ? 1 : 0,
        ':pj' => json_encode($perms, JSON_UNESCAPED_UNICODE),
        ':tfa' => $twofaEnabled ? 1 : 0,
        ':id' => $id,
        ':now' => gmdate('c'),
    ];
    if (!$twofaEnabled) {
        $twofaSql = ', twofa_secret = NULL, twofa_backup_codes_json = :tfa_codes';
        $exec[':tfa_codes'] = json_encode([], JSON_UNESCAPED_UNICODE);
    } else {
        foreach ($execExtra as $k => $v) {
            $exec[$k] = $v;
        }
    }
    if (array_key_exists('password', $input)) {
        $pw = (string) $input['password'];
        if ($pw !== '') {
            if (strlen($pw) < 8) {
                http_response_code(400);
                echo json_encode(['ok' => false, 'error' => 'password'], JSON_UNESCAPED_UNICODE);
                exit;
            }
            $exec[':ph'] = password_hash($pw, PASSWORD_DEFAULT);
            $hashSql = ', password_hash = :ph';
        }
    }
    try {
        $up = $pdo->prepare(
            'UPDATE admin_operators SET username = :u, display_name = :d, is_super = :s, permissions_json = :pj, twofa_enabled = :tfa, updated_at = :now' . $hashSql . $twofaSql . ' WHERE id = :id'
        );
        $up->execute($exec);
    } catch (PDOException $e) {
        if ($e->getCode() === '23000' || str_contains($e->getMessage(), 'UNIQUE')) {
            http_response_code(409);
            echo json_encode(['ok' => false, 'error' => 'duplicate'], JSON_UNESCAPED_UNICODE);
            exit;
        }
        throw $e;
    }
    $response = ['ok' => true];
    if (is_array($autoSetup)) {
        $otpauth = mocker_admin_twofa_make_otpauth_uri('Mocker Admin', $username !== '' ? $username : ('staff-' . $id), (string) $autoSetup['secret']);
        $response['autoSetup'] = [
            'secret' => (string) $autoSetup['secret'],
            'otpauthUri' => $otpauth,
            'qrUrl' => 'https://api.qrserver.com/v1/create-qr-code/?size=220x220&data=' . rawurlencode($otpauth),
            'backupCodes' => $autoSetup['backupCodes'],
        ];
    }
    echo json_encode($response, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

if ($method === 'DELETE') {
    $delId = (int) ($input['id'] ?? 0);
    if ($delId <= 0) {
        http_response_code(400);
        echo json_encode(['ok' => false, 'error' => 'id'], JSON_UNESCAPED_UNICODE);
        exit;
    }
    $sid = (int) ($_SESSION['mocker_admin_id'] ?? 0);
    if ($sid > 0 && $sid === $delId) {
        http_response_code(400);
        echo json_encode(['ok' => false, 'error' => 'self_delete'], JSON_UNESCAPED_UNICODE);
        exit;
    }
    $del = $pdo->prepare('DELETE FROM admin_operators WHERE id = :id');
    $del->execute([':id' => $delId]);
    if ($del->rowCount() === 0) {
        http_response_code(404);
        echo json_encode(['ok' => false, 'error' => 'not_found'], JSON_UNESCAPED_UNICODE);
        exit;
    }
    echo json_encode(['ok' => true], JSON_UNESCAPED_UNICODE);
    exit;
}

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