<?php
declare(strict_types=1);

header('Content-Type: application/json; charset=utf-8');
$allowedHosts = ['chrono-party.e-u.fr'];

$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
$referer = $_SERVER['HTTP_REFERER'] ?? '';
$host = $_SERVER['HTTP_HOST'] ?? '';

$validRequest = false;

if (in_array($host, $allowedHosts, true)) {
    $validRequest = true;
}

if ($origin !== '') {
    $originHost = parse_url($origin, PHP_URL_HOST);
    if (in_array($originHost, $allowedHosts, true)) {
        $validRequest = true;
    }
}

if ($referer !== '') {
    $refererHost = parse_url($referer, PHP_URL_HOST);
    if (in_array($refererHost, $allowedHosts, true)) {
        $validRequest = true;
    }
}

if (!$validRequest) {
    $logFile = dirname(__DIR__) . '/privatedata/security-alerts.log';

    $logLine = date('Y-m-d H:i:s') . ' | SAVE-PARTY REFUSED | '
        . 'host=' . ($_SERVER['HTTP_HOST'] ?? '') . ' | '
        . 'origin=' . ($_SERVER['HTTP_ORIGIN'] ?? '') . ' | '
        . 'referer=' . ($_SERVER['HTTP_REFERER'] ?? '') . ' | '
        . 'ip=' . ($_SERVER['REMOTE_ADDR'] ?? '') . ' | '
        . 'ua=' . ($_SERVER['HTTP_USER_AGENT'] ?? '') . PHP_EOL;

    file_put_contents($logFile, $logLine, FILE_APPEND | LOCK_EX);

    @mail(
        'gcse.webdigital@gmail.com',
        'Alerte sécurité Chrono Party',
        $logLine,
        'From: contact@chrono-party.fr'
    );

    http_response_code(403);
    echo json_encode([
        'success' => false,
        'message' => 'Accès refusé'
    ]);
    exit;
}
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    echo json_encode(['success' => false, 'message' => 'Méthode non autorisée']);
    exit;
}

$input = file_get_contents('php://input');

if (!$input || strlen($input) > 300000) {
    http_response_code(400);
    echo json_encode(['success' => false, 'message' => 'Données invalides ou trop volumineuses']);
    exit;
}

$data = json_decode($input, true);

if (!is_array($data)) {
    http_response_code(400);
    echo json_encode(['success' => false, 'message' => 'JSON invalide']);
    exit;
}

$baseDir = dirname(__DIR__);
$privateDir = $baseDir . '/privatedata';

if (!is_dir($privateDir)) {
    mkdir($privateDir, 0755, true);
}

$partiesFile = $privateDir . '/parties.json';
$playersFile = $privateDir . '/players.json';

function clean_text($value, int $max = 120): string
{
    $value = trim((string) $value);
    $value = strip_tags($value);
    $value = preg_replace('/[\x00-\x1F\x7F]/u', '', $value);
    return mb_substr($value, 0, $max);
}

function read_json_file(string $file, $default)
{
    if (!file_exists($file)) {
        file_put_contents($file, json_encode($default, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), LOCK_EX);
        return $default;
    }

    $content = file_get_contents($file);
    $json = json_decode((string) $content, true);

    return is_array($json) ? $json : $default;
}

function write_json_file(string $file, $data): void
{
    file_put_contents(
        $file,
        json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE),
        LOCK_EX
    );
}

$current = read_json_file($partiesFile, []);

$sessionId = clean_text($data['session_id'] ?? '', 80);

if ($sessionId !== '') {
    foreach ($current as $existingParty) {
        if (($existingParty['session_id'] ?? '') === $sessionId) {
            $existingId = (string)($existingParty['id'] ?? '');
            echo json_encode([
                'success' => true,
                'duplicate' => true,
                'party_id' => $existingId,
                'public_url' => 'https://chrono-party.e-u.fr/jeux/partie/?id=' . urlencode($existingId)
            ], JSON_UNESCAPED_UNICODE);
            exit;
        }
    }
}

$source = clean_text($data['source'] ?? 'unknown', 30);
$allowedSources = ['demo', 'demo2026', 'premium', 'gift', 'asso', 'test', 'unknown'];

if (!in_array($source, $allowedSources, true)) {
    $source = 'unknown';
}

