<?php

declare(strict_types=1);

header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-store');
header('X-Content-Type-Options: nosniff');

const MAX_REQUEST_SIZE = 16384;

function respond(int $statusCode, array $body): void
{
    http_response_code($statusCode);
    echo json_encode($body, JSON_UNESCAPED_SLASHES);
    exit;
}

function clean_text(mixed $value): string
{
    $cleaned = preg_replace('/\s+/u', ' ', trim((string) $value));
    return $cleaned === null ? '' : $cleaned;
}

function malaysia_phone_digits(mixed $value): string
{
    $digits = preg_replace('/\D+/', '', (string) $value) ?? '';
    return substr($digits, 0, 2) === '60' ? '0' . substr($digits, 2) : $digits;
}

function telegram_html(string $value): string
{
    return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

function load_invite_registry(string $path): array
{
    if (!is_file($path)) {
        return [];
    }
    $registry = require $path;
    return is_array($registry) ? $registry : [];
}

function save_invite_registry(string $path, array $registry): bool
{
    $contents = "<?php\n\ndeclare(strict_types=1);\n\nreturn "
        . var_export($registry, true)
        . ";\n";
    return file_put_contents($path, $contents, LOCK_EX) !== false;
}

if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
    header('Allow: POST');
    respond(405, ['ok' => false, 'message' => 'Method not allowed.']);
}

$rawBody = file_get_contents('php://input');
if ($rawBody === false || strlen($rawBody) > MAX_REQUEST_SIZE) {
    respond(413, ['ok' => false, 'message' => 'Request is too large.']);
}

$payload = json_decode($rawBody, true);
if (!is_array($payload)) {
    respond(400, ['ok' => false, 'message' => 'Invalid JSON request.']);
}

$event = clean_text($payload['event'] ?? 'daily_results');
$name = clean_text($payload['name'] ?? '');
$phone = clean_text($payload['phone'] ?? '');
$memberUsername = clean_text($payload['memberUsername'] ?? '');
$rawPhoneDigits = preg_replace('/\D+/', '', $phone) ?? '';
$phoneDigits = substr($rawPhoneDigits, 0, 2) === '60'
    ? '0' . substr($rawPhoneDigits, 2)
    : $rawPhoneDigits;
$inviteCode = strtoupper(clean_text($payload['inviteCode'] ?? ''));
$prizeLabels = [
    'jackpot' => 'FREE RM10',
    'big' => 'FREE RM9',
    'lucky' => 'FREE RM8',
    'none' => 'No prize',
];

if (strlen($name) < 2 || strlen($name) > 120) {
    respond(400, ['ok' => false, 'message' => 'Invalid player name.']);
}
if (
    preg_match('/^[+0-9() .-]+$/', $phone) !== 1
    || (substr($phone, 0, 1) === '+' && substr($rawPhoneDigits, 0, 2) !== '60')
    || preg_match('/^01(?:0|1|2|3|4|6|7|8|9)\d{6,8}$/', $phoneDigits) !== 1
) {
    respond(400, ['ok' => false, 'message' => 'Invalid Malaysian mobile number.']);
}
if ($inviteCode !== '' && preg_match('/^[A-Z0-9]{6,16}$/', $inviteCode) !== 1) {
    respond(400, ['ok' => false, 'message' => 'Invalid invite code.']);
}
if (!in_array(
    $event,
    ['daily_results', 'referral_used', 'invite_registered', 'claim_invite_bonus'],
    true
)) {
    respond(400, ['ok' => false, 'message' => 'Invalid Telegram event.']);
}
if (
    in_array($event, ['referral_used', 'invite_registered', 'claim_invite_bonus'], true)
    && $inviteCode === ''
) {
    respond(400, ['ok' => false, 'message' => 'Invite code is required.']);
}

$results = [];
if ($event === 'daily_results') {
    $rawResults = $payload['results'] ?? [];
    if (!is_array($rawResults) || count($rawResults) < 1 || count($rawResults) > 10) {
        respond(400, ['ok' => false, 'message' => 'Invalid daily roll results.']);
    }

    foreach ($rawResults as $rawResult) {
        if (!is_array($rawResult)) {
            respond(400, ['ok' => false, 'message' => 'Invalid dice result.']);
        }
        $dice = $rawResult['dice'] ?? [];
        $prize = (string) ($rawResult['prize'] ?? '');
        if (
            !is_array($dice)
            || count($dice) !== 2
            || filter_var($dice[0], FILTER_VALIDATE_INT) === false
            || filter_var($dice[1], FILTER_VALIDATE_INT) === false
            || (int) $dice[0] < 1
            || (int) $dice[0] > 6
            || (int) $dice[1] < 1
            || (int) $dice[1] > 6
        ) {
            respond(400, ['ok' => false, 'message' => 'Invalid dice result.']);
        }
        if (!array_key_exists($prize, $prizeLabels)) {
            respond(400, ['ok' => false, 'message' => 'Invalid prize result.']);
        }

        $firstDie = (int) $dice[0];
        $secondDie = (int) $dice[1];
        $results[] = [
            'dice' => [$firstDie, $secondDie],
            'total' => $firstDie + $secondDie,
            'prize' => $prize,
        ];
    }
}

