<?php
declare(strict_types=1);

require __DIR__ . '/common.php';

$env = gobao_load_env();
$method = (string)($_SERVER['REQUEST_METHOD'] ?? 'GET');
$input = $method === 'POST' ? gobao_json_input() : [];
$action = trim((string)($input['action'] ?? $_GET['action'] ?? 'me'));
$db = gobao_db();
if (!$db) {
    gobao_fail('Database is not available: ' . gobao_db_last_error(), 500);
}

if ($action === 'login_or_register') {
    $username = gobao_normalize_username((string)($input['username'] ?? ''));
    $stmt = $db->prepare('SELECT 1 FROM users WHERE username = :username');
    $stmt->execute([':username' => $username]);
    $action = $stmt->fetchColumn() ? 'login' : 'register';
}

if ($action === 'register') {
    $username = gobao_normalize_username((string)($input['username'] ?? ''));
    $password = (string)($input['password'] ?? '');
    $displayName = trim((string)($input['displayName'] ?? ''));
    if ($displayName === '') {
        $displayName = $username;
    }
    if (strlen($password) < 10 || strlen($password) > 200) {
        gobao_fail('Password must be 10-200 characters.', 422);
    }
    if ($displayName === '' || mb_strlen($displayName, 'UTF-8') > 60) {
        gobao_fail('Display name must be 1-60 characters.', 422);
    }
    $existing = $db->prepare('SELECT 1 FROM users WHERE username = :username');
    $existing->execute([':username' => $username]);
    if ($existing->fetchColumn()) {
        gobao_fail('Username is already registered.', 409);
    }
    $userId = 'usr_' . substr(bin2hex(random_bytes(16)), 0, 24);
    $now = gmdate('c');
    $stmt = $db->prepare('
        INSERT INTO users(user_id, username, display_name, password_hash, status, created_at, updated_at)
        VALUES(:user_id, :username, :display_name, :password_hash, :status, :created_at, :updated_at)
    ');
    $stmt->execute([
        ':user_id' => $userId,
        ':username' => $username,
        ':display_name' => $displayName,
        ':password_hash' => password_hash($password, PASSWORD_DEFAULT),
        ':status' => 'active',
        ':created_at' => $now,
        ':updated_at' => $now,
    ]);
    $session = gobao_auth_session_create($userId);
    gobao_ok(['user' => [
        'userId' => $userId,
        'username' => $username,
        'displayName' => $displayName,
        'status' => 'active',
        'createdAt' => $now,
    ]] + $session);
}

if ($action === 'login') {
    $username = gobao_normalize_username((string)($input['username'] ?? ''));
    $password = (string)($input['password'] ?? '');
    $stmt = $db->prepare('SELECT * FROM users WHERE username = :username LIMIT 1');
    $stmt->execute([':username' => $username]);
    $user = $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
    if (!$user || (string)$user['status'] !== 'active' || !password_verify($password, (string)$user['password_hash'])) {
        usleep(250000);
        gobao_fail('Invalid username or password.', 401);
    }
    $db->prepare('UPDATE users SET last_login_at = :now, updated_at = :now WHERE user_id = :user_id')
        ->execute([':now' => gmdate('c'), ':user_id' => (string)$user['user_id']]);
    $session = gobao_auth_session_create((string)$user['user_id']);
    gobao_ok(['user' => gobao_public_user($user)] + $session);
}

if ($action === 'logout') {
    $token = gobao_bearer_token();
    if ($token !== '') {
        $stmt = $db->prepare('UPDATE auth_sessions SET revoked_at = :now WHERE token_hash = :token_hash');
        $stmt->execute([':now' => gmdate('c'), ':token_hash' => gobao_token_hash($token)]);
    }
    gobao_ok(['loggedOut' => true]);
}

if ($action === 'me') {
    $user = gobao_current_user(true);
    gobao_ok(['user' => gobao_public_user($user)]);
}

if ($action === 'create_pairing') {
    $user = gobao_current_user(true);
    $code = strtoupper(substr(bin2hex(random_bytes(5)), 0, 10));
    $formatted = substr($code, 0, 5) . '-' . substr($code, 5);
    $expiresAt = gmdate('c', time() + 3600);
    $stmt = $db->prepare('
        INSERT INTO device_pairings(user_id, code_hash, device_label, expires_at, created_at)
        VALUES(:user_id, :code_hash, :device_label, :expires_at, :created_at)
    ');
    $stmt->execute([
        ':user_id' => (string)$user['user_id'],
        ':code_hash' => gobao_token_hash(str_replace('-', '', $formatted)),
        ':device_label' => mb_substr(trim((string)($input['deviceLabel'] ?? 'New device')), 0, 100),
        ':expires_at' => $expiresAt,
        ':created_at' => gmdate('c'),
    ]);
    gobao_ok(['pairingCode' => $formatted, 'expiresAt' => $expiresAt]);
}

if ($action === 'devices') {
    $user = gobao_current_user(true);
    $stmt = $db->prepare('
        SELECT device_id, label, status, last_seen_at, paired_at, revoked_at, capabilities_json
        FROM local_devices
        WHERE user_id = :user_id
        ORDER BY updated_at DESC
        LIMIT 50
    ');
    $stmt->execute([':user_id' => (string)$user['user_id']]);
    $devices = array_map(static function (array $row): array {
        return [
            'deviceId' => (string)$row['device_id'],
            'label' => (string)$row['label'],
            'status' => (string)$row['status'],
            'lastSeenAt' => $row['last_seen_at'],
            'pairedAt' => $row['paired_at'],
            'revokedAt' => $row['revoked_at'],
            'capabilities' => json_decode((string)$row['capabilities_json'], true) ?: [],
        ];
    }, $stmt->fetchAll(PDO::FETCH_ASSOC) ?: []);
    gobao_ok(['devices' => $devices]);
}

if ($action === 'revoke_device') {
    $user = gobao_current_user(true);
    $deviceId = substr(preg_replace('/[^a-zA-Z0-9_.@-]+/', '_', trim((string)($input['deviceId'] ?? ''))) ?: '', 0, 96);
    if ($deviceId === '') {
        gobao_fail('deviceId is required.', 422);
    }
    $stmt = $db->prepare('
        UPDATE local_devices
        SET status = :status, revoked_at = :now, device_token_hash = NULL, updated_at = :now
        WHERE user_id = :user_id AND device_id = :device_id
    ');
    $stmt->execute([
        ':status' => 'revoked',
        ':now' => gmdate('c'),
        ':user_id' => (string)$user['user_id'],
        ':device_id' => $deviceId,
    ]);
    gobao_ok(['revoked' => $stmt->rowCount() > 0, 'deviceId' => $deviceId]);
}

gobao_fail('Unsupported auth action.', 400);
