<?php
declare(strict_types=1);

require_once __DIR__ . '/telegram_lib.php';

function mocker_telegram_invite_link_name(int $userId): string
{
    return 'u' . max(0, $userId);
}

function mocker_telegram_invite_settings_get(PDO $pdo): array
{
    $defaults = [
        'is_active' => false,
        'group_chat_id' => '',
        'coins_per_invite' => 50,
        'updated_at' => 0,
    ];
    try {
        $st = $pdo->query(
            'SELECT is_active, group_chat_id, coins_per_invite, updated_at FROM telegram_invite_settings WHERE id = 1 LIMIT 1'
        );
        $row = $st ? $st->fetch(PDO::FETCH_ASSOC) : false;
    } catch (Throwable $e) {
        return $defaults;
    }
    if (!$row) {
        return $defaults;
    }

    return [
        'is_active' => (int) ($row['is_active'] ?? 0) === 1,
        'group_chat_id' => trim((string) ($row['group_chat_id'] ?? '')),
        'coins_per_invite' => max(0, (int) ($row['coins_per_invite'] ?? 50)),
        'updated_at' => (int) ($row['updated_at'] ?? 0),
    ];
}

function mocker_telegram_invite_settings_save(PDO $pdo, array $patch): void
{
    $cur = mocker_telegram_invite_settings_get($pdo);
    $active = array_key_exists('is_active', $patch) ? (!empty($patch['is_active']) ? 1 : 0) : ($cur['is_active'] ? 1 : 0);
    $group = array_key_exists('group_chat_id', $patch)
        ? trim((string) $patch['group_chat_id'])
        : $cur['group_chat_id'];
    $coins = array_key_exists('coins_per_invite', $patch)
        ? max(1, min(100000, (int) $patch['coins_per_invite']))
        : $cur['coins_per_invite'];
    $now = time();
    $st = $pdo->prepare(
        'INSERT INTO telegram_invite_settings (id, is_active, group_chat_id, coins_per_invite, updated_at)
         VALUES (1, :a, :g, :c, :t)
         ON CONFLICT(id) DO UPDATE SET
            is_active = excluded.is_active,
            group_chat_id = excluded.group_chat_id,
            coins_per_invite = excluded.coins_per_invite,
            updated_at = excluded.updated_at'
    );
    $st->execute([
        ':a' => $active,
        ':g' => $group,
        ':c' => $coins,
        ':t' => $now,
    ]);
}

function mocker_telegram_invite_parse_user_id_from_link_name(?string $name): int
{
    $name = trim((string) $name);
    if ($name === '' || !preg_match('/^u(\d{1,9})$/', $name, $m)) {
        return 0;
    }

    return (int) $m[1];
}

function mocker_telegram_invite_create_link_for_user(PDO $pdo, int $userId): array
{
    if (!mocker_telegram_is_configured()) {
        return ['ok' => false, 'error' => 'telegram_config'];
    }
    $settings = mocker_telegram_invite_settings_get($pdo);
    if (!$settings['is_active']) {
        return ['ok' => false, 'error' => 'campaign_inactive'];
    }
    $groupChatId = $settings['group_chat_id'];
    if ($groupChatId === '') {
        return ['ok' => false, 'error' => 'group_not_set'];
    }

    $st = $pdo->prepare(
        'SELECT telegram_verified_at, telegram_user_id FROM users WHERE id = :id LIMIT 1'
    );
    $st->execute([':id' => $userId]);
    $user = $st->fetch(PDO::FETCH_ASSOC);
    if (!$user) {
        return ['ok' => false, 'error' => 'user'];
    }
    $verified = isset($user['telegram_verified_at']) ? (int) $user['telegram_verified_at'] : 0;
    if ($verified <= 0) {
        return ['ok' => false, 'error' => 'telegram_required'];
    }

    $existing = $pdo->prepare(
        'SELECT invite_link FROM telegram_invite_links
         WHERE user_id = :uid AND revoked_at IS NULL
         ORDER BY id DESC LIMIT 1'
    );
    $existing->execute([':uid' => $userId]);
    $row = $existing->fetch(PDO::FETCH_ASSOC);
    if ($row && !empty($row['invite_link'])) {
        return ['ok' => true, 'invite_link' => (string) $row['invite_link']];
    }

    $linkName = mocker_telegram_invite_link_name($userId);
    $payload = [
        'chat_id' => mocker_telegram_normalize_chat_id($groupChatId),
        'name' => $linkName,
        'creates_join_request' => false,
    ];
    $j = mocker_telegram_api_post_json('createChatInviteLink', $payload);
    if (!is_array($j) || empty($j['ok']) || !isset($j['result']) || !is_array($j['result'])) {
        $desc = mocker_telegram_error_description($j);

        return ['ok' => false, 'error' => 'telegram_api', 'description' => $desc];
    }
    $inviteLink = trim((string) ($j['result']['invite_link'] ?? ''));
    if ($inviteLink === '') {
        return ['ok' => false, 'error' => 'telegram_api'];
    }

    $now = time();
    $ins = $pdo->prepare(
        'INSERT INTO telegram_invite_links (user_id, invite_link, link_name, created_at)
         VALUES (:uid, :link, :name, :t)'
    );
    $ins->execute([
        ':uid' => $userId,
        ':link' => $inviteLink,
        ':name' => $linkName,
        ':t' => $now,
    ]);

    return ['ok' => true, 'invite_link' => $inviteLink];
}