$registryPath = __DIR__ . DIRECTORY_SEPARATOR . 'invite-registry.php';
$inviteRegistry = load_invite_registry($registryPath);

if ($event === 'invite_registered') {
    $existingInvite = is_array($inviteRegistry[$inviteCode] ?? null)
        ? $inviteRegistry[$inviteCode]
        : [];
    $inviteRegistry[$inviteCode] = [
        'name' => $name,
        'phone' => $phone,
        'updatedAt' => gmdate('c'),
        'bonusRolls' => max(0, (int) ($existingInvite['bonusRolls'] ?? 0)),
        'successfulInviteHashes' =>
            is_array($existingInvite['successfulInviteHashes'] ?? null)
                ? $existingInvite['successfulInviteHashes']
                : [],
    ];

    if (count($inviteRegistry) > 500) {
        uasort($inviteRegistry, static function (array $first, array $second): int {
            return strcmp((string) ($first['updatedAt'] ?? ''), (string) ($second['updatedAt'] ?? ''));
        });
        $inviteRegistry = array_slice($inviteRegistry, -500, null, true);
    }

    if (!save_invite_registry($registryPath, $inviteRegistry)) {
        respond(500, ['ok' => false, 'message' => 'Could not save the invite owner.']);
    }
    respond(200, ['ok' => true]);
}

$inviter = is_array($inviteRegistry[$inviteCode] ?? null)
    ? $inviteRegistry[$inviteCode]
    : [];

if ($event === 'claim_invite_bonus') {
    $ownerMatches = $inviter !== []
        && malaysia_phone_digits($inviter['phone'] ?? '') === $phoneDigits;
    $pendingBonusRolls = $ownerMatches ? max(0, (int) ($inviter['bonusRolls'] ?? 0)) : 0;
    $bonusRolls = $pendingBonusRolls > 0 ? 1 : 0;

    if ($ownerMatches && $bonusRolls > 0) {
        $inviteRegistry[$inviteCode]['bonusRolls'] = $pendingBonusRolls - 1;
        $inviteRegistry[$inviteCode]['updatedAt'] = gmdate('c');
        if (!save_invite_registry($registryPath, $inviteRegistry)) {
            respond(500, ['ok' => false, 'message' => 'Could not claim the invite reward.']);
        }
    }
    respond(200, ['ok' => true, 'bonusRolls' => $bonusRolls]);
}

$inviteBonusAwarded = false;
if ($event === 'referral_used' && $inviter !== []) {
    $inviterPhoneDigits = malaysia_phone_digits($inviter['phone'] ?? '');
    $successfulInviteHashes =
        is_array($inviter['successfulInviteHashes'] ?? null)
            ? $inviter['successfulInviteHashes']
            : [];
    $inviteeHash = hash('sha256', $phoneDigits);
    $isSelfReferral = $inviterPhoneDigits !== '' && $inviterPhoneDigits === $phoneDigits;
    $alreadyRewarded = in_array($inviteeHash, $successfulInviteHashes, true);

    if (!$isSelfReferral && !$alreadyRewarded) {
        $successfulInviteHashes[] = $inviteeHash;
        $inviteRegistry[$inviteCode]['successfulInviteHashes'] =
            array_slice($successfulInviteHashes, -500);
        $inviteRegistry[$inviteCode]['bonusRolls'] =
            max(0, (int) ($inviter['bonusRolls'] ?? 0)) + 1;
        $inviteRegistry[$inviteCode]['updatedAt'] = gmdate('c');
        if (!save_invite_registry($registryPath, $inviteRegistry)) {
            respond(500, ['ok' => false, 'message' => 'Could not save the invite reward.']);
        }
        $inviter = $inviteRegistry[$inviteCode];
        $inviteBonusAwarded = true;
    }
}

$localConfig = [];
$configPath = __DIR__ . DIRECTORY_SEPARATOR . 'telegram-config.php';
if (is_file($configPath)) {
    $loadedConfig = require $configPath;
    if (is_array($loadedConfig)) {
        $localConfig = $loadedConfig;
    }
}

$botToken = (string) (getenv('TELEGRAM_BOT_TOKEN') ?: ($localConfig['bot_token'] ?? ''));
$chatId = (string) (getenv('TELEGRAM_CHAT_ID') ?: ($localConfig['chat_id'] ?? ''));

if ($botToken === '' || $chatId === '') {
    respond(503, ['ok' => false, 'message' => 'Telegram is not configured on the server.']);
}
if (preg_match('/^\d+:[A-Za-z0-9_-]+$/', $botToken) !== 1) {
    respond(503, ['ok' => false, 'message' => 'The Telegram bot token format is invalid.']);
}

