#include "InspectWindow.h"

#include <cmath>

#include <QCloseEvent>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QSignalMapper>
#include <QVBoxLayout>

#include "ArenaView.h"
#include "EntityAdmin.h"
#include "EventManager.h"
#include "HealthComponent.h"
#include "InspectWindowClosedEvent.h"
#include "ModuleOwnerComponent.h"
#include "SelectedBehaviorComponent.h"
#include "ShipIdentityComponent.h"
#include "ShipStatsCalculator.h"
#include "ShipStatsPanel.h"
#include "StationBodyComponent.h"
#include "WeaponComponent.h"

const double InspectWindow::kSpeeds[]   = { 0.0, 0.5, 1.0, 2.0, 10.0 };
const int    InspectWindow::kSpeedCount = 5;

InspectWindow::InspectWindow(ArenaSimulation* sim, const GameConfig* config,
                             const VisualsConfig* visuals,
                             const std::string& arenaName, QWidget* parent)
    : QWidget(parent)
    , m_sim(sim)
    , m_config(config)
{
    setWindowTitle(tr("Inspect \u2014 %1").arg(QString::fromStdString(arenaName)));
    resize(900, 700);
    setAttribute(Qt::WA_DeleteOnClose, false);

    QVBoxLayout* mainLayout = new QVBoxLayout(this);
    mainLayout->setContentsMargins(0, 0, 0, 0);
    mainLayout->setSpacing(0);

    // Header: arena name + speed buttons
    {
        QWidget* header = new QWidget(this);
        QHBoxLayout* headerLayout = new QHBoxLayout(header);
        headerLayout->setContentsMargins(8, 4, 8, 4);
        headerLayout->setSpacing(8);

        QLabel* nameLabel = new QLabel(QString::fromStdString(arenaName), header);
        QFont nameFont = nameLabel->font();
        nameFont.setBold(true);
        nameFont.setPointSize(nameFont.pointSize() + 2);
        nameLabel->setFont(nameFont);
        headerLayout->addWidget(nameLabel);

        m_durationLabel = new QLabel(header);
        headerLayout->addWidget(m_durationLabel);

        headerLayout->addStretch();

        const char* labels[] = { "0x", "0.5x", "1x", "2x", "10x" };
        QSignalMapper* mapper = new QSignalMapper(this);
        for (int i = 0; i < kSpeedCount; ++i)
        {
            QPushButton* btn = new QPushButton(labels[i], header);
            btn->setCheckable(true);
            btn->setChecked(i == 2);
            headerLayout->addWidget(btn);
            m_speedButtons.push_back(btn);
            mapper->setMapping(btn, i);
            connect(btn, &QPushButton::clicked,
                    mapper, qOverload<>(&QSignalMapper::map));
        }
        connect(mapper, qOverload<int>(&QSignalMapper::mapped),
                this, &InspectWindow::onSpeedButton);

        header->setFixedHeight(header->sizeHint().height());
        mainLayout->addWidget(header);
    }

    // Arena view (center, stretch)
    m_arenaView = new ArenaView(sim, visuals, this);
    mainLayout->addWidget(m_arenaView, 1);

    // Info panel (bottom)
    {
        QWidget* infoPanel = new QWidget(this);
        QHBoxLayout* infoLayout = new QHBoxLayout(infoPanel);
        infoLayout->setContentsMargins(8, 4, 8, 4);
        infoLayout->setSpacing(16);

        QVBoxLayout* team1Layout = new QVBoxLayout();
        m_team1Header = new QLabel(infoPanel);
        QFont headerFont = m_team1Header->font();
        headerFont.setBold(true);
        m_team1Header->setFont(headerFont);
        team1Layout->addWidget(m_team1Header);
        m_team1Threat = new QLabel(infoPanel);
        team1Layout->addWidget(m_team1Threat);
        m_team1Ehp = new QLabel(infoPanel);
        team1Layout->addWidget(m_team1Ehp);
        m_team1Content = new QLabel(infoPanel);
        team1Layout->addWidget(m_team1Content);
        team1Layout->addStretch();
        infoLayout->addLayout(team1Layout);

        QVBoxLayout* team2Layout = new QVBoxLayout();
        m_team2Header = new QLabel(infoPanel);
        m_team2Header->setFont(headerFont);
        team2Layout->addWidget(m_team2Header);
        m_team2Threat = new QLabel(infoPanel);
        team2Layout->addWidget(m_team2Threat);
        m_team2Ehp = new QLabel(infoPanel);
        team2Layout->addWidget(m_team2Ehp);
        m_team2Content = new QLabel(infoPanel);
        team2Layout->addWidget(m_team2Content);
        team2Layout->addStretch();
        infoLayout->addLayout(team2Layout);

        // Entity stats section (right side of info panel)
        QVBoxLayout* entityLayout = new QVBoxLayout();
        m_entityTitleLabel = new QLabel(infoPanel);
        QFont entityTitleFont = m_entityTitleLabel->font();
        entityTitleFont.setBold(true);
        m_entityTitleLabel->setFont(entityTitleFont);
        m_entityTitleLabel->hide();
        entityLayout->addWidget(m_entityTitleLabel);

        m_entityStatsPanel = new ShipStatsPanel(config, infoPanel);
        m_entityStatsPanel->hide();
        entityLayout->addWidget(m_entityStatsPanel);

        m_stationStatsLabel = new QLabel(infoPanel);
        m_stationStatsLabel->setWordWrap(true);
        m_stationStatsLabel->hide();
        entityLayout->addWidget(m_stationStatsLabel);

        entityLayout->addStretch();
        infoLayout->addLayout(entityLayout);

        mainLayout->addWidget(infoPanel);
    }

    // Poll timer for info panel updates
    m_pollTimer = new QTimer(this);
    connect(m_pollTimer, &QTimer::timeout, this, &InspectWindow::pollStatus);
    m_pollTimer->start(100);

    // Show initial status
    pollStatus();

    setFocusPolicy(Qt::StrongFocus);

    registerForEvents();
}

