<?php

declare(strict_types=1);

function mocker_migrate_tickets(PDO $pdo): void
{
    $pdo->exec(
        'CREATE TABLE IF NOT EXISTS ticket_events (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            title TEXT NOT NULL,
            description TEXT NOT NULL DEFAULT \'\',
            sponsor_title TEXT NOT NULL DEFAULT \'\',
            logo_url TEXT NOT NULL DEFAULT \'\',
            site_url TEXT NOT NULL DEFAULT \'\',
            prize_description TEXT NOT NULL DEFAULT \'\',
            winner_count INTEGER NOT NULL DEFAULT 1,
            ticket_cost INTEGER NOT NULL DEFAULT 0,
            max_entries_per_user INTEGER NOT NULL DEFAULT 20,
            status TEXT NOT NULL DEFAULT \'open\',
            ends_at INTEGER,
            created_at INTEGER NOT NULL,
            drawn_at INTEGER
        )'
    );
    $pdo->exec('CREATE INDEX IF NOT EXISTS idx_ticket_events_status ON ticket_events(status, ends_at DESC)');

    $pdo->exec(
        'CREATE TABLE IF NOT EXISTS ticket_entries (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            event_id INTEGER NOT NULL,
            user_id INTEGER NOT NULL,
            ticket_code TEXT NOT NULL,
            is_winner INTEGER NOT NULL DEFAULT 0,
            created_at INTEGER NOT NULL,
            FOREIGN KEY (event_id) REFERENCES ticket_events(id) ON DELETE CASCADE,
            FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
        )'
    );
    $pdo->exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_ticket_entries_code ON ticket_entries(ticket_code)');
    $pdo->exec('CREATE INDEX IF NOT EXISTS idx_ticket_entries_event ON ticket_entries(event_id, created_at)');
    $pdo->exec('CREATE INDEX IF NOT EXISTS idx_ticket_entries_user ON ticket_entries(event_id, user_id)');

    mocker_ticket_migrate_event_columns($pdo);
    mocker_ticket_migrate_category($pdo);
    mocker_ticket_migrate_sponsor_id($pdo);
    mocker_ticket_migrate_requests($pdo);
}

function mocker_ticket_migrate_sponsor_id(PDO $pdo): void
{
    $cols = $pdo->query('PRAGMA table_info(ticket_events)')->fetchAll(PDO::FETCH_ASSOC);
    $names = array_column($cols, 'name');
    if (!in_array('sponsor_id', $names, true)) {
        $pdo->exec("ALTER TABLE ticket_events ADD COLUMN sponsor_id TEXT NOT NULL DEFAULT ''");
    }
}

function mocker_sponsors_load(): array
{
    static $cache = null;
    if ($cache !== null) {
        return $cache;
    }
    $path = dirname(__DIR__) . '/data/sponsors.json';
    if (!is_file($path)) {
        $cache = [];
        return $cache;
    }
    $raw = (string) file_get_contents($path);
    if (str_starts_with($raw, "\xEF\xBB\xBF")) {
        $raw = substr($raw, 3);
    }
    $decoded = json_decode($raw, true);
    $cache = is_array($decoded) ? $decoded : [];
    return $cache;
}

function mocker_sponsor_by_id(string $id): ?array
{
    $id = trim($id);
    if ($id === '') {
        return null;
    }
    foreach (mocker_sponsors_load() as $row) {
        if (!is_array($row)) {
            continue;
        }
        if ((string) ($row['id'] ?? '') === $id) {
            return $row;
        }
    }
    return null;
}

function mocker_sponsors_admin_options(): array
{
    $out = [];
    foreach (mocker_sponsors_load() as $row) {
        if (!is_array($row)) {
            continue;
        }
        $id = trim((string) ($row['id'] ?? ''));
        if ($id === '') {
            continue;
        }
        $out[] = [
            'id' => $id,
            'title' => trim((string) ($row['title'] ?? '')),
            'logo' => trim((string) ($row['logo'] ?? '')),
            'url' => trim((string) ($row['url'] ?? '')),
        ];
    }
    return $out;
}

