<?php

header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Authorization');

if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(200); exit; }

require_once __DIR__ . '/../includes/supabase.php';
require_once __DIR__ . '/../includes/auth.php';

$token = requireAuth();
$userId = getUserId($token);
$input = getInput();
$action = $input['action'] ?? '';

$db = supabase($token);

switch ($action) {
    case 'start':
        handleStart($db, $userId, $input);
        break;
    case 'move':
        handleGameMove($db, $userId, $input);
        break;
    case 'resign':
        handleResign($db, $userId, $input);
        break;
    case 'draw':
        handleDraw($db, $userId, $input);
        break;
    case 'complete':
        handleComplete($db, $userId, $input);
        break;
    case 'get':
        handleGet($db, $userId, $input);
        break;
    default:
        jsonError('Invalid action');
}

function handleGet($db, string $userId, array $input): void {
    $matchId = $_GET['match_id'] ?? ($input['match_id'] ?? '');
    if (!$matchId) jsonError('match_id required');
    $sdb = supabaseService();
    $matches = $sdb->get('matches', ['id' => 'eq.' . $matchId, 'select' => '*', 'limit' => 1]);
    $match = is_array($matches) && !empty($matches) && !isset($matches['error']) ? $matches[0] : null;
    if (!$match) jsonError('Match not found', 404);

    // Auto-close: if match in_progress but no activity for 10s from BOTH players, close it
    if ($match['status'] === 'in_progress' && !empty($match['game_state'])) {
        $gs = json_decode($match['game_state'], true);
        if ($gs && isset($gs['ping'])) {
            $lastPing = $gs['t'] ?? 0;
            $updatedAt = strtotime($match['updated_at'] ?? '2000-01-01');
            $now = time();
            // If match hasn't been updated in 10+ seconds AND no recent ping
            if (($now - $updatedAt) > 10 && ($now * 1000 - $lastPing) > 10000) {
                // Check if THIS player is the only one active — don't close if we're here
                // Only close if updated_at is stale (means nobody is writing)
                if (($now - $updatedAt) > 30) {
                    // Both players inactive for 30s — close match as abandoned
                    $sdb->update('matches', ['status' => 'completed', 'result' => 'aborted'], ['id' => 'eq.' . $matchId]);
                    $match['status'] = 'completed';
                    $match['result'] = 'aborted';
                }
            }
        }
    }

    jsonResponse($match);
}

function handleStart($db, string $userId, array $input): void {
    $gameKey = $input['game_key'] ?? 'chess';
    $mode = $input['mode'] ?? 'bot';
    $timeControl = $input['time_control'] ?? 'rapid_10_0';
    $opponentId = $input['opponent_id'] ?? null;
    $botId = $input['bot_id'] ?? null;

    $data = [
        'game_key' => $gameKey,
        'white_player_id' => $userId,
        'black_player_id' => $opponentId,
        'status' => 'in_progress',
        'time_control' => $timeControl,
        'current_fen' => 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1',
        'moves' => '[]',
        'metadata' => json_encode(['mode' => $mode, 'bot_id' => $botId])
    ];

    $result = $db->insert('matches', $data);
    if (isset($result['error'])) jsonError($result['error']);
    jsonResponse($result[0] ?? $result);
}

