#include "Torneo.h"
#include <cmath>
#include <algorithm>
#include <random>

// ─── Torneo (base) ────────────────────────────

Torneo::Torneo(const QString& nombre, const QString& juego)
    : m_nombre(nombre), m_juego(juego)
{}

void Torneo::addJugador(std::shared_ptr<Jugador> j) {
    if (m_estado != Estado::CONFIGURACION) return;
    m_jugadores.push_back(j);
}

bool Torneo::removeJugador(const QString& id) {
    if (m_estado != Estado::CONFIGURACION) return false;
    auto it = std::find_if(m_jugadores.begin(), m_jugadores.end(),
                           [&](const auto& j){ return j->getId() == id; });
    if (it == m_jugadores.end()) return false;
    m_jugadores.erase(it);
    return true;
}

void Torneo::iniciarTorneo() {
    if (m_estado != Estado::CONFIGURACION) return;
    if (m_jugadores.size() < 2) return;

    // Mezclar jugadores para seedings aleatorios
    std::shuffle(m_jugadores.begin(), m_jugadores.end(),
                 std::mt19937{std::random_device{}()});

    m_estado = Estado::EN_CURSO;
    generarRondaInicial();
    m_rondaActual = 0;
}

void Torneo::avanzarRonda() {
    auto rondaActualPtr = getRondaActual();
    if (!rondaActualPtr || !rondaActualPtr->isCompleta()) return;

    auto ganadores = rondaActualPtr->getGanadores();

    if (ganadores.size() <= 1) {
        // Torneo terminado
        if (!ganadores.empty())
            setCampeon(ganadores[0]);
        m_estado = Estado::FINALIZADO;
        return;
    }

    generarSiguienteRonda(ganadores);
    ++m_rondaActual;
}

std::shared_ptr<Ronda> Torneo::getRondaActual() const {
    if (m_rondaActual < 0 || m_rondaActual >= (int)m_rondas.size())
        return nullptr;
    return m_rondas[m_rondaActual];
}

void Torneo::agregarRonda(std::shared_ptr<Ronda> r) {
    m_rondas.push_back(r);
}

void Torneo::setCampeon(std::shared_ptr<Jugador> j) {
    m_campeon = j;
}

int Torneo::totalRondasEsperadas() const {
    if (m_jugadores.empty()) return 0;
    return static_cast<int>(std::ceil(std::log2(m_jugadores.size())));
}

QString Torneo::resumen() const {
    return QString("Torneo: %1 | Juego: %2 | Tipo: %3 | Jugadores: %4")
        .arg(m_nombre, m_juego, tipo())
        .arg(m_jugadores.size());
}

// ─── TorneoEliminatorioSimple ─────────────────

TorneoEliminatorioSimple::TorneoEliminatorioSimple(const QString& nombre, const QString& juego)
    : Torneo(nombre, juego)
{}

std::shared_ptr<Ronda> TorneoEliminatorioSimple::crearRondaDesdeJugadores(
    const std::vector<std::shared_ptr<Jugador>>& jugadores,
    int numero, const QString& nombreRonda)
{
    auto ronda = std::make_shared<Ronda>(numero, nombreRonda);

    // Emparejar: j[0] vs j[1], j[2] vs j[3], ...
    for (size_t i = 0; i + 1 < jugadores.size(); i += 2) {
        ronda->addPartida(std::make_shared<Partida>(jugadores[i], jugadores[i + 1]));
    }
    // Si número impar → BYE para el último
    if (jugadores.size() % 2 != 0) {
        ronda->addPartida(std::make_shared<Partida>(jugadores.back()));
    }
    return ronda;
}

void TorneoEliminatorioSimple::generarRondaInicial() {
    QString nombre = (m_jugadores.size() <= 2) ? "Final" :
                     (m_jugadores.size() <= 4) ? "Semifinales" :
                     (m_jugadores.size() <= 8) ? "Cuartos de Final" : "Ronda 1";
    auto ronda = crearRondaDesdeJugadores(m_jugadores, 1, nombre);
    agregarRonda(ronda);
}

void TorneoEliminatorioSimple::generarSiguienteRonda(
    const std::vector<std::shared_ptr<Jugador>>& ganadores)
{
    int num = (int)m_rondas.size() + 1;
    QString nombre = (ganadores.size() <= 2) ? "Final" :
                     (ganadores.size() <= 4) ? "Semifinales" :
                     (ganadores.size() <= 8) ? "Cuartos de Final" :
                     QString("Ronda %1").arg(num);
    auto ronda = crearRondaDesdeJugadores(ganadores, num, nombre);
    agregarRonda(ronda);
}

// ─── TorneoDobleEliminacion ───────────────────

TorneoDobleEliminacion::TorneoDobleEliminacion(const QString& nombre, const QString& juego)
    : Torneo(nombre, juego)
{}

void TorneoDobleEliminacion::generarRondaInicial() {
    auto ronda = std::make_shared<Ronda>(1, "Ronda 1 — Bracket Principal");
    for (size_t i = 0; i + 1 < m_jugadores.size(); i += 2)
        ronda->addPartida(std::make_shared<Partida>(m_jugadores[i], m_jugadores[i + 1]));
    if (m_jugadores.size() % 2 != 0)
        ronda->addPartida(std::make_shared<Partida>(m_jugadores.back()));
    agregarRonda(ronda);
}

void TorneoDobleEliminacion::generarSiguienteRonda(
    const std::vector<std::shared_ptr<Jugador>>& ganadores)
{
    // Recolectar perdedores de la ronda anterior para el bracket de perdedores
    auto rondaAnterior = getRondaActual();
    if (rondaAnterior) {
        for (const auto& p : rondaAnterior->getPartidas()) {
            if (p->getEstado() == Partida::Estado::FINALIZADA) {
                auto j1 = p->getJugador1();
                auto j2 = p->getJugador2();
                auto winner = p->getGanador();
                auto loser = (winner == j1) ? j2 : j1;
                if (loser && !loser->isEliminado())
                    ; // ya marcado en Partida
                // Si tiene 1 derrota, va al bracket de perdedores
                if (loser && loser->getDerrotas() == 1) {
                    loser->restaurar(); // quitar bandera eliminado
                    loser->registrarDerrota(); // re-registrar la derrota
                    m_bracketPerdedores.push_back(loser);
                }
            }
        }
    }

    int num = (int)m_rondas.size() + 1;

    // Partidas en bracket principal
    auto ronda = std::make_shared<Ronda>(num,
        ganadores.size() <= 2 ? "Gran Final" : QString("Ronda %1 — Principal").arg(num));

    for (size_t i = 0; i + 1 < ganadores.size(); i += 2)
        ronda->addPartida(std::make_shared<Partida>(ganadores[i], ganadores[i + 1]));

    // Si hay bracket de perdedores, agregar esas partidas también
    if (!m_bracketPerdedores.empty() && m_bracketPerdedores.size() >= 2) {
        for (size_t i = 0; i + 1 < m_bracketPerdedores.size(); i += 2) {
            auto p = std::make_shared<Partida>(m_bracketPerdedores[i], m_bracketPerdedores[i+1]);
            ronda->addPartida(p);
        }
        m_bracketPerdedores.clear();
    }

    agregarRonda(ronda);
}