$party = [
    'id' => uniqid('party_', true),
    'created_at' => date('Y-m-d H:i:s'),
    'session_id' => $sessionId,

'source' => $source,

'security' => [
    'server_host' => $_SERVER['HTTP_HOST'] ?? '',
    'origin' => $_SERVER['HTTP_ORIGIN'] ?? '',
    'referer' => $_SERVER['HTTP_REFERER'] ?? '',
    'ip' => $_SERVER['REMOTE_ADDR'] ?? '',
    'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
],

'client_id' => clean_text($data['client_id'] ?? '', 80),
    'client_name' => clean_text($data['client_name'] ?? '', 120),
    'gift_code' => clean_text($data['gift_code'] ?? '', 80),

    'duration' => max(0, (int)($data['duration'] ?? 0)),

    'players_count' => max(0, (int)($data['players_count'] ?? 0)),
    'teams_count' => max(0, (int)($data['teams_count'] ?? 0)),
    'rounds_played' => max(0, (int)($data['rounds_played'] ?? 0)),

    'game_mode' => clean_text($data['game_mode'] ?? 'classic', 20),
    'coop' => is_array($data['coop'] ?? null) ? $data['coop'] : null,

    'winner' => clean_text($data['winner'] ?? '', 120),

    'scores' => is_array($data['scores'] ?? null) ? $data['scores'] : [],
    'teams' => is_array($data['teams'] ?? null) ? $data['teams'] : [],
    'themes' => is_array($data['themes'] ?? null) ? $data['themes'] : [],
    'difficulties' => is_array($data['difficulties'] ?? null) ? $data['difficulties'] : []
];

if (!in_array($party['game_mode'], ['classic', 'coop'], true)) {
    $party['game_mode'] = 'classic';
}

if (is_array($party['coop'])) {
    $allowedBossLevels = ['easy', 'standard', 'hard', 'legend'];
    $level = clean_text($party['coop']['level'] ?? 'standard', 20);
    if (!in_array($level, $allowedBossLevels, true)) {
        $level = 'standard';
    }

    $party['coop'] = [
        'enabled' => !empty($party['coop']['enabled']),
        'level' => $level,
        'label' => clean_text($party['coop']['label'] ?? '', 80),
        'icon' => clean_text($party['coop']['icon'] ?? '', 10),
        'max_turns' => max(0, (int)($party['coop']['max_turns'] ?? 0)),
        'turns_used' => max(0, (int)($party['coop']['turns_used'] ?? 0)),
        'turns_remaining' => max(0, (int)($party['coop']['turns_remaining'] ?? 0)),
        'over_by' => max(0, (int)($party['coop']['over_by'] ?? 0)),
        'won' => !empty($party['coop']['won']),
        'result' => clean_text($party['coop']['result'] ?? '', 20),
        'rank_key' => clean_text($party['coop']['rank_key'] ?? '', 20),
        'rank_label' => clean_text($party['coop']['rank_label'] ?? '', 40),
        'rank_icon' => clean_text($party['coop']['rank_icon'] ?? '', 10),
        'words_per_turn_target' => (float)($party['coop']['words_per_turn_target'] ?? 0),
    ];
} else {
    $party['coop'] = null;
}

/****************************
 * ANTI-TRICHE SIMPLE
 *
 * Important : une partie suspecte est sauvegardée,
 * mais elle n'est pas comptée dans les statistiques joueurs
 * tant qu'elle n'est pas validée manuellement dans Admin Parties.
 ****************************/

$antiCheatReasons = [];

function add_anti_cheat_reason(array &$reasons, string $reason): void
{
    if ($reason !== '' && !in_array($reason, $reasons, true)) {
        $reasons[] = $reason;
    }
}

/* joueurs */
if ($party['players_count'] < 2 || $party['players_count'] > 12) {
    add_anti_cheat_reason($antiCheatReasons, 'Nombre de joueurs anormal : ' . $party['players_count']);
}

/* équipes */
if ($party['teams_count'] < 1 || $party['teams_count'] > 6) {
    add_anti_cheat_reason($antiCheatReasons, 'Nombre d’équipes anormal : ' . $party['teams_count']);
}

/* manches */
if ($party['rounds_played'] < 1 || $party['rounds_played'] > 4) {
    add_anti_cheat_reason($antiCheatReasons, 'Nombre de manches anormal : ' . $party['rounds_played']);
}

/* durée globale */
if ($party['duration'] < 600) {
    add_anti_cheat_reason($antiCheatReasons, 'Durée totale trop courte : ' . $party['duration'] . ' seconde(s)');
}

if ($party['duration'] > 7200) {
    add_anti_cheat_reason($antiCheatReasons, 'Durée totale trop longue : ' . $party['duration'] . ' seconde(s)');
}

/* score total impossible */
foreach ($party['teams'] as $team) {
    if (!is_array($team)) {
        continue;
    }

    $score = (int)($team['total'] ?? 0);

    if ($score > 200) {
        add_anti_cheat_reason($antiCheatReasons, 'Score d’équipe anormal : ' . clean_text($team['name'] ?? 'Équipe', 80) . ' avec ' . $score . ' points');
        break;
    }
}

