// Scores.cpp - Implementation of ScoreManager
#include "Scores.h"
#include <SDL3/SDL.h>
#include <fstream>
#include <sstream>
#include <algorithm>
#include "../network/supabase_client.h"
#include <nlohmann/json.hpp>
#include <iostream>
#include <thread>
#include <ctime>
#include <filesystem>

using json = nlohmann::json;

ScoreManager::ScoreManager(size_t maxScores) : maxEntries(maxScores) {}

std::string ScoreManager::filePath() const {
    static std::string path; if (!path.empty()) return path;
    char* base = SDL_GetPrefPath("example","spacetris_sdl3");
    if (base) { path = std::string(base)+"highscores.txt"; SDL_free(base);} else path="highscores.txt";
    return path;
}

void ScoreManager::load() {
    scores.clear();
    
    // Try to load from Supabase first
    try {
        // Request only 10 records from Supabase to keep payload small
        auto fetched = supabase::FetchHighscores("", 10);
        if (!fetched.empty()) {
            scores = fetched;
            std::sort(scores.begin(), scores.end(), [](auto&a,auto&b){return a.score>b.score;});
            if (scores.size() > maxEntries) scores.resize(maxEntries);
            save();
            return;
        }
    } catch (...) {
        std::cerr << "Failed to load from Supabase, falling back to local file." << std::endl;
    }

    // Fallback to local file
    std::ifstream f(filePath()); 
    if (!f) {
        // Create sample high scores if file doesn't exist
        createSampleScores();
        save();
        return;
    }
    
    std::string line; 
    while (std::getline(f, line)) { 
        std::istringstream iss(line); 
        ScoreEntry e; 
        iss >> e.score >> e.lines >> e.level >> e.timeSec;
        if (iss) {
            // Try to read name (rest of line after timeSec). We may also have a trailing gameType token.
            std::string remaining;
            std::getline(iss, remaining);
            if (!remaining.empty() && remaining[0] == ' ') remaining = remaining.substr(1);
            if (!remaining.empty()) {
                static const std::vector<std::string> known = {"classic","cooperate","challenge","versus"};
                while (!remaining.empty() && (remaining.back() == '\n' || remaining.back() == '\r' || remaining.back() == ' ')) remaining.pop_back();
                size_t lastSpace = remaining.find_last_of(' ');
                std::string lastToken = (lastSpace == std::string::npos) ? remaining : remaining.substr(lastSpace + 1);
                bool matched = false;
                for (const auto &k : known) {
                    if (lastToken == k) {
                        matched = true;
                        e.gameType = k;
                        if (lastSpace == std::string::npos) e.name = "PLAYER";
                        else e.name = remaining.substr(0, lastSpace);
                        break;
                    }
                }
                if (!matched) {
                    e.name = remaining;
                    e.gameType = "classic";
                }
            } else {
                e.name = "PLAYER";
                e.gameType = "classic";
            }
            scores.push_back(e);
        }
        if (scores.size() >= maxEntries) break; 
    }
    
    if (scores.empty()) {
        createSampleScores();
        save();
    }
    
    std::sort(scores.begin(), scores.end(), [](auto&a,auto&b){return a.score>b.score;});
}

void ScoreManager::save() const {
    std::ofstream f(filePath(), std::ios::trunc);
    for (auto &e : scores) {
        // Save gameType as trailing token so future loads can preserve it
        f << e.score << ' ' << e.lines << ' ' << e.level << ' ' << e.timeSec << ' ' << e.name << ' ' << e.gameType << '\n';
    }
}

void ScoreManager::submit(int score, int lines, int level, double timeSec, const std::string& name, const std::string& gameType) {
    // Add to local list
    ScoreEntry newEntry{};
    newEntry.score = score;
    newEntry.lines = lines;
    newEntry.level = level;
    newEntry.timeSec = timeSec;
    newEntry.name = name;
    // preserve the game type locally so menu filtering works immediately
    newEntry.gameType = gameType;
    scores.push_back(newEntry);
    std::sort(scores.begin(), scores.end(), [](auto&a,auto&b){return a.score>b.score;});
    if (scores.size()>maxEntries) scores.resize(maxEntries);
    save();
    // Submit to Supabase asynchronously
    ScoreEntry se{score, lines, level, timeSec, name, gameType};
    supabase::SubmitHighscoreAsync(se);
}

bool ScoreManager::isHighScore(int score) const {
    if (scores.size() < maxEntries) return true;
    return score > scores.back().score;
}

void ScoreManager::replaceAll(const std::vector<ScoreEntry>& newScores) {
    scores = newScores;
    // Ensure ordering and trimming to our configured maxEntries
    std::sort(scores.begin(), scores.end(), [](auto&a,auto&b){return a.score>b.score;});
    if (scores.size() > maxEntries) scores.resize(maxEntries);
    // Persist new set to local file for next launch
    try { save(); } catch (...) { /* swallow */ }
}

void ScoreManager::createSampleScores() {
    scores = {
        {159840, 189, 14, 972.0, "GREGOR"},
        {156340, 132, 12, 714.0, "GREGOR"}, 
        {155219, 125, 12, 696.0, "GREGOR"},
        {141823, 123, 10, 710.0, "GREGOR"},
        {140079, 71, 11, 410.0, "GREGOR"},
        {116012, 121, 10, 619.0, "GREGOR"},
        {112643, 137, 13, 689.0, "GREGOR"},
        {99190, 61, 10, 378.0, "GREGOR"},
        {93648, 107, 10, 629.0, "GREGOR"},
        {89041, 115, 10, 618.0, "GREGOR"},
        {88600, 55, 9, 354.0, "GREGOR"},
        {86346, 141, 13, 723.0, "GREGOR"}
    };
}
