#include "BlueprintPanel.h"

#include <algorithm>
#include <climits>

#include <QCoreApplication>
#include <QFile>
#include <QHBoxLayout>
#include <QInputDialog>
#include <QMessageBox>
#include <QPushButton>
#include <QScrollArea>
#include <QVBoxLayout>

#include "BlueprintPlacementRequestedEvent.h"
#include "BlueprintSerializer.h"
#include "BuildingBlocksChangedEvent.h"
#include "EventManager.h"
#include "ExitBlueprintModeRequestedEvent.h"

#include "Building.h"
#include "BuildingConfig.h"
#include "BuildingSystem.h"
#include "Simulation.h"

BlueprintPanel::BlueprintPanel(Simulation* sim, const GameConfig* config, QWidget* parent)
    : QWidget(parent)
    , m_sim(sim)
    , m_config(config)
{
    QVBoxLayout* layout = new QVBoxLayout(this);
    layout->setContentsMargins(4, 4, 4, 4);
    layout->setSpacing(4);

    m_createBtn = new QPushButton(tr("Create Blueprint"), this);
    m_createBtn->setFixedHeight(48);
    m_createBtn->setEnabled(false);
    layout->addWidget(m_createBtn);

    QScrollArea* scrollArea = new QScrollArea(this);
    scrollArea->setWidgetResizable(true);
    scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

    m_buttonsContainer = new QWidget(scrollArea);
    m_buttonsLayout    = new QVBoxLayout(m_buttonsContainer);
    m_buttonsLayout->setContentsMargins(0, 0, 0, 0);
    m_buttonsLayout->setSpacing(4);
    m_buttonsLayout->addStretch();

    scrollArea->setWidget(m_buttonsContainer);
    layout->addWidget(scrollArea, 1);

    connect(m_createBtn, &QPushButton::clicked, this, &BlueprintPanel::onCreateClicked);

    loadFromDisk();
    rebuildButtons();

    registerForEvents();
}

BlueprintPanel::~BlueprintPanel()
{
    saveToDisk();
    unregisterForEvents();
}

void BlueprintPanel::onSelectionChanged(const std::vector<BuildingId>& ids)
{
    m_selectedBuildingIds = ids;
    refreshButtonStates();
}

void BlueprintPanel::handleEvent(std::shared_ptr<const BuildingBlocksChangedEvent> /*event*/)
{
    refreshButtonStates();
}

void BlueprintPanel::clearActiveBlueprintButton()
{
    if (m_activeIndex.has_value() && *m_activeIndex < static_cast<int>(m_blueprintButtons.size()))
    {
        m_blueprintButtons[static_cast<std::size_t>(*m_activeIndex)]->setChecked(false);
    }
    m_activeIndex = std::nullopt;
    refreshButtonStates();
}

void BlueprintPanel::onCreateClicked()
{
    if (m_selectedBuildingIds.empty()) { return; }

    Blueprint bp = createBlueprintFromSelection();
    if (bp.buildings.empty()) { return; }

    bool ok = false;
    const QString name = QInputDialog::getText(
        this, tr("Create Blueprint"), tr("Blueprint name:"), QLineEdit::Normal, QString(), &ok);
    if (!ok || name.trimmed().isEmpty()) { return; }

    bp.name = name.trimmed();
    m_blueprints.push_back(bp);
    rebuildButtons();
}

void BlueprintPanel::onDeleteBlueprintClicked(int index)
{
    if (m_activeIndex == index)
    {
        m_activeIndex = std::nullopt;
        EventManager::getInstance()->sendEventImmediately(
            std::make_shared<ExitBlueprintModeRequestedEvent>());
    }
    else if (m_activeIndex.has_value() && *m_activeIndex > index)
    {
        --*m_activeIndex;
    }
    m_blueprints.erase(m_blueprints.begin() + index);
    rebuildButtons();
}

void BlueprintPanel::onBlueprintButtonClicked(int index)
{
    if (index < 0 || index >= static_cast<int>(m_blueprints.size())) { return; }

    if (m_activeIndex == index)
    {
        clearActiveBlueprintButton();
        EventManager::getInstance()->sendEventImmediately(
            std::make_shared<ExitBlueprintModeRequestedEvent>());
        return;
    }

    if (m_activeIndex.has_value() && *m_activeIndex < static_cast<int>(m_blueprintButtons.size()))
    {
        m_blueprintButtons[static_cast<std::size_t>(*m_activeIndex)]->setChecked(false);
    }

    m_activeIndex = index;
    m_blueprintButtons[static_cast<std::size_t>(index)]->setChecked(true);
    EventManager::getInstance()->sendEventImmediately(
        std::make_shared<BlueprintPlacementRequestedEvent>(m_blueprints[static_cast<std::size_t>(index)]));
}

Blueprint BlueprintPanel::createBlueprintFromSelection() const
{
    // Capture is shared, testable logic in lib/sim: it resolves each selected id as an
    // operational building or a construction site alike (REQ-UI-BLUEPRINT-CREATE,
    // REQ-UI-BLUEPRINT-STORAGE).
    return captureBlueprintFromSelection(*m_sim, m_selectedBuildingIds);
}

