<?php
declare(strict_types=1);

const MOCKER_TELEGRAM_PENDING_EMAIL_DOMAIN = 'telegram.pending.local';

function mocker_is_telegram_pending_email(string $email): bool
{
    $email = strtolower(trim($email));

    return str_ends_with($email, '@' . MOCKER_TELEGRAM_PENDING_EMAIL_DOMAIN);
}

function mocker_normalize_phone(string $phone): ?string
{
    $digits = preg_replace('/\D+/', '', trim($phone));
    if ($digits === null || $digits === '') {
        return null;
    }
    if (strlen($digits) === 11 && $digits[0] === '0' && $digits[1] === '5') {
        $digits = substr($digits, 1);
    }
    if (strlen($digits) === 12 && str_starts_with($digits, '90') && $digits[2] === '5') {
        $digits = substr($digits, 2);
    }
    if (strlen($digits) === 10 && $digits[0] === '5') {
        return '+90' . $digits;
    }
    if (strlen($digits) >= 10 && strlen($digits) <= 15) {
        return '+' . $digits;
    }

    return null;
}

/**
 * @param array<string, mixed> $apiRow
 */
function mocker_extract_password_hash_from_api_row(array $apiRow): ?string
{
    foreach (['password_hash', 'password', 'pass', 'pwd', 'user_password'] as $key) {
        $raw = trim((string) ($apiRow[$key] ?? ''));
        if ($raw === '') {
            continue;
        }
        if (
            str_starts_with($raw, '$2y$')
            || str_starts_with($raw, '$2a$')
            || str_starts_with($raw, '$argon2')
        ) {
            return $raw;
        }
        if (strlen($raw) === 32 && ctype_xdigit($raw)) {
            return $raw;
        }
    }

    return null;
}

const MOCKER_MIGRATION_PASSWORD_PLAIN = 'Mocker2026!';

function mocker_shared_migration_password_hash(): string
{
    static $hash = null;
    if ($hash === null) {
        $hash = password_hash(MOCKER_MIGRATION_PASSWORD_PLAIN, PASSWORD_DEFAULT);
    }

    return $hash;
}

/** @deprecated yeni kayıtlar için ortak geçici şifre kullanılıyor */
function mocker_migration_initial_password_hash(string $username): string
{
    unset($username);

    return mocker_shared_migration_password_hash();
}

function mocker_password_verify_stored(string $password, string $stored): bool
{
    if ($password === '' || $stored === '') {
        return false;
    }
    if (password_verify($password, $stored)) {
        return true;
    }
    if (strlen($stored) === 32 && ctype_xdigit($stored)) {
        if (hash_equals(strtolower($stored), md5($password))) {
            return true;
        }
        if (hash_equals(strtolower($stored), md5($password, true))) {
            return true;
        }
    }
    if (hash_equals($stored, $password)) {
        return true;
    }

    return false;
}

function mocker_user_is_placeholder_password_hash(string $hash): bool
{
    return str_contains($hash, '__external_rx__') || str_starts_with($hash, '$2y$10$92IXUNpkjO0rOQ5byMi');
}

function mocker_user_auth_migrate(PDO $pdo): void
{
    require_once __DIR__ . '/external_players_sync_lib.php';
    mocker_external_players_migrate($pdo);

    $cols = $pdo->query('PRAGMA table_info(users)')->fetchAll(PDO::FETCH_ASSOC);
    $have = [];
    foreach ($cols as $c) {
        $have[(string) ($c['name'] ?? '')] = true;
    }
    if (!isset($have['must_change_password'])) {
        $pdo->exec('ALTER TABLE users ADD COLUMN must_change_password INTEGER NOT NULL DEFAULT 0');
    }
    if (!isset($have['password_imported'])) {
        $pdo->exec('ALTER TABLE users ADD COLUMN password_imported INTEGER NOT NULL DEFAULT 0');
    }
    if (!isset($have['twofa_enabled'])) {
        $pdo->exec('ALTER TABLE users ADD COLUMN twofa_enabled INTEGER NOT NULL DEFAULT 0');
    }
    if (!isset($have['twofa_secret'])) {
        $pdo->exec('ALTER TABLE users ADD COLUMN twofa_secret TEXT');
    }
    if (!isset($have['twofa_backup_codes_json'])) {
        $pdo->exec('ALTER TABLE users ADD COLUMN twofa_backup_codes_json TEXT');
    }
    if (isset($have['external_player_id'])) {
        if (mocker_app_meta_get($pdo, 'backfill_must_change_password_v1') !== '1') {
            $pdo->exec(
                'UPDATE users SET must_change_password = 1
                 WHERE external_player_id IS NOT NULL AND external_player_id > 0
                   AND COALESCE(password_imported, 0) = 0
                   AND COALESCE(must_change_password, 0) = 0'
            );
            mocker_app_meta_set($pdo, 'backfill_must_change_password_v1', '1');
        }
        $pdo->exec(
            'UPDATE users SET must_change_password = 0
             WHERE COALESCE(password_imported, 0) = 1 AND COALESCE(must_change_password, 0) = 1'
        );
    }
}

