#include "game/gameplay/stats.hpp"

#include <fstream>  // Para basic_ostream, basic_ifstream, basic_istream
#include <sstream>  // Para basic_stringstream
#include <utility>

#include "game/options.hpp"  // Para Options, OptionsStats, options

// Constructor
Stats::Stats(std::string file, std::string buffer)
    : buffer_path_(std::move(buffer)),
      file_path_(std::move(file)) {}

// Destructor
Stats::~Stats() {
    // Vuelca los datos del buffer en la lista de estadisticas
    updateListFromBuffer();
}

// Inicializador
void Stats::init()
// Se debe llamar a este procedimiento una vez se haya creado el diccionario numero-nombre
{
    loadFromFile(buffer_path_, buffer_list_);
    loadFromFile(file_path_, list_);

    // Vuelca los datos del buffer en la lista de estadisticas
    updateListFromBuffer();
}

// Añade una muerte a las estadisticas
void Stats::addDeath(const std::string& name) {
    // Primero busca si ya hay una entrada con ese nombre
    const int INDEX = findByName(name, buffer_list_);
    if (INDEX != -1) {
        buffer_list_[INDEX].died++;
    }

    // En caso contrario crea la entrada
    else {
        RoomData item;
        item.name = name;
        item.visited = 0;
        item.died = 1;
        buffer_list_.push_back(item);
    }
}

// Añade una visita a las estadisticas
void Stats::addVisit(const std::string& name) {
    // Primero busca si ya hay una entrada con ese nombre
    const int INDEX = findByName(name, buffer_list_);
    if (INDEX != -1) {
        buffer_list_[INDEX].visited++;
    }

    // En caso contrario crea la entrada
    else {
        RoomData item;
        item.name = name;
        item.visited = 1;
        item.died = 0;
        buffer_list_.push_back(item);
    }
}

// Busca una entrada en la lista por nombre
auto Stats::findByName(const std::string& name, const std::vector<RoomData>& list) -> int {
    int i = 0;

    for (const auto& l : list) {
        if (l.name == name) {
            return i;
        }
        i++;
    }

    return -1;
}

// Carga las estadisticas desde un fichero
void Stats::loadFromFile(const std::string& file_path, std::vector<RoomData>& list) {
    list.clear();

    // Variables para manejar el fichero
    std::ifstream file(file_path);

    // Si el fichero se puede abrir
    if (file.good()) {
        std::string line;
        // Procesa el fichero linea a linea
        while (std::getline(file, line)) {
            // Remove Windows line ending if present
            if (!line.empty() && line.back() == '\r') {
                line.pop_back();
            }
            // Comprueba que la linea no sea un comentario ni esté vacía
            if (line.empty() || line.starts_with("#")) {
                continue;
            }

            RoomData stat;
            std::stringstream ss(line);
            std::string tmp;

            // Obtiene el nombre
            getline(ss, tmp, ';');
            stat.name = tmp;

            // Obtiene las visitas y muertes
            try {
                getline(ss, tmp, ';');
                stat.visited = std::stoi(tmp);

                getline(ss, tmp, ';');
                stat.died = std::stoi(tmp);
            } catch (const std::exception&) {
                // Línea con formato incorrecto, la ignora
                continue;
            }

            list.push_back(stat);
        }

        // Cierra el fichero
        file.close();
    }

    // El fichero no existe
    else {
        // Crea el fichero con los valores por defecto
        saveToFile(file_path, list);
    }
}

// Guarda las estadisticas en un fichero
void Stats::saveToFile(const std::string& file_path, const std::vector<RoomData>& list) {
    // Crea y abre el fichero de texto
    std::ofstream file(file_path);

    // Escribe en el fichero
    file << "# ROOM NAME;VISITS;DEATHS" << '\n';
    for (const auto& item : list) {
        file << item.name << ";" << item.visited << ";" << item.died << '\n';
    }

    // Cierra el fichero
    file.close();
}

// Calcula cual es la habitación con más muertes
void Stats::checkWorstNightmare() {
    int deaths = 0;
    for (const auto& item : list_) {
        if (item.died > deaths) {
            deaths = item.died;
            Options::stats.worst_nightmare = item.name;
        }
    }
}

// Vuelca los datos del buffer en la lista de estadisticas
void Stats::updateListFromBuffer() {
    // Actualiza list_ desde buffer_list_
    for (const auto& buffer : buffer_list_) {
        int index = findByName(buffer.name, list_);

        if (index != -1) {  // Encontrado. Aumenta sus estadisticas
            list_[index].visited += buffer.visited;
            list_[index].died += buffer.died;
        } else {  // En caso contrario crea la entrada
            RoomData item;
            item.name = buffer.name;
            item.visited = buffer.visited;
            item.died = buffer.died;
            list_.push_back(item);
        }
    }

    // Limpia el buffer después de volcarlo
    buffer_list_.clear();

    // Calcula cual es la habitación con más muertes
    checkWorstNightmare();

    // Guarda las estadísticas
    saveToFile(buffer_path_, buffer_list_);
    saveToFile(file_path_, list_);
}