InspectWindow::~InspectWindow()
{
    unregisterForEvents();
}

void InspectWindow::closeEvent(QCloseEvent* event)
{
    m_arenaView->stopRendering();
    m_pollTimer->stop();
    EventManager::getInstance()->sendEventImmediately(
        std::make_shared<InspectWindowClosedEvent>());
    event->accept();
}

void InspectWindow::keyPressEvent(QKeyEvent* event)
{
    if (event->key() == Qt::Key_Space)
    {
        m_arenaView->togglePause();
    }
    else
    {
        QWidget::keyPressEvent(event);
    }
}

void InspectWindow::onSpeedButton(int index)
{
    if (index >= 0 && index < kSpeedCount)
    {
        m_arenaView->setGameSpeed(kSpeeds[index]);
    }
}

void InspectWindow::handleEvent(std::shared_ptr<const GameSpeedChangedEvent> event)
{
    for (int i = 0; i < kSpeedCount; ++i)
    {
        const bool active = (std::abs(kSpeeds[i] - event->speed) < 0.001);
        m_speedButtons[static_cast<std::size_t>(i)]->setChecked(active);
    }
}

void InspectWindow::pollStatus()
{
    const ArenaStatus status = m_sim->getStatus();
    updateInfoPanel(status);
    refreshEntityStats();
}

void InspectWindow::updateInfoPanel(const ArenaStatus& status)
{
    m_durationLabel->setText(status.finished
        ? tr("Duration: %1 s").arg(QString::number(status.durationSeconds, 'f', 1))
        : QString());

    for (int ti = 0; ti < 2; ++ti)
    {
        const ArenaStatus::TeamStatus& team = status.teams[ti];
        QLabel* header  = (ti == 0) ? m_team1Header  : m_team2Header;
        QLabel* threat  = (ti == 0) ? m_team1Threat  : m_team2Threat;
        QLabel* ehp     = (ti == 0) ? m_team1Ehp     : m_team2Ehp;
        QLabel* content = (ti == 0) ? m_team1Content  : m_team2Content;

        if (status.finished && status.winnerTeam == ti)
        {
            header->setText(tr("[WON] %1").arg(QString::fromStdString(team.name)));
        }
        else
        {
            header->setText(QString::fromStdString(team.name));
        }

        threat->setText(tr("Threat: %1").arg(QString::number(team.threatLevel, 'f', 0)));
        ehp->setText(tr("EHP: %1").arg(QString::fromStdString(team.getEhpPercentText())));

        QString lines;
        for (const ArenaStatus::Entry& entry : team.entries)
        {
            if (!lines.isEmpty())
            {
                lines += "\n";
            }
            lines += QString("%1/%2 %3")
                         .arg(entry.surviving)
                         .arg(entry.total)
                         .arg(QString::fromStdString(entry.displayName));
            if (entry.level.has_value())
            {
                lines += QString(" L%1").arg(entry.level.value());
            }
        }
        content->setText(lines);
    }
}