function mocker_user_is_external_import(array $row): bool
{
    return (int) ($row['external_player_id'] ?? 0) > 0;
}

function mocker_user_needs_password_change(array $row): bool
{
    return (int) ($row['must_change_password'] ?? 0) === 1;
}

/**
 * Şifre yenilemesi bekleyen üyeler: kullanıcı adı + herhangi bir şifre ile giriş.
 * Diğer üyeler: veritabanındaki hash ile doğrulama.
 */
function mocker_user_verify_credentials(PDO $pdo, array $row, string $identity, string $password): bool
{
    unset($pdo, $identity);

    if (mocker_user_needs_password_change($row)) {
        return trim($password) !== '';
    }

    return mocker_password_verify_stored($password, (string) ($row['password_hash'] ?? ''));
}

function mocker_user_needs_profile_completion(array $row): bool
{
    $completed = isset($row['profile_completed_at']) ? (int) $row['profile_completed_at'] : 0;
    if ($completed > 0) {
        return false;
    }

    return mocker_is_telegram_pending_email((string) ($row['email'] ?? ''));
}

function mocker_telegram_pending_email(int $telegramUserId): string
{
    return 'tg_' . $telegramUserId . '@' . MOCKER_TELEGRAM_PENDING_EMAIL_DOMAIN;
}

function mocker_generate_username_from_telegram(PDO $pdo, ?string $tgUsername, int $tgUserId, ?string $firstName = null): string
{
    $candidates = [];
    if ($tgUsername !== null && $tgUsername !== '') {
        $candidates[] = preg_replace('/[^a-zA-Z0-9_]/', '', $tgUsername) ?: '';
    }
    if ($firstName !== null && $firstName !== '') {
        $candidates[] = preg_replace('/[^a-zA-Z0-9_]/', '', $firstName) ?: '';
    }
    $candidates[] = 'tg' . $tgUserId;

    foreach ($candidates as $raw) {
        $base = strtolower(trim((string) $raw));
        if (strlen($base) < 2) {
            continue;
        }
        $base = substr($base, 0, 64);
        $uname = $base;
        for ($i = 0; $i < 50; $i++) {
            $st = $pdo->prepare('SELECT 1 FROM users WHERE lower(username) = lower(:u) LIMIT 1');
            $st->execute([':u' => $uname]);
            if (!$st->fetch()) {
                return $uname;
            }
            $suffix = $i === 0 ? 2 : $i + 2;
            $maxBase = max(2, 64 - strlen((string) $suffix) - 1);
            $uname = substr($base, 0, $maxBase) . '_' . $suffix;
        }
    }

    return 'tg' . $tgUserId;
}

function mocker_user_auth_payload(PDO $pdo, array $row): array
{
    unset($pdo);
    $needsProfile = mocker_user_needs_profile_completion($row);
    $needsPassword = mocker_user_needs_password_change($row);

    return [
        'id' => (int) $row['id'],
        'username' => (string) $row['username'],
        'coins' => (int) ($row['coins'] ?? 0),
        'needsProfileCompletion' => $needsProfile,
        'needsPasswordChange' => $needsPassword,
        'twofaEnabled' => (int) ($row['twofa_enabled'] ?? 0) === 1,
    ];
}

function mocker_user_select_auth_columns(): string
{
    return 'id, username, email, password_hash, coins, profile_completed_at, COALESCE(must_change_password, 0) AS must_change_password, COALESCE(password_imported, 0) AS password_imported, external_player_id, COALESCE(is_active, 1) AS is_active, COALESCE(twofa_enabled, 0) AS twofa_enabled, twofa_secret, twofa_backup_codes_json';
}