$timeZone = new DateTimeZone('Asia/Kuala_Lumpur');
$createdAt = new DateTimeImmutable('now', $timeZone);

if ($event === 'referral_used') {
    $messageLines = [
        '<b>Game : Dice Roll</b>',
        '',
        '🔗 <b>Invite Link Used</b>',
        '',
        '🎟 <b>Inviter code:</b> ' . telegram_html($inviteCode),
        '🙋 <b>Inviter username:</b> '
            . telegram_html(clean_text($inviter['name'] ?? 'Not registered')),
        '☎️ <b>Inviter phone:</b> '
            . telegram_html(clean_text($inviter['phone'] ?? 'Not registered')),
        '',
        '👤 <b>New player:</b> ' . telegram_html($name),
        '📱 <b>New player phone:</b> ' . telegram_html($phone),
        '✅ <b>Action:</b> Completed a roll from the invite link',
        $inviteBonusAwarded
            ? '🎁 <b>Inviter reward:</b> +1 extra roll chance'
            : '🎁 <b>Inviter reward:</b> No new bonus (already counted, self-referral, or unregistered)',
        '🕒 <b>Time:</b> ' . telegram_html($createdAt->format('d M Y, h:i:s A')),
    ];
} else {
    $rollCount = count($results);
    $messageLines = [
        '<b>Game : Dice Roll</b>',
        '',
        "🎲 <b>Daily Dice Results ({$rollCount} Rolls)</b>",
        '',
        '👤 <b>Player:</b> ' . telegram_html($name),
        '📱 <b>Phone:</b> ' . telegram_html($phone),
        '🪪 <b>Member username:</b> ' . telegram_html($memberUsername !== '' ? $memberUsername : 'Not provided'),
    ];
    if ($inviteCode !== '') {
        $messageLines[] = '🎟 <b>Referred by:</b> ' . telegram_html($inviteCode);
    }

    foreach ($results as $index => $result) {
        $rollNumber = $index + 1;
        $firstDie = $result['dice'][0];
        $secondDie = $result['dice'][1];
        $total = $result['total'];
        $messageLines[] = '';
        $messageLines[] = "🎯 <b>Roll {$rollNumber}:</b> {$firstDie} + {$secondDie} = {$total}";
        $messageLines[] =
            '🏆 <b>Result:</b> ' . telegram_html($prizeLabels[$result['prize']]);
    }
    $messageLines[] = '';
    $messageLines[] =
        '🕒 <b>Time:</b> ' . telegram_html($createdAt->format('d M Y, h:i:s A'));
}

$message = implode("\n", $messageLines);

$telegramPayload = json_encode([
    'chat_id' => $chatId,
    'text' => $message,
    'parse_mode' => 'HTML',
    'disable_web_page_preview' => true,
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);

if ($telegramPayload === false) {
    respond(500, ['ok' => false, 'message' => 'Could not prepare the Telegram message.']);
}

$telegramUrl = 'https://api.telegram.org/bot' . $botToken . '/sendMessage';
$telegramResponse = false;
$transportError = '';

if (function_exists('curl_init')) {
    $curl = curl_init($telegramUrl);
    curl_setopt_array($curl, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CONNECTTIMEOUT => 5,
        CURLOPT_TIMEOUT => 10,
        CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
        CURLOPT_POSTFIELDS => $telegramPayload,
    ]);
    $telegramResponse = curl_exec($curl);
    if ($telegramResponse === false) {
        $transportError = clean_text(curl_error($curl));
    }
    curl_close($curl);
} elseif (filter_var(ini_get('allow_url_fopen'), FILTER_VALIDATE_BOOLEAN)) {
    $context = stream_context_create([
        'http' => [
            'method' => 'POST',
            'header' => "Content-Type: application/json\r\n",
            'content' => $telegramPayload,
            'timeout' => 10,
            'ignore_errors' => true,
        ],
    ]);
    $telegramResponse = @file_get_contents($telegramUrl, false, $context);
} else {
    $transportError = 'This PHP server does not allow outgoing HTTPS requests.';
}

$telegramResult = is_string($telegramResponse) ? json_decode($telegramResponse, true) : null;
if (!is_array($telegramResult) || ($telegramResult['ok'] ?? false) !== true) {
    $telegramDescription = is_array($telegramResult)
        ? clean_text($telegramResult['description'] ?? '')
        : '';

    if ($telegramDescription !== '') {
        respond(502, [
            'ok' => false,
            'message' => 'Telegram: ' . substr($telegramDescription, 0, 180),
        ]);
    }
    if ($transportError !== '') {
        respond(502, [
            'ok' => false,
            'message' => 'Telegram connection failed: ' . substr($transportError, 0, 140),
        ]);
    }
    respond(502, [
        'ok' => false,
        'message' => 'The PHP server could not connect to Telegram.',
    ]);
}

respond(200, ['ok' => true]);