function handleGameMove($db, string $userId, array $input): void {
    $matchId = $input['match_id'] ?? '';
    if (!$matchId) jsonError('match_id required');

    $sdb = supabaseService();
    $update = ['updated_at' => date('c')];

    if (!empty($input['fen'])) $update['current_fen'] = $input['fen'];
    if (!empty($input['move'])) $update['moves'] = $input['move'];
    if (isset($input['move_count'])) $update['move_count'] = intval($input['move_count']);
    if (isset($input['white_time_remaining_ms'])) $update['white_time_remaining_ms'] = intval($input['white_time_remaining_ms']);
    if (isset($input['black_time_remaining_ms'])) $update['black_time_remaining_ms'] = intval($input['black_time_remaining_ms']);

    // Merge game_state instead of overwriting — preserves emotes, draw offers, etc.
    if (!empty($input['game_state'])) {
        $newState = json_decode($input['game_state'], true) ?: [];
        $matches = $sdb->get('matches', ['id' => 'eq.' . $matchId, 'select' => 'game_state', 'limit' => 1]);
        $existing = [];
        if (is_array($matches) && !empty($matches) && !isset($matches['error'])) {
            $raw = $matches[0]['game_state'] ?? null;
            if ($raw) $existing = is_array($raw) ? $raw : (json_decode($raw, true) ?: []);
        }
        $merged = array_merge($existing, $newState);
        $update['game_state'] = json_encode($merged);
    }

    $result = $sdb->update('matches', $update, ['id' => 'eq.' . $matchId]);

    if (isset($result['error'])) jsonError($result['error']);
    jsonResponse(['success' => true]);
}

function handleResign($db, string $userId, array $input): void {
    $matchId = $input['match_id'] ?? '';
    if (!$matchId) jsonError('match_id required');

    $sdb = supabaseService();
    $matches = $sdb->get('matches', ['id' => 'eq.' . $matchId, 'select' => 'id,white_player_id,black_player_id,status', 'limit' => 1]);
    $match = (is_array($matches) && !empty($matches) && !isset($matches['error'])) ? $matches[0] : null;
    if (!$match) jsonError('Match not found', 404);
    if ($match['status'] === 'completed') jsonError('Match already ended');

    $isWhite = $match['white_player_id'] === $userId;
    $result = $isWhite ? 'black_wins' : 'white_wins';

    $sdb->update('matches', [
        'status' => 'completed',
        'result' => $result,
        'ended_at' => date('c')
    ], ['id' => 'eq.' . $matchId]);

    jsonResponse(['result' => $result]);
}

function handleDraw($db, string $userId, array $input): void {
    $matchId = $input['match_id'] ?? '';
    if (!$matchId) jsonError('match_id required');

    $sdb = supabaseService();
    $matches = $sdb->get('matches', ['id' => 'eq.' . $matchId, 'select' => 'id,status', 'limit' => 1]);
    $match = (is_array($matches) && !empty($matches) && !isset($matches['error'])) ? $matches[0] : null;
    if (!$match) jsonError('Match not found', 404);
    if ($match['status'] === 'completed') jsonError('Match already ended');

    $sdb->update('matches', [
        'status' => 'completed',
        'result' => 'draw',
        'ended_at' => date('c'),
        'game_state' => json_encode(['draw_accepted' => true])
    ], ['id' => 'eq.' . $matchId]);

    jsonResponse(['result' => 'draw']);
}