function mocker_telegram_invite_handle_chat_member(PDO $pdo, array $chatMember): void
{
    $settings = mocker_telegram_invite_settings_get($pdo);
    if (!$settings['is_active'] || $settings['group_chat_id'] === '') {
        return;
    }

    $chat = $chatMember['chat'] ?? [];
    if (!is_array($chat)) {
        return;
    }
    $chatId = isset($chat['id']) ? (string) (int) $chat['id'] : '';
    $targetGroup = mocker_telegram_normalize_chat_id($settings['group_chat_id']);
    if ($chatId === '' || mocker_telegram_normalize_chat_id($chatId) !== $targetGroup) {
        return;
    }

    $newMember = $chatMember['new_chat_member'] ?? [];
    $oldMember = $chatMember['old_chat_member'] ?? [];
    if (!is_array($newMember) || !is_array($oldMember)) {
        return;
    }
    $newStatus = (string) ($newMember['status'] ?? '');
    $oldStatus = (string) ($oldMember['status'] ?? '');
    if ($newStatus !== 'member') {
        return;
    }
    if (!in_array($oldStatus, ['left', 'kicked', 'restricted', ''], true)) {
        return;
    }

    $inviteLinkObj = $chatMember['invite_link'] ?? null;
    if (!is_array($inviteLinkObj)) {
        return;
    }

    $inviterUserId = mocker_telegram_invite_parse_user_id_from_link_name(
        isset($inviteLinkObj['name']) ? (string) $inviteLinkObj['name'] : ''
    );
    if ($inviterUserId <= 0) {
        $linkUrl = trim((string) ($inviteLinkObj['invite_link'] ?? ''));
        if ($linkUrl !== '') {
            $lk = $pdo->prepare(
                'SELECT user_id FROM telegram_invite_links WHERE invite_link = :l ORDER BY id DESC LIMIT 1'
            );
            $lk->execute([':l' => $linkUrl]);
            $lr = $lk->fetch(PDO::FETCH_ASSOC);
            if ($lr) {
                $inviterUserId = (int) $lr['user_id'];
            }
        }
    }
    if ($inviterUserId <= 0) {
        return;
    }

    $joinUser = $newMember['user'] ?? [];
    if (!is_array($joinUser)) {
        return;
    }
    if (!empty($joinUser['is_bot'])) {
        return;
    }
    $inviteeTgId = isset($joinUser['id']) ? (int) $joinUser['id'] : 0;
    if ($inviteeTgId <= 0) {
        return;
    }

    $invSt = $pdo->prepare(
        'SELECT telegram_user_id FROM users WHERE id = :id LIMIT 1'
    );
    $invSt->execute([':id' => $inviterUserId]);
    $invRow = $invSt->fetch(PDO::FETCH_ASSOC);
    if (!$invRow) {
        return;
    }
    $inviterTgId = isset($invRow['telegram_user_id']) ? (int) $invRow['telegram_user_id'] : 0;
    if ($inviterTgId > 0 && $inviterTgId === $inviteeTgId) {
        return;
    }

    $inviteeUsername = isset($joinUser['username']) ? trim((string) $joinUser['username']) : '';
    if ($inviteeUsername === '' && isset($joinUser['first_name'])) {
        $inviteeUsername = trim((string) $joinUser['first_name']);
    }

    $coins = max(1, $settings['coins_per_invite']);
    $now = time();

    $pdo->beginTransaction();
    try {
        $dup = $pdo->prepare('SELECT id FROM telegram_invite_events WHERE invitee_telegram_user_id = :tid LIMIT 1');
        $dup->execute([':tid' => $inviteeTgId]);
        if ($dup->fetch()) {
            $pdo->rollBack();

            return;
        }

        $ins = $pdo->prepare(
            'INSERT INTO telegram_invite_events (inviter_user_id, invitee_telegram_user_id, invitee_username, coins_awarded, created_at)
             VALUES (:inv, :tid, :un, :c, :t)'
        );
        $ins->execute([
            ':inv' => $inviterUserId,
            ':tid' => $inviteeTgId,
            ':un' => $inviteeUsername !== '' ? $inviteeUsername : null,
            ':c' => $coins,
            ':t' => $now,
        ]);

        $upd = $pdo->prepare('UPDATE users SET coins = coins + :c WHERE id = :id');
        $upd->execute([':c' => $coins, ':id' => $inviterUserId]);

        $pdo->commit();
    } catch (Throwable $e) {
        if ($pdo->inTransaction()) {
            $pdo->rollBack();
        }
    }
}