function mocker_ticket_resolve_sponsor_fields(array $input): array
{
    $sponsorId = trim((string) ($input['sponsorId'] ?? ''));
    $sponsorTitle = trim((string) ($input['sponsorTitle'] ?? ''));
    $siteUrl = trim((string) ($input['siteUrl'] ?? ''));
    if ($sponsorId !== '') {
        $sp = mocker_sponsor_by_id($sponsorId);
        if ($sp) {
            $title = trim((string) ($sp['title'] ?? ''));
            if ($title !== '') {
                $sponsorTitle = $title;
            }
            if ($siteUrl === '') {
                $siteUrl = trim((string) ($sp['url'] ?? ''));
            }
        } else {
            $sponsorId = '';
        }
    }
    return [
        'sponsorId' => $sponsorId,
        'sponsorTitle' => $sponsorTitle,
        'siteUrl' => $siteUrl,
    ];
}

function mocker_ticket_event_sponsor_logo(string $sponsorId, string $sponsorTitle): string
{
    if ($sponsorId !== '') {
        $sp = mocker_sponsor_by_id($sponsorId);
        if ($sp) {
            $logo = trim((string) ($sp['logo'] ?? ''));
            if ($logo !== '') {
                return $logo;
            }
        }
    }
    $titleNorm = mocker_utf8_lower(trim($sponsorTitle));
    if ($titleNorm === '') {
        return '';
    }
    foreach (mocker_sponsors_load() as $row) {
        if (!is_array($row)) {
            continue;
        }
        $rowTitle = mocker_utf8_lower(trim((string) ($row['title'] ?? '')));
        if ($rowTitle !== '' && $rowTitle === $titleNorm) {
            return trim((string) ($row['logo'] ?? ''));
        }
    }
    return '';
}

function mocker_ticket_delete_event(PDO $pdo, int $eventId): array
{
    if ($eventId <= 0) {
        return ['ok' => false, 'error' => 'event'];
    }
    $st = $pdo->prepare('SELECT id FROM ticket_events WHERE id = :id LIMIT 1');
    $st->execute([':id' => $eventId]);
    if (!$st->fetch(PDO::FETCH_ASSOC)) {
        return ['ok' => false, 'error' => 'not_found'];
    }
    $pdo->prepare('DELETE FROM ticket_requests WHERE event_id = :id')->execute([':id' => $eventId]);
    $pdo->prepare('DELETE FROM ticket_entries WHERE event_id = :id')->execute([':id' => $eventId]);
    $pdo->prepare('DELETE FROM ticket_events WHERE id = :id')->execute([':id' => $eventId]);

    return ['ok' => true];
}

function mocker_ticket_migrate_category(PDO $pdo): void
{
    $cols = $pdo->query('PRAGMA table_info(ticket_events)')->fetchAll(PDO::FETCH_ASSOC);
    $names = array_column($cols, 'name');
    if (!in_array('category', $names, true)) {
        $pdo->exec("ALTER TABLE ticket_events ADD COLUMN category TEXT NOT NULL DEFAULT 'ticket'");
        $pdo->exec('CREATE INDEX IF NOT EXISTS idx_ticket_events_category ON ticket_events(category, status, ends_at DESC)');
    }
}

function mocker_ticket_migrate_event_columns(PDO $pdo): void
{
    $cols = $pdo->query('PRAGMA table_info(ticket_events)')->fetchAll(PDO::FETCH_ASSOC);
    $names = array_column($cols, 'name');
    if (!in_array('max_total_tickets', $names, true)) {
        $pdo->exec('ALTER TABLE ticket_events ADD COLUMN max_total_tickets INTEGER NOT NULL DEFAULT 0');
    }
    if (!in_array('investment_per_ticket', $names, true)) {
        $pdo->exec('ALTER TABLE ticket_events ADD COLUMN investment_per_ticket REAL NOT NULL DEFAULT 1000');
    }
}

function mocker_ticket_migrate_requests(PDO $pdo): void
{
    $pdo->exec(
        'CREATE TABLE IF NOT EXISTS ticket_requests (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            event_id INTEGER NOT NULL,
            user_id INTEGER NOT NULL,
            username TEXT NOT NULL DEFAULT \'\',
            site_username TEXT NOT NULL DEFAULT \'\',
            investment_amount REAL NOT NULL DEFAULT 0,
            investment_at INTEGER,
            ticket_ref TEXT NOT NULL DEFAULT \'\',
            tickets_eligible INTEGER NOT NULL DEFAULT 1,
            tickets_granted INTEGER NOT NULL DEFAULT 0,
            status TEXT NOT NULL DEFAULT \'pending\',
            admin_note TEXT NOT NULL DEFAULT \'\',
            created_at INTEGER NOT NULL,
            reviewed_at INTEGER,
            FOREIGN KEY (event_id) REFERENCES ticket_events(id) ON DELETE CASCADE,
            FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
        )'
    );
    $pdo->exec('CREATE INDEX IF NOT EXISTS idx_ticket_requests_event_status ON ticket_requests(event_id, status, created_at DESC)');
    $pdo->exec('CREATE INDEX IF NOT EXISTS idx_ticket_requests_user ON ticket_requests(event_id, user_id)');
}