void InspectWindow::handleEvent(std::shared_ptr<const EntitySelectionChangedEvent> event)
{
    if (!event->entities.empty())
    {
        // The arena is single-select, so only the first entity is inspected.
        m_selectedEntity = event->entities.front();

        EntityAdmin& admin = m_sim->getAdmin();
        entt::entity entity = *m_selectedEntity;

        if (!admin.isValid(entity))
        {
            m_selectedEntity = std::nullopt;
            m_entityTitleLabel->hide();
            m_entityStatsPanel->hide();
            m_stationStatsLabel->hide();
            return;
        }

        if (admin.hasAll<ShipIdentityComponent>(entity))
        {
            const ShipIdentityComponent& identity = admin.get<ShipIdentityComponent>(entity);
            const HealthComponent& health = admin.get<HealthComponent>(entity);

            m_entityTitleLabel->setText(tr("Ship: %1")
                .arg(QString::fromStdString(identity.schematicId)));
            m_entityTitleLabel->show();

            const ShipStats stats = buildShipStatsFromEntity(admin, entity);
            m_entityStatsPanel->refreshFromLive(stats, health.hp);
            m_entityStatsPanel->setBehavior(
                admin.get<SelectedBehaviorComponent>(entity).winner);
            m_entityStatsPanel->show();
            m_stationStatsLabel->hide();
        }
        else if (admin.hasAll<StationBodyComponent>(entity))
        {
            const HealthComponent& health = admin.get<HealthComponent>(entity);

            m_entityTitleLabel->setText(tr("Defence Station"));
            m_entityTitleLabel->show();

            float totalDps = 0.0f;
            float maxRange = 0.0f;
            bool hasWeapons = false;

            admin.forEach<ModuleOwnerComponent, WeaponComponent>(
                [&](entt::entity /*child*/, const ModuleOwnerComponent& owner, const WeaponComponent& w)
                {
                    if (owner.owner != entity) { return; }
                    hasWeapons = true;
                    totalDps += w.damage * w.fireRateHz;
                    if (w.range_tiles > maxRange) { maxRange = w.range_tiles; }
                });

            QString statsText = tr("HP: %1 / %2")
                .arg(static_cast<int>(health.hp + 0.5f))
                .arg(static_cast<int>(health.maxHp + 0.5f));

            if (hasWeapons)
            {
                statsText += tr("\nDPS: %1").arg(QString::number(static_cast<double>(totalDps), 'f', 1));
                statsText += tr("\nRange: %1 tiles").arg(QString::number(static_cast<double>(maxRange), 'f', 1));
            }

            m_stationStatsLabel->setText(statsText);
            m_stationStatsLabel->show();
            m_entityStatsPanel->hide();
        }
    }
    else
    {
        m_selectedEntity = std::nullopt;
        m_entityTitleLabel->hide();
        m_entityStatsPanel->hide();
        m_stationStatsLabel->hide();
    }
}

void InspectWindow::refreshEntityStats()
{
    if (!m_selectedEntity.has_value()) { return; }

    EntityAdmin& admin = m_sim->getAdmin();
    entt::entity entity = *m_selectedEntity;

    if (!admin.isValid(entity))
    {
        m_selectedEntity = std::nullopt;
        m_entityTitleLabel->hide();
        m_entityStatsPanel->hide();
        m_stationStatsLabel->hide();
        return;
    }

    const HealthComponent& health = admin.get<HealthComponent>(entity);
    if (health.hp <= 0.0f)
    {
        m_selectedEntity = std::nullopt;
        m_entityTitleLabel->hide();
        m_entityStatsPanel->hide();
        m_stationStatsLabel->hide();
        return;
    }

    if (admin.hasAll<ShipIdentityComponent>(entity))
    {
        const ShipStats stats = buildShipStatsFromEntity(admin, entity);
        m_entityStatsPanel->refreshFromLive(stats, health.hp);
        m_entityStatsPanel->setBehavior(
            admin.get<SelectedBehaviorComponent>(entity).winner);
    }
    else if (admin.hasAll<StationBodyComponent>(entity))
    {
        float totalDps = 0.0f;
        float maxRange = 0.0f;
        bool hasWeapons = false;

        admin.forEach<ModuleOwnerComponent, WeaponComponent>(
            [&](entt::entity /*child*/, const ModuleOwnerComponent& owner, const WeaponComponent& w)
            {
                if (owner.owner != entity) { return; }
                hasWeapons = true;
                totalDps += w.damage * w.fireRateHz;
                if (w.range_tiles > maxRange) { maxRange = w.range_tiles; }
            });

        QString statsText = tr("HP: %1 / %2")
            .arg(static_cast<int>(health.hp + 0.5f))
            .arg(static_cast<int>(health.maxHp + 0.5f));

        if (hasWeapons)
        {
            statsText += tr("\nDPS: %1").arg(QString::number(static_cast<double>(totalDps), 'f', 1));
            statsText += tr("\nRange: %1 tiles").arg(QString::number(static_cast<double>(maxRange), 'f', 1));
        }

        m_stationStatsLabel->setText(statsText);
    }
}