function mocker_telegram_invite_user_stats(PDO $pdo, int $userId): array
{
    $st = $pdo->prepare(
        'SELECT COUNT(*) AS cnt, COALESCE(SUM(coins_awarded), 0) AS earned
         FROM telegram_invite_events WHERE inviter_user_id = :id'
    );
    $st->execute([':id' => $userId]);
    $row = $st->fetch(PDO::FETCH_ASSOC) ?: ['cnt' => 0, 'earned' => 0];

    $linkSt = $pdo->prepare(
        'SELECT invite_link FROM telegram_invite_links
         WHERE user_id = :id AND revoked_at IS NULL ORDER BY id DESC LIMIT 1'
    );
    $linkSt->execute([':id' => $userId]);
    $linkRow = $linkSt->fetch(PDO::FETCH_ASSOC);
    $link = $linkRow && !empty($linkRow['invite_link']) ? (string) $linkRow['invite_link'] : null;

    $rank = null;
    $rankSt = $pdo->prepare(
        'SELECT COUNT(*) + 1 AS r FROM (
            SELECT inviter_user_id, COUNT(*) AS c FROM telegram_invite_events
            GROUP BY inviter_user_id
            HAVING c > (
                SELECT COUNT(*) FROM telegram_invite_events WHERE inviter_user_id = :id
            )
         )'
    );
    $rankSt->execute([':id' => $userId]);
    $rankRow = $rankSt->fetch(PDO::FETCH_ASSOC);
    if ($rankRow) {
        $rank = (int) $rankRow['r'];
    }

    return [
        'invite_count' => (int) ($row['cnt'] ?? 0),
        'coins_earned' => (int) ($row['earned'] ?? 0),
        'invite_link' => $link,
        'rank' => $rank,
    ];
}

function mocker_telegram_invite_leaderboard(PDO $pdo, int $limit = 10): array
{
    $limit = max(1, min(50, $limit));
    $st = $pdo->prepare(
        'SELECT e.inviter_user_id AS user_id, u.username,
                COUNT(*) AS invite_count, COALESCE(SUM(e.coins_awarded), 0) AS coins_earned
         FROM telegram_invite_events e
         INNER JOIN users u ON u.id = e.inviter_user_id
         GROUP BY e.inviter_user_id
         ORDER BY invite_count DESC, coins_earned DESC, e.inviter_user_id ASC
         LIMIT ' . $limit
    );
    $st->execute();
    $out = [];
    foreach ($st->fetchAll(PDO::FETCH_ASSOC) as $r) {
        $out[] = [
            'userId' => (int) $r['user_id'],
            'username' => (string) $r['username'],
            'inviteCount' => (int) $r['invite_count'],
            'coinsEarned' => (int) $r['coins_earned'],
        ];
    }

    return $out;
}