/**
 * @return int Bilet adedi (en az 1)
 */
function mocker_ticket_calc_eligible(float $investmentAmount, float $perTicket): int
{
    if ($perTicket <= 0) {
        return max(1, (int) floor($investmentAmount / 1000) ?: 1);
    }
    return max(1, (int) floor($investmentAmount / $perTicket));
}

/**
 * @return array{ok:bool, granted?:int, codes?:list<string>, error?:string}
 */
function mocker_ticket_grant_entries_for_request(PDO $pdo, int $requestId, ?int $countOverride = null): array
{
    $st = $pdo->prepare(
        'SELECT r.*, e.status AS event_status, e.max_total_tickets, e.investment_per_ticket
         FROM ticket_requests r
         INNER JOIN ticket_events e ON e.id = r.event_id
         WHERE r.id = :id LIMIT 1'
    );
    $st->execute([':id' => $requestId]);
    $req = $st->fetch(PDO::FETCH_ASSOC);
    if (!$req) {
        return ['ok' => false, 'error' => 'not_found'];
    }
    if ((string) $req['status'] === 'approved') {
        return ['ok' => false, 'error' => 'already_approved'];
    }
    if ((string) $req['event_status'] === 'drawn') {
        return ['ok' => false, 'error' => 'drawn'];
    }

    $eventId = (int) $req['event_id'];
    $uid = (int) $req['user_id'];
    $want = $countOverride !== null && $countOverride > 0
        ? $countOverride
        : max(1, (int) $req['tickets_eligible']);

    $cap = (int) $req['max_total_tickets'];
    if ($cap > 0) {
        $cntSt = $pdo->prepare('SELECT COUNT(*) FROM ticket_entries WHERE event_id = :eid');
        $cntSt->execute([':eid' => $eventId]);
        $current = (int) $cntSt->fetchColumn();
        if ($current + $want > $cap) {
            $want = max(0, $cap - $current);
            if ($want <= 0) {
                return ['ok' => false, 'error' => 'capacity'];
            }
        }
    }

    $maxPerUser = 100;
    $evSt = $pdo->prepare('SELECT max_entries_per_user FROM ticket_events WHERE id = :id');
    $evSt->execute([':id' => $eventId]);
    $evRow = $evSt->fetch(PDO::FETCH_ASSOC);
    if ($evRow) {
        $maxPerUser = max(1, min(100, (int) $evRow['max_entries_per_user']));
    }
    $userCntSt = $pdo->prepare('SELECT COUNT(*) FROM ticket_entries WHERE event_id = :eid AND user_id = :uid');
    $userCntSt->execute([':eid' => $eventId, ':uid' => $uid]);
    $userHave = (int) $userCntSt->fetchColumn();
    if ($userHave + $want > $maxPerUser) {
        $want = max(0, $maxPerUser - $userHave);
        if ($want <= 0) {
            return ['ok' => false, 'error' => 'user_limit'];
        }
    }

    $now = time();
    $codes = [];
    $ins = $pdo->prepare(
        'INSERT INTO ticket_entries (event_id, user_id, ticket_code, is_winner, created_at) VALUES (:eid, :uid, :code, 0, :t)'
    );
    for ($i = 0; $i < $want; $i++) {
        $code = mocker_ticket_generate_code($pdo);
        $ins->execute([':eid' => $eventId, ':uid' => $uid, ':code' => $code, ':t' => $now]);
        $codes[] = $code;
    }

    $pdo->prepare(
        'UPDATE ticket_requests SET status = \'approved\', tickets_granted = :g, reviewed_at = :t WHERE id = :id'
    )->execute([':g' => $want, ':t' => $now, ':id' => $requestId]);

    return ['ok' => true, 'granted' => $want, 'codes' => $codes];
}

