<?php
ini_set('display_errors', '0');
error_reporting(E_ALL & ~E_NOTICE);

header('Content-Type: application/json');
header('X-Content-Type-Options: nosniff');

require_once 'koneksi.php';

function game_history_fail(int $httpCode, string $message, array $extra = []): void
{
    http_response_code($httpCode);
    echo json_encode(array_merge([
        'status' => 'fail',
        'msg' => $message,
    ], $extra));
    exit;
}

function game_history_datetime(string $value, string $fallback, string $label): string
{
    $value = trim($value);
    if ($value === '') {
        return $fallback;
    }

    $time = strtotime($value);
    if ($time === false) {
        game_history_fail(400, 'INVALID ' . strtoupper($label));
    }

    return date('Y-m-d H:i:s', $time);
}

function game_history_int($value, int $default, int $min, int $max, string $label): int
{
    $value = trim((string)$value);
    if ($value === '') {
        return $default;
    }

    if (!ctype_digit($value)) {
        game_history_fail(400, 'INVALID ' . strtoupper($label));
    }

    $number = (int)$value;
    if ($number < $min || $number > $max) {
        game_history_fail(400, 'INVALID ' . strtoupper($label));
    }

    return $number;
}

function game_history_match_filter(string $value, string $filter): bool
{
    return $filter === '' || strtoupper($value) === strtoupper($filter);
}

function game_history_value_in_list(string $value, array $allowedValues): bool
{
    $value = trim($value);
    if ($value === '') {
        return true;
    }

    foreach ($allowedValues as $allowedValue) {
        if (strcasecmp($value, trim((string)$allowedValue)) === 0) {
            return true;
        }
    }

    return false;
}

function game_history_in_date_range(string $createdAt, string $start, string $end): bool
{
    $time = strtotime($createdAt);
    if ($time === false) {
        return true;
    }

    return $time >= strtotime($start) && $time <= strtotime($end);
}

function game_history_user_scope_condition(mysqli $db, string $column, array $agent): string
{
    $agentLevel = strtolower(trim((string)($agent['agent_level'] ?? '')));
    $agentRefsSql = agent_sql_values($db, agent_identity_values($agent));

    if (in_array($agentLevel, ['admin', 'superadmin', 'super_admin'], true)) {
        return '1=1';
    }

    if ($agentLevel === 'head_agent') {
        return "(
            $column IN ($agentRefsSql)
            OR $column IN (
                SELECT a.agent_code
                FROM tb_agent a
                WHERE a.agent_upline IN ($agentRefsSql)
            )
            OR $column IN (
                SELECT a.agent_name
                FROM tb_agent a
                WHERE a.agent_upline IN ($agentRefsSql)
            )
        )";
    }

    return "$column IN ($agentRefsSql)";
}

function game_history_scalar_value($value, $default = '')
{
    if ($value === null || is_array($value) || is_object($value)) {
        return $default;
    }

    if (is_bool($value)) {
        return $value ? '1' : '0';
    }

    return $value;
}

function game_history_pick(array $row, array $keys, $default = '')
{
    foreach ($keys as $key) {
        if (!array_key_exists($key, $row)) {
            continue;
        }

        $value = game_history_scalar_value($row[$key], null);
        if ($value !== null && (string)$value !== '') {
            return $value;
        }
    }

    return $default;
}

function game_history_number($value): float
{
    $value = game_history_scalar_value($value, 0);
    if (is_numeric($value)) {
        return (float)$value;
    }

    $clean = str_replace(',', '', trim((string)$value));
    return is_numeric($clean) ? (float)$clean : 0.0;
}

function game_history_provider_type_to_game_type(string $type): string
{
    $type = strtolower(trim($type));
    $map = [
        'sl' => 'slot',
        'slot' => 'slot',
        'slots' => 'slot',
        'fh' => 'fishing',
        'fish' => 'fishing',
        'fishing' => 'fishing',
        'arcade' => 'arcade',
        'ac' => 'arcade',
        'tb' => 'table',
        'table' => 'table',
        'lc' => 'table',
        'casino' => 'table',
        'lottery' => 'lottery',
        'lotto' => 'lottery',
        'sp' => 'sports',
        'sport' => 'sports',
        'sports' => 'sports',
    ];

    return $map[$type] ?? $type;
}

