#pragma once

#include <optional>
#include <vector>

#include <QString>

#include "Blueprint.h"
#include "BlueprintModeExitedEvent.h"
#include "BuildingId.h"
#include "EventHandler.h"
#include "GameConfig.h"
#include "GameResetEvent.h"
#include "SelectionChangedEvent.h"
#include "TemporaryBlueprintCaptureRequestedEvent.h"
#include "TemporaryBlueprintPlaceRequestedEvent.h"

class Simulation;
class QWidget;

// The player's saved blueprints: the list itself, its persistence (REQ-UI-BLUEPRINT-SAVE,
// REQ-UI-BLUEPRINT-LOAD), capture from the current selection (REQ-UI-BLUEPRINT-CREATE,
// REQ-UI-BLUEPRINT-TEMP), and entry into blueprint placement mode.
//
// Deliberately not a widget: blueprints have no permanent place on screen any more
// (REQ-UI-BLUEPRINT-DIALOG), and the modal dialogs that present them must be driven from
// MainWindow, the only widget that can pause the game (ModalPauseScope) and raise the dim
// overlay. This class is the model those dialogs read and mutate.
class BlueprintLibrary
    : public CombinedEventHandler<SelectionChangedEvent,
                                  BlueprintModeExitedEvent,
                                  TemporaryBlueprintCaptureRequestedEvent,
                                  TemporaryBlueprintPlaceRequestedEvent,
                                  GameResetEvent>
{
public:
    // dialogParent parents the load-failure message box (REQ-UI-BLUEPRINT-LOAD) and is
    // not owned.
    BlueprintLibrary(Simulation* sim, const GameConfig* config, QWidget* dialogParent);
    ~BlueprintLibrary();

    // True when the current selection holds at least one player-placeable building or
    // construction site -- the condition under which Ctrl+C has any effect
    // (REQ-UI-BLUEPRINT-CREATE).
    bool getCanCaptureSelection() const;

    // True once C has captured something -- the condition under which V does anything
    // (REQ-UI-BLUEPRINT-TEMP), and so the condition under which the controls panel
    // offers it (REQ-UI-CONTROLS-ACCURACY).
    bool getHasTemporaryBlueprint() const;

    // Captures the current selection under the given name and appends it to the list.
    // Silently does nothing when nothing player-placeable is selected.
    void saveSelectionAs(const QString& name);

    const std::vector<Blueprint>& getBlueprints() const;

    // The blueprint's contents line, ready for its card: one "<name> x <count>" entry
    // per building type, comma-separated, ordered by summarizeBlueprintContents
    // (REQ-UI-BLUEPRINT-CARD).
    QString getContentsSummary(int index) const;

    // Sum of the constituent buildings' placement costs (REQ-UI-BLUEPRINT-CARD).
    int getCost(int index) const;

    // Whether the player can currently afford the blueprint's total cost, which is what
    // enables or greys out its card (REQ-UI-BLUEPRINT-CARD).
    bool getCanAfford(int index) const;

    // Removes the blueprint, exiting blueprint placement mode if it was the active one
    // (REQ-UI-BLUEPRINT-DELETE).
    void remove(int index);

    // Enters blueprint placement mode for the blueprint (REQ-UI-BLUEPRINT-MODE).
    void beginPlacement(int index);

private:
    void handleEvent(std::shared_ptr<const SelectionChangedEvent> event) override;
    void handleEvent(std::shared_ptr<const BlueprintModeExitedEvent> event) override;
    void handleEvent(
        std::shared_ptr<const TemporaryBlueprintCaptureRequestedEvent> event) override;
    void handleEvent(
        std::shared_ptr<const TemporaryBlueprintPlaceRequestedEvent> event) override;
    void handleEvent(std::shared_ptr<const GameResetEvent> event) override;

    Blueprint createBlueprintFromSelection() const;
    void      loadFromDisk();
    void      saveToDisk() const;

    // The simulation is the single source of truth for the block stock and the
    // selection; the change events are only refresh signals.
    Simulation*             m_sim;
    const GameConfig*       m_config;
    QWidget*                m_dialogParent;
    std::vector<BuildingId> m_selectedBuildingIds;
    std::vector<Blueprint>  m_blueprints;
    // Index of the blueprint currently in placement mode, so deleting it can exit that
    // mode (REQ-UI-BLUEPRINT-DELETE). nullopt = no saved blueprint is being placed.
    std::optional<int>      m_activeIndex;
    // The one unnamed blueprint captured with C and re-placed with V
    // (REQ-UI-BLUEPRINT-TEMP). Deliberately kept out of m_blueprints: it is never named,
    // never listed in the selection dialog, and never written to blueprints.toml. It
    // outlives its placement mode, so V can re-enter it. nullopt = none captured since
    // startup or the last restart.
    std::optional<Blueprint> m_temporaryBlueprint;
};