function handleComplete($db, string $userId, array $input): void {
    $matchId = $input['match_id'] ?? '';
    $result = $input['result'] ?? '';
    $fen = $input['fen'] ?? '';
    $pgn = $input['pgn'] ?? '';
    $opponentRating = intval($input['opponent_rating'] ?? 1200);
    $timeControl = $input['time_control'] ?? 'rapid_10_0';

    if (!$matchId || !$result) {
        jsonError('match_id and result are required');
    }

    $sdb = supabaseService();
    $sdb->update('matches', [
        'status' => 'completed',
        'result' => $result,
        'current_fen' => $fen,
        'pgn' => $pgn,
        'ended_at' => date('c')
    ], ['id' => 'eq.' . $matchId]);

    // Calculate Elo rating change
    $ratingCol = getRatingColumn($timeControl);
    $profiles = $sdb->get('profiles', ['id' => 'eq.' . $userId, 'select' => $ratingCol . ',games_played,total_wins,total_draws,total_losses,win_streak,coins,xp,level', 'limit' => 1]);
    $profile = is_array($profiles) && !empty($profiles) ? $profiles[0] : null;

    if ($profile) {
        $playerRating = $profile[$ratingCol] ?? 1200;
        $score = ($result === 'win') ? 1.0 : (($result === 'draw') ? 0.5 : 0.0);
        $newRating = calculateElo($playerRating, $opponentRating, $score);
        $ratingChange = $newRating - $playerRating;

        $updates = [$ratingCol => $newRating, 'games_played' => ($profile['games_played'] ?? 0) + 1];
        if ($result === 'win') {
            $updates['total_wins'] = ($profile['total_wins'] ?? 0) + 1;
            $updates['win_streak'] = ($profile['win_streak'] ?? 0) + 1;
        } elseif ($result === 'draw') {
            $updates['total_draws'] = ($profile['total_draws'] ?? 0) + 1;
            $updates['win_streak'] = 0;
        } else {
            $updates['total_losses'] = ($profile['total_losses'] ?? 0) + 1;
            $updates['win_streak'] = 0;
        }

        $sdb->update('profiles', $updates, ['id' => 'eq.' . $userId]);

        // Record rating history
        $sdb->insert('rating_history', [
            'player_id' => $userId,
            'game_key' => 'chess',
            'time_control_type' => getTimeControlType($timeControl),
            'rating_before' => $playerRating,
            'rating_after' => $newRating,
            'rating_change' => $ratingChange,
            'match_id' => $matchId,
            'result' => $result
        ]);

        // Grant coins from reward_config (admin-configurable)
        $rewardConfig = $sdb->get('reward_config', ['select' => 'key,value']);
        $rewards = [];
        if (is_array($rewardConfig) && !isset($rewardConfig['error'])) {
            foreach ($rewardConfig as $r) $rewards[$r['key']] = intval($r['value']);
        }
        $coins = ($result === 'win') ? ($rewards['chess_win_coins'] ?? 50) : (($result === 'draw') ? ($rewards['chess_draw_coins'] ?? 20) : ($rewards['chess_loss_coins'] ?? 10));

        // Use atomic DB function to prevent race conditions
        $sdb->rpc('award_coins', ['p_player_id' => $userId, 'p_amount' => $coins, 'p_reason' => 'Chess ' . $result]);

        // Legacy insert kept for compatibility
        $currentCoins = ($profile['coins'] ?? 0) + $coins;
        $sdb->insert('economy_transactions', [
            'player_id' => $userId,
            'type' => 'game_reward',
            'currency' => 'coins',
            'amount' => $coins,
            'balance_after' => $currentCoins + $coins,
            'reason' => 'Chess ' . $result
        ]);

        // Tournament result reporting hook
        reportTournamentResult($db, $matchId, $result, $userId);

        // Check achievements after game completion
        checkGameAchievements($sdb, $userId, $updates, $newRating);

        jsonResponse(['success' => true, 'result' => $result, 'rating_before' => $playerRating, 'rating_after' => $newRating, 'rating_change' => $ratingChange, 'coins_earned' => $coins]);
    }

    // Tournament result reporting hook (fallback when no profile)
    reportTournamentResult($db, $matchId, $result, $userId);

    jsonResponse(['success' => true, 'result' => $result]);
}

function calculateElo(int $playerRating, int $opponentRating, float $score): int {
    $k = 32;
    if ($playerRating > 2400) $k = 16;
    elseif ($playerRating > 2000) $k = 24;

    $expected = 1.0 / (1.0 + pow(10, ($opponentRating - $playerRating) / 400.0));
    $newRating = round($playerRating + $k * ($score - $expected));
    return max(100, $newRating);
}

function getRatingColumn(string $timeControl): string {
    if (strpos($timeControl, 'bullet') !== false) return 'elo_bullet';
    if (strpos($timeControl, 'blitz') !== false) return 'elo_blitz';
    if (strpos($timeControl, 'classical') !== false) return 'elo_classical';
    return 'elo_rapid';
}

function getTimeControlType(string $timeControl): string {
    if (strpos($timeControl, 'bullet') !== false) return 'bullet';
    if (strpos($timeControl, 'blitz') !== false) return 'blitz';
    if (strpos($timeControl, 'classical') !== false) return 'classical';
    return 'rapid';
}