function game_history_row_game_type(array $row, string $fallbackType = ''): string
{
    $type = (string)game_history_pick($row, ['game_type', 'gameType', 'provider_type', 'providerType', 'category'], '');
    if ($type === '') {
        $type = $fallbackType;
    }

    return game_history_provider_type_to_game_type($type);
}

function game_history_row_matches_game_type(array $row, string $gameType): bool
{
    if ($gameType === '' || $gameType === 'all') {
        return true;
    }

    $rowType = game_history_row_game_type($row);
    return $rowType === '' || $rowType === $gameType;
}

function game_history_normalize_log_row(array $row, string $extUser, string $intUser, float $agentBalance = 0, string $fallbackType = ''): array
{
    foreach (['txn', 'transaction'] as $nestedKey) {
        if (isset($row[$nestedKey]) && is_array($row[$nestedKey])) {
            $row = array_merge($row[$nestedKey], $row);
        }
    }

    $historyId = game_history_pick($row, ['history_id', 'historyId', 'id', 'round_id', 'roundId', 'session', 'txn_id', 'transaction_id', 'transactionId', 'trx_id', 'trxId'], '');
    $txnId = game_history_pick($row, ['txn_id', 'transaction_id', 'transactionId', 'trx_id', 'trxId', 'session', 'history_id', 'historyId', 'id'], $historyId);
    $createdAt = game_history_pick($row, ['created_at', 'createdAt', 'created', 'date', 'datetime', 'time', 'bet_time', 'betTime', 'settle_time', 'settleTime', 'end_time', 'endTime', 'match_time', 'matchTime', 'start_time', 'startTime'], '');

    return [
        'history_id' => (string)$historyId,
        'user' => $extUser,
        'user_member' => $extUser,
        'user_code' => (string)game_history_pick($row, ['user_code', 'userCode', 'intUser', 'username', 'user', 'member', 'member_code', 'memberCode'], $intUser),
        'provider_code' => (string)game_history_pick($row, ['provider_code', 'providerCode', 'provider', 'provider_name', 'providerName'], ''),
        'game_code' => (string)game_history_pick($row, ['game_code', 'gameCode', 'game', 'game_name', 'gameName'], ''),
        'type' => (string)game_history_pick($row, ['type', 'game_type', 'gameType', 'provider_type', 'providerType', 'category'], $fallbackType),
        'game_type' => game_history_row_game_type($row, $fallbackType),
        'bet_money' => game_history_number(game_history_pick($row, ['bet_money', 'betMoney', 'bet', 'turnover', 'bet_amount', 'betAmount', 'valid_bet', 'validBet', 'amount'], 0)),
        'win_money' => game_history_number(game_history_pick($row, ['win_money', 'winMoney', 'win', 'payout', 'win_amount', 'winAmount', 'settle_amount', 'settleAmount'], 0)),
        'txn_id' => (string)$txnId,
        'txn_type' => (string)game_history_pick($row, ['txn_type', 'txnType', 'transaction_type', 'transactionType', 'status', 'action'], ''),
        'user_start_balance' => game_history_number(game_history_pick($row, ['user_start_balance', 'userStartBalance', 'before_balance', 'beforeBalance', 'balance_before', 'balanceBefore', 'user_before_balance', 'userBeforeBalance'], 0)),
        'user_end_balance' => game_history_number(game_history_pick($row, ['user_end_balance', 'userEndBalance', 'after_balance', 'afterBalance', 'balance_after', 'balanceAfter', 'user_after_balance', 'userAfterBalance', 'balance'], 0)),
        'agent_start_balance' => game_history_number(game_history_pick($row, ['agent_start_balance', 'agentStartBalance', 'agent_before_balance', 'agentBeforeBalance'], $agentBalance)),
        'agent_end_balance' => game_history_number(game_history_pick($row, ['agent_end_balance', 'agentEndBalance', 'agent_after_balance', 'agentAfterBalance'], $agentBalance)),
        'created_at' => (string)$createdAt,
    ];
}