int BlueprintPanel::computeBlueprintCost(const Blueprint& bp) const
{
    int total = 0;
    for (const BlueprintBuilding& bb : bp.buildings)
    {
        const BuildingDef* def = m_config->buildings.findBuildingDef(bb.type);
        if (def) { total += def->cost; }
    }
    return total;
}

void BlueprintPanel::rebuildButtons()
{
    while (m_buttonsLayout->count() > 1)
    {
        QLayoutItem* item = m_buttonsLayout->takeAt(0);
        if (item->widget()) { delete item->widget(); }
        delete item;
    }
    m_blueprintButtons.clear();

    for (int i = 0; i < static_cast<int>(m_blueprints.size()); ++i)
    {
        const Blueprint& bp   = m_blueprints[static_cast<std::size_t>(i)];
        const int        cost = computeBlueprintCost(bp);
        const QString label   = bp.name + "\n" + tr("%1 Building Blocks").arg(cost);

        QWidget*     row       = new QWidget(m_buttonsContainer);
        QHBoxLayout* rowLayout = new QHBoxLayout(row);
        rowLayout->setContentsMargins(0, 0, 0, 0);
        rowLayout->setSpacing(4);

        QPushButton* btn = new QPushButton(label, row);
        btn->setCheckable(true);
        btn->setFixedHeight(48);

        QPushButton* delBtn = new QPushButton("\xc3\x97", row);
        delBtn->setFixedWidth(28);
        delBtn->setFixedHeight(48);

        rowLayout->addWidget(btn, 1);
        rowLayout->addWidget(delBtn, 0);

        m_buttonsLayout->insertWidget(i, row);

        const int capturedIndex = i;
        connect(btn, &QPushButton::clicked, this, [this, capturedIndex]() {
            onBlueprintButtonClicked(capturedIndex);
        });
        connect(delBtn, &QPushButton::clicked, this, [this, capturedIndex]() {
            onDeleteBlueprintClicked(capturedIndex);
        });

        m_blueprintButtons.push_back(btn);
    }

    refreshButtonStates();
}

void BlueprintPanel::saveToDisk() const
{
    // Persist on shutdown; write errors are silently ignored (REQ-UI-BLUEPRINT-SAVE).
    const QString path = QCoreApplication::applicationDirPath() + "/blueprints.toml";
    QFile file(path);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { return; }
    try
    {
        const std::string content = BlueprintSerializer::serialize(m_blueprints);
        file.write(QByteArray::fromStdString(content));
    }
    catch (...) {}
}

void BlueprintPanel::loadFromDisk()
{
    // Load at startup (REQ-UI-BLUEPRINT-LOAD). Missing file: start empty, no error.
    const QString path = QCoreApplication::applicationDirPath() + "/blueprints.toml";
    QFile file(path);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { return; }
    try
    {
        m_blueprints = BlueprintSerializer::deserialize(file.readAll().toStdString());
    }
    catch (const std::exception& e)
    {
        QMessageBox::critical(this, tr("Load Failed"),
            tr("Failed to load blueprints:\n%1").arg(e.what()));
        m_blueprints.clear();
    }
}

void BlueprintPanel::refreshButtonStates()
{
    // A construction site counts the same as an operational building (REQ-UI-BLUEPRINT-CREATE).
    m_createBtn->setEnabled(selectionHasPlaceableBuilding(*m_sim, m_selectedBuildingIds));

    const int blocks = m_sim->getBuildingBlocksStock();

    for (int i = 0; i < static_cast<int>(m_blueprintButtons.size()); ++i)
    {
        const int  cost      = computeBlueprintCost(m_blueprints[static_cast<std::size_t>(i)]);
        const bool canAfford = blocks >= cost;
        m_blueprintButtons[static_cast<std::size_t>(i)]->setEnabled(
            canAfford || m_activeIndex == i);
    }
}

void BlueprintPanel::handleEvent(std::shared_ptr<const SelectionChangedEvent> event)
{
    onSelectionChanged(event->ids);
}

void BlueprintPanel::handleEvent(std::shared_ptr<const BlueprintModeExitedEvent> /*event*/)
{
    clearActiveBlueprintButton();
}

void BlueprintPanel::handleEvent(std::shared_ptr<const TemporaryBlueprintRequestedEvent> /*event*/)
{
    // Temporary blueprint (REQ-UI-BLUEPRINT-TEMP): build from the current selection and
    // enter placement mode without adding it to the list or persisting it. If nothing
    // player-placeable is selected, do nothing.
    Blueprint bp = createBlueprintFromSelection();
    if (bp.buildings.empty()) { return; }

    // No saved blueprint is active while a temporary one is being placed.
    clearActiveBlueprintButton();
    EventManager::getInstance()->sendEventImmediately(
        std::make_shared<BlueprintPlacementRequestedEvent>(std::move(bp)));
}