function mocker_ticket_generate_code(PDO $pdo): string
{
    $alphabet = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
    for ($attempt = 0; $attempt < 40; $attempt++) {
        $raw = '';
        for ($i = 0; $i < 8; $i++) {
            $raw .= $alphabet[random_int(0, strlen($alphabet) - 1)];
        }
        $code = 'ALP-' . $raw;
        $st = $pdo->prepare('SELECT 1 FROM ticket_entries WHERE ticket_code = :c LIMIT 1');
        $st->execute([':c' => $code]);
        if (!$st->fetch()) {
            return $code;
        }
    }

    return 'ALP-' . strtoupper(bin2hex(random_bytes(4)));
}

/**
 * @return array{ok:bool, winners?:list<array>, error?:string, winnerCount?:int}
 */
function mocker_ticket_draw_winners(PDO $pdo, int $eventId, int $winnerCount): array
{
    $st = $pdo->prepare('SELECT id, status, winner_count FROM ticket_events WHERE id = :id LIMIT 1');
    $st->execute([':id' => $eventId]);
    $ev = $st->fetch(PDO::FETCH_ASSOC);
    if (!$ev) {
        return ['ok' => false, 'error' => 'not_found'];
    }
    if ((string) $ev['status'] === 'drawn') {
        return ['ok' => false, 'error' => 'already_drawn'];
    }

    $want = $winnerCount > 0 ? $winnerCount : (int) $ev['winner_count'];
    $want = max(1, min(500, $want));

    $st = $pdo->prepare(
        'SELECT e.id, e.user_id, e.ticket_code, u.username
         FROM ticket_entries e
         INNER JOIN users u ON u.id = e.user_id
         WHERE e.event_id = :eid AND e.is_winner = 0
         ORDER BY e.id ASC'
    );
    $st->execute([':eid' => $eventId]);
    $pool = $st->fetchAll(PDO::FETCH_ASSOC);
    if (!$pool) {
        return ['ok' => false, 'error' => 'no_entries'];
    }

    $pick = min($want, count($pool));
    $keys = array_keys($pool);
    for ($i = count($keys) - 1; $i > 0; $i--) {
        $j = random_int(0, $i);
        $tmp = $keys[$i];
        $keys[$i] = $keys[$j];
        $keys[$j] = $tmp;
    }
    $chosenKeys = array_slice($keys, 0, $pick);
    $winners = [];
    $upd = $pdo->prepare('UPDATE ticket_entries SET is_winner = 1 WHERE id = :id');
    foreach ($chosenKeys as $k) {
        $row = $pool[$k];
        $upd->execute([':id' => (int) $row['id']]);
        $winners[] = [
            'entryId' => (int) $row['id'],
            'userId' => (int) $row['user_id'],
            'username' => (string) $row['username'],
            'ticketCode' => (string) $row['ticket_code'],
        ];
    }

    $now = time();
    $pdo->prepare('UPDATE ticket_events SET status = \'drawn\', drawn_at = :t WHERE id = :id')
        ->execute([':t' => $now, ':id' => $eventId]);

    return ['ok' => true, 'winners' => $winners, 'winnerCount' => $pick];
}

function mocker_ticket_event_row_to_public(array $r, int $participantCount, array $winners = [], array $myEntries = []): array
{
    $sponsorId = (string) ($r['sponsor_id'] ?? '');
    $sponsorTitle = (string) $r['sponsor_title'];
    return [
        'id' => (int) $r['id'],
        'title' => (string) $r['title'],
        'description' => (string) $r['description'],
        'sponsorId' => $sponsorId,
        'sponsorTitle' => $sponsorTitle,
        'sponsorLogo' => mocker_ticket_event_sponsor_logo($sponsorId, $sponsorTitle),
        'logoUrl' => (string) $r['logo_url'],
        'siteUrl' => (string) $r['site_url'],
        'prizeDescription' => (string) $r['prize_description'],
        'winnerCount' => (int) $r['winner_count'],
        'ticketCost' => (int) $r['ticket_cost'],
        'maxEntriesPerUser' => (int) $r['max_entries_per_user'],
        'status' => (string) $r['status'],
        'endsAt' => $r['ends_at'] !== null ? (int) $r['ends_at'] : null,
        'createdAt' => isset($r['created_at']) ? (int) $r['created_at'] : null,
        'drawnAt' => $r['drawn_at'] !== null ? (int) $r['drawn_at'] : null,
        'participantCount' => $participantCount,
        'winners' => $winners,
        'myEntries' => $myEntries,
    ];
}