function game_history_normalize_history_row(array $row, string $extUser, string $intUser, float $agentBalance): array
{
    return game_history_normalize_log_row($row, $extUser, $intUser, $agentBalance);
}

function game_history_is_success_response(array $response, string $type): bool
{
    $statusRaw = $response['status'] ?? null;
    $statusText = strtolower(trim((string)$statusRaw));
    $status = is_numeric($statusRaw) ? (int)$statusRaw : null;

    if ($status === 1 || $status === 200 || $statusText === 'success') {
        return true;
    }

    return $status === 0 && (
        game_history_response_rows($response, $type) !== []
        ||
        isset($response[$type])
        || isset($response['data'])
        || isset($response['game_log'])
        || isset($response['total_count'])
    );
}

function game_history_response_message(array $response, string $fallback): string
{
    return trim((string)($response['error'] ?? $response['msg'] ?? $response['message'] ?? $fallback)) ?: $fallback;
}

function game_history_is_rate_limit_response($response): bool
{
    if (!is_array($response)) {
        return false;
    }

    $message = strtolower(game_history_response_message($response, ''));
    return strpos($message, 'rate') !== false && strpos($message, 'limit') !== false;
}

function game_history_fetch_primary_history($provider, string $userCode, string $type, string $start, string $end, int $page, int $perPage)
{
    $attempts = 4;
    $response = null;

    for ($attempt = 0; $attempt < $attempts; $attempt++) {
        if ($attempt > 0) {
            usleep(1000000 * $attempt);
        }

        $response = $provider->historyPlay($userCode, $type, $start, $end, $page, $perPage);
        if (!game_history_is_rate_limit_response($response)) {
            return $response;
        }
    }

    return $response;
}

function game_history_response_rows(array $response, string $type): array
{
    $candidates = [
        $response[$type] ?? null,
        $response['data'] ?? null,
        $response['game_log'] ?? null,
        $response['history'] ?? null,
        $response['histories'] ?? null,
        $response['logs'] ?? null,
        $response['rows'] ?? null,
        $response['records'] ?? null,
        $response['result'] ?? null,
        $response['results'] ?? null,
        $response['list'] ?? null,
        $response['items'] ?? null,
        $response['data'][$type] ?? null,
        $response['data']['data'] ?? null,
        $response['data']['game_log'] ?? null,
        $response['data']['history'] ?? null,
        $response['data']['histories'] ?? null,
        $response['data']['logs'] ?? null,
        $response['data']['rows'] ?? null,
        $response['data']['records'] ?? null,
        $response['data']['result'] ?? null,
        $response['data']['results'] ?? null,
        $response['data']['list'] ?? null,
        $response['data']['items'] ?? null,
    ];

    foreach ($candidates as $candidate) {
        $rows = game_history_collect_rows($candidate);
        if ($rows !== []) {
            return $rows;
        }
    }

    return [];
}

function game_history_collect_rows($candidate, int $depth = 0): array
{
    if (!is_array($candidate) || $depth > 5) {
        return [];
    }

    if (game_history_looks_like_row($candidate)) {
        return [$candidate];
    }

    $rows = [];
    foreach ($candidate as $item) {
        if (!is_array($item)) {
            continue;
        }

        $nestedRows = game_history_collect_rows($item, $depth + 1);
        if ($nestedRows !== []) {
            $rows = array_merge($rows, $nestedRows);
        }
    }

    return $rows;
}

function game_history_looks_like_row(array $row): bool
{
    $rowKeys = [
        'history_id', 'historyId', 'id', 'round_id', 'roundId', 'session',
        'txn', 'transaction', 'txn_id', 'transaction_id', 'transactionId', 'trx_id', 'trxId',
        'provider_code', 'providerCode', 'provider', 'provider_name',
        'game_code', 'gameCode', 'game', 'game_name', 'gameName',
        'bet_money', 'betMoney', 'bet', 'turnover', 'bet_amount', 'betAmount',
        'win_money', 'winMoney', 'win', 'payout', 'win_amount', 'winAmount',
        'created_at', 'createdAt', 'created', 'date', 'datetime', 'time',
        'bet_time', 'betTime', 'end_time', 'endTime', 'match_time', 'matchTime',
        'start_time', 'startTime',
    ];

    foreach ($rowKeys as $key) {
        if (array_key_exists($key, $row)) {
            return true;
        }
    }

    return false;
}

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    game_history_fail(405, 'METHOD NOT ALLOWED');
}