/* tours très anormaux */
foreach ($party['teams'] as $team) {
    if (!is_array($team) || !is_array($team['turnStats'] ?? null)) {
        continue;
    }

    foreach ($team['turnStats'] as $turn) {
        if (!is_array($turn)) {
            continue;
        }

        $playerName = clean_text($turn['playerName'] ?? 'Joueur', 80);
        $foundCount = max(0, (int)($turn['foundCount'] ?? 0));
        $turnDuration = max(0, (int)($turn['durationSeconds'] ?? 0));

        if ($foundCount >= 12 && $turnDuration > 0 && $turnDuration <= 20) {
            add_anti_cheat_reason($antiCheatReasons, $playerName . ' : ' . $foundCount . ' mots trouvés en ' . $turnDuration . ' seconde(s)');
        }

        if ($foundCount > 30) {
            add_anti_cheat_reason($antiCheatReasons, $playerName . ' : nombre de mots anormal sur un tour (' . $foundCount . ')');
        }
    }
}

/* doublon récent */
foreach ($current as $existing) {
    if (
        ($existing['winner'] ?? '') === ($party['winner'] ?? '')
        &&
        ($existing['duration'] ?? 0) === ($party['duration'] ?? 0)
        &&
        abs(
            strtotime($party['created_at'])
            - strtotime($existing['created_at'] ?? '')
        ) < 60
    ) {
        add_anti_cheat_reason($antiCheatReasons, 'Doublon récent possible avec une autre partie');
        break;
    }
}

$party['suspicious'] = !empty($antiCheatReasons);
$party['status'] = $party['suspicious'] ? 'suspect' : 'validated';
$party['anti_cheat'] = [
    'status' => $party['suspicious'] ? 'suspect' : 'valid',
    'score' => $party['suspicious'] ? max(0, 100 - (count($antiCheatReasons) * 25)) : 100,
    'reasons' => $antiCheatReasons
];

array_unshift($current, $party);
$current = array_slice($current, 0, 5000);

write_json_file($partiesFile, $current);

$playersStats = read_json_file($playersFile, []);

if (!empty($party['suspicious'])) {

    echo json_encode([
        'success' => true,
        'duplicate' => false,
        'party_id' => $party['id'],
        'public_url' => 'https://chrono-party.e-u.fr/jeux/partie/?id=' . urlencode($party['id'])
    ], JSON_UNESCAPED_UNICODE);

    exit;
}

$winnerName = clean_text($party['winner'], 120);

foreach ($party['teams'] as $team) {
    if (!is_array($team)) {
        continue;
    }

    $teamName = clean_text($team['name'] ?? '', 120);
    $teamScore = max(0, (int)($team['total'] ?? 0));
    $isWinner = ($winnerName !== '' && $teamName === $winnerName);

    if (empty($team['players']) || !is_array($team['players'])) {
        continue;
    }

    foreach ($team['players'] as $player) {
        if (!is_array($player)) {
            continue;
        }

        $playerId = clean_text($player['id'] ?? '', 80);
        $playerName = clean_text($player['name'] ?? '', 80);

        if ($playerId === '' || $playerName === '') {
            continue;
        }

        if (!isset($playersStats[$playerId])) {
            $playersStats[$playerId] = [
                'id' => $playerId,
                'name' => $playerName,
                'games_count' => 0,
                'wins' => 0,
                'total_score' => 0,
                'last_played_at' => null,
                'coop_boss_wins' => [
                    'easy' => 0,
                    'standard' => 0,
                    'hard' => 0,
                    'legend' => 0
                ]
            ];
        }

        $playersStats[$playerId]['name'] = $playerName;
        $playersStats[$playerId]['games_count']++;
        $playersStats[$playerId]['total_score'] += $teamScore;
        $playersStats[$playerId]['last_played_at'] = $party['created_at'];

        if ($isWinner) {
            $playersStats[$playerId]['wins']++;
        }

        if (($party['game_mode'] ?? 'classic') === 'coop' && is_array($party['coop'] ?? null) && !empty($party['coop']['won'])) {
            $bossLevel = clean_text($party['coop']['level'] ?? '', 20);

            if (in_array($bossLevel, ['easy', 'standard', 'hard', 'legend'], true)) {
                if (!isset($playersStats[$playerId]['coop_boss_wins']) || !is_array($playersStats[$playerId]['coop_boss_wins'])) {
                    $playersStats[$playerId]['coop_boss_wins'] = [
                        'easy' => 0,
                        'standard' => 0,
                        'hard' => 0,
                        'legend' => 0
                    ];
                }

                $playersStats[$playerId]['coop_boss_wins'][$bossLevel] =
                    max(0, (int)($playersStats[$playerId]['coop_boss_wins'][$bossLevel] ?? 0)) + 1;
            }
        }
    }
}

write_json_file($playersFile, $playersStats);

echo json_encode([
    'success' => true,
    'duplicate' => false,
    'party_id' => $party['id'],
    'public_url' => 'https://chrono-party.e-u.fr/jeux/partie/?id=' . urlencode($party['id'])
], JSON_UNESCAPED_UNICODE);