function reportTournamentResult($db, string $matchId, string $result, string $userId): void {
    $sdb = supabaseService();
    $matches = $sdb->get('matches', ['id' => 'eq.' . $matchId, 'select' => 'tournament_id,metadata', 'limit' => 1]);
    $match = is_array($matches) && !empty($matches) && !isset($matches['error']) ? $matches[0] : null;

    if (!$match || empty($match['tournament_id'])) return;

    $metadata = json_decode($match['metadata'] ?? '{}', true);
    if (!empty($metadata['tournament_reported'])) return;

    // Delegate to tournament-match.php logic via internal call
    $payload = json_encode([
        'action' => 'report-result',
        'match_id' => $matchId,
        'tournament_id' => $match['tournament_id'],
        'result' => $result
    ]);

    // Use stream context for internal request
    $url = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http')
        . '://' . ($_SERVER['HTTP_HOST'] ?? 'localhost') . '/api/tournament-match.php';

    $ctx = stream_context_create(['http' => [
        'method' => 'POST',
        'header' => "Content-Type: application/json\r\nAuthorization: Bearer " . (getAuthToken() ?? '') . "\r\n",
        'content' => $payload,
        'timeout' => 5
    ]]);

    @file_get_contents($url, false, $ctx);
}

function checkGameAchievements($sdb, string $userId, array $profileUpdates, int $newRating): void {
    $achievements = $sdb->get('achievements', ['select' => 'id,condition,coins_reward,xp_reward']);
    if (!is_array($achievements) || isset($achievements['error'])) return;

    $stats = [
        'wins' => $profileUpdates['total_wins'] ?? 0,
        'games_played' => $profileUpdates['games_played'] ?? 0,
        'win_streak' => $profileUpdates['win_streak'] ?? 0,
        'elo' => $newRating,
    ];

    foreach ($achievements as $a) {
        $condition = $a['condition'];
        if (is_string($condition)) $condition = json_decode($condition, true);
        if (!$condition) continue;

        $type = $condition['type'] ?? '';
        $target = $condition['count'] ?? 1;

        if (!isset($stats[$type])) continue;
        if ($stats[$type] < $target) continue;

        // Check if already completed
        $existing = $sdb->get('player_achievements', [
            'player_id' => 'eq.' . $userId,
            'achievement_id' => 'eq.' . $a['id'],
            'limit' => 1
        ]);

        if (is_array($existing) && !isset($existing['error']) && !empty($existing)) {
            if ($existing[0]['completed'] ?? false) continue;
            $sdb->update('player_achievements', [
                'progress' => $stats[$type],
                'completed' => true,
                'completed_at' => gmdate('c')
            ], ['player_id' => 'eq.' . $userId, 'achievement_id' => 'eq.' . $a['id']]);
        } else {
            $sdb->insert('player_achievements', [
                'player_id' => $userId,
                'achievement_id' => $a['id'],
                'progress' => $stats[$type],
                'completed' => true,
                'completed_at' => gmdate('c')
            ]);
        }

        // Grant rewards
        $coins = intval($a['coins_reward'] ?? 0);
        if ($coins > 0) {
            $sdb->rpc('award_coins', ['p_player_id' => $userId, 'p_amount' => $coins, 'p_reason' => 'Achievement: ' . $a['id']]);
        }
        $xp = intval($a['xp_reward'] ?? 0);
        if ($xp > 0) {
            $profiles = $sdb->get('profiles', ['id' => 'eq.' . $userId, 'select' => 'xp,level', 'limit' => 1]);
            if (is_array($profiles) && !empty($profiles)) {
                $currentXp = ($profiles[0]['xp'] ?? 0) + $xp;
                $newLevel = floor($currentXp / 500) + 1;
                $sdb->update('profiles', ['xp' => $currentXp, 'level' => $newLevel], ['id' => 'eq.' . $userId]);
            }
        }
    }
}