$agentName = trim($_POST['user_agent'] ?? ($_POST['agent_name'] ?? ''));
$secretKey = trim($_POST['signature'] ?? '');
$username = trim($_POST['user_member'] ?? ($_POST['username'] ?? ($_POST['user_code'] ?? '')));
$gameType = strtolower(trim($_POST['game_type'] ?? ($_POST['type'] ?? 'all')));
$providerFilter = trim($_POST['provider_code'] ?? '');
$gameFilter = trim($_POST['game_code'] ?? '');
$debugHistory = in_array(strtolower(trim((string)($_POST['debug'] ?? $_POST['debug_history'] ?? ''))), ['1', 'true', 'yes'], true);

if ($agentName === '' || $secretKey === '' || $username === '') {
    game_history_fail(400, 'INVALID PARAMETER');
}

$defaultStart = date('Y-m-d 00:00:00', strtotime('-30 days'));
$defaultEnd = date('Y-m-d 23:59:59');
$start = game_history_datetime((string)($_POST['start'] ?? ($_POST['start_date'] ?? ($_POST['date_from'] ?? ($_POST['from'] ?? '')))), $defaultStart, 'start');
$end = game_history_datetime((string)($_POST['end'] ?? ($_POST['end_date'] ?? ($_POST['date_to'] ?? ($_POST['to'] ?? '')))), $defaultEnd, 'end');

if (strtotime($start) > strtotime($end)) {
    game_history_fail(400, 'INVALID DATE RANGE');
}

$page = game_history_int($_POST['log_page'] ?? ($_POST['page'] ?? ''), 0, 0, 1000000, 'page');
$perPage = game_history_int($_POST['per_page'] ?? ($_POST['perPage'] ?? ''), 1000, 1, 1000, 'per_page');

$gameTypes = ['slot', 'fishing', 'arcade', 'table', 'lottery', 'sports'];
if ($gameType === '' || $gameType === 'all') {
    $requestedGameTypes = $gameTypes;
    $gameType = 'all';
} elseif (in_array($gameType, $gameTypes, true)) {
    $requestedGameTypes = [$gameType];
} else {
    game_history_fail(400, 'INVALID GAME TYPE');
}

$usernameSql = mysqli_real_escape_string($call, $username);
$clientIp = getClientIP();

$agent = agent_find_by_name($call, $agentName, $secretKey);
if ($agent === null) {
    game_history_fail(401, 'INVALID AGENT');
}

if (($agent['agent_status'] ?? '') !== 'active') {
    game_history_fail(403, 'AGENT STATUS INACTIVE');
}

$whitelist = array_filter(array_map('trim', explode(',', (string)($agent['agent_IP1'] ?? ''))));
if (!in_array($clientIp, $whitelist, true)) {
    game_history_fail(403, "IP $clientIp NOT ALLOWED");
}

$agentUserWhere = game_history_user_scope_condition($call, 'agent_code', $agent);
$qUser = mysqli_query(
    $call,
    "SELECT intUser, extUser
     FROM tb_user
     WHERE (extUser = '$usernameSql' OR intUser = '$usernameSql')
       AND $agentUserWhere
     LIMIT 1"
);

if (!$qUser || mysqli_num_rows($qUser) === 0) {
    game_history_fail(404, 'MEMBER NOT FOUND');
}

$user = mysqli_fetch_assoc($qUser) ?: [];
$intUser = (string)($user['intUser'] ?? '');
$extUser = (string)($user['extUser'] ?? $username);
$agentBalance = (float)($agent['agent_balance'] ?? 0);
$validUserCodes = array_values(array_unique(array_filter([$intUser, $extUser, $username], static function ($value) {
    return trim((string)$value) !== '';
})));

$rows = [];
$warnings = [];
$primaryRequestIndex = 0;
$debugCounts = [
    'primary_rows_received' => 0,
    'secondary_rows_received' => 0,
    'filtered_user' => 0,
    'filtered_game_type' => 0,
    'filtered_date' => 0,
    'filtered_provider' => 0,
    'filtered_game_code' => 0,
];

foreach ($requestedGameTypes as $type) {
    if ($primaryRequestIndex > 0) {
        usleep(800000);
    }
    $primaryRequestIndex++;

    $response = game_history_fetch_primary_history($GGR, $intUser, $type, $start, $end, $page, $perPage);

    if (!is_array($response) || !isset($response['status'])) {
        $warnings[] = 'NO RESPONSE FROM GAME HISTORY SERVICE (' . strtoupper($type) . ')';
        continue;
    }

    if (!game_history_is_success_response($response, $type)) {
        $warnings[] = game_history_response_message($response, 'FAILED GET GAME HISTORY') . ' (' . strtoupper($type) . ')';
        continue;
    }

    $historyRows = game_history_response_rows($response, $type);
    $debugCounts['primary_rows_received'] += count($historyRows);
    foreach ($historyRows as $row) {
        $normalized = game_history_normalize_log_row($row, $extUser, $intUser, $agentBalance, $type);

        if (!game_history_in_date_range($normalized['created_at'], $start, $end)) {
            $debugCounts['filtered_date']++;
            continue;
        }

        if (!game_history_match_filter($normalized['provider_code'], $providerFilter)) {
            $debugCounts['filtered_provider']++;
            continue;
        }

        if (!game_history_match_filter($normalized['game_code'], $gameFilter)) {
            $debugCounts['filtered_game_code']++;
            continue;
        }

        $rows[] = $normalized;
    }
}

$sgxResponse = $SGX->getGameHistory($intUser, min($perPage, 500));

if (is_array($sgxResponse) && isset($sgxResponse['status'])) {
    if ((int)$sgxResponse['status'] === 200) {
        $secondaryRows = game_history_response_rows($sgxResponse, 'data');
        $debugCounts['secondary_rows_received'] += count($secondaryRows);

        foreach ($secondaryRows as $row) {
            if (!is_array($row)) {
                continue;
            }

            $sgxUserCode = (string)game_history_pick($row, ['user_code', 'userCode', 'intUser', 'username', 'user'], '');
            if (!game_history_value_in_list($sgxUserCode, $validUserCodes)) {
                $debugCounts['filtered_user']++;
                continue;
            }

            if (!game_history_row_matches_game_type($row, $gameType)) {
                $debugCounts['filtered_game_type']++;
                continue;
            }

            $normalized = game_history_normalize_history_row($row, $extUser, $intUser, $agentBalance);

            if (!game_history_in_date_range($normalized['created_at'], $start, $end)) {
                $debugCounts['filtered_date']++;
                continue;
            }

            if (!game_history_match_filter($normalized['provider_code'], $providerFilter)) {
                $debugCounts['filtered_provider']++;
                continue;
            }

            if (!game_history_match_filter($normalized['game_code'], $gameFilter)) {
                $debugCounts['filtered_game_code']++;
                continue;
            }

            $rows[] = $normalized;
        }
    } elseif ((int)$sgxResponse['status'] !== 404) {
        $warnings[] = 'FAILED GET GAME HISTORY';
    }
} else {
    $warnings[] = 'NO RESPONSE FROM GAME HISTORY SERVICE';
}

usort($rows, static function (array $a, array $b): int {
    $left = strtotime((string)($a['created_at'] ?? '')) ?: 0;
    $right = strtotime((string)($b['created_at'] ?? '')) ?: 0;

    return $right <=> $left;
});

$response = [
    'status' => 200,
    'message' => 'success',
    'data' => $rows,
    'meta' => [
        'total_count' => count($rows),
        'page' => $page,
        'per_page' => $perPage,
        'start' => $start,
        'end' => $end,
        'game_type' => $gameType,
    ],
];

if ($rows === []) {
    $response['note'] = 'NO GAME HISTORY';
}

if ($warnings !== []) {
    $response['warnings'] = array_values(array_unique($warnings));
}

if ($debugHistory) {
    $response['debug'] = array_merge($debugCounts, [
        'matched_rows' => count($rows),
        'user_code_checked' => $validUserCodes,
    ]);
}

echo json_encode($response);
