/*
 * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License v2 as published by the
 * Free Software Foundation.
 */

#ifndef MOONWELL_ENCOUNTER_JOURNAL_MGR_H
#define MOONWELL_ENCOUNTER_JOURNAL_MGR_H

#include "Define.h"

#include <cstddef>
#include <cstdint>
#include <string>
#include <unordered_map>
#include <vector>

namespace MoonWell::EncounterJournal
{
    struct Tier
    {
        uint32 id        = 0;
        std::string name;
        uint32 flags     = 0;
        int32  sortOrder = 0;
    };

    struct Instance
    {
        uint32 id              = 0;
        std::string name;
        std::string description;
        std::string buttonIcon;
        std::string smallButtonIcon;
        std::string background;
        std::string loreBackground;
        uint32 mapId           = 0;
        uint32 areaId          = 0;
        uint32 worldMapAreaId  = 0;
        uint32 flags           = 0;
        int32  sortOrder       = 0;
    };

    struct TierInstance
    {
        uint32 tierId     = 0;
        uint32 instanceId = 0;
        int32  sortOrder  = 0;
    };

    struct Encounter
    {
        uint32 id              = 0;
        uint32 instanceId      = 0;
        std::string name;
        std::string description;
        float mapPositionX     = 0.f;
        float mapPositionY     = 0.f;
        uint32 floorIndex      = 1;
        uint32 worldMapAreaId  = 0;
        uint32 firstSectionId  = 0;
        uint32 difficultyMask  = 0;
        uint32 flags           = 0;
        int32  sortOrder       = 0;
    };

    struct Creature
    {
        uint32 id                = 0;
        uint32 encounterId       = 0;
        uint32 creatureId        = 0;
        std::string name;
        std::string description;
        uint32 creatureDisplayId = 0;
        std::string icon;
        uint32 difficultyMask    = 0;
        int32  sortOrder         = 0;
    };

    struct Section
    {
        uint32 id                  = 0;
        uint32 encounterId         = 0;
        uint32 parentSectionId     = 0;
        uint32 nextSectionId       = 0;
        uint32 subSectionId        = 0;
        std::string name;
        std::string description;
        uint32 creatureDisplayId   = 0;
        uint32 descriptionSpellId  = 0;
        uint32 iconSpellId         = 0;
        std::string icon;        // empty when SQL value is NULL or ''
        uint32 flags               = 0;
        uint32 iconFlags           = 0;
        int32  sortOrder           = 0;
        uint32 type                = 0;
        uint32 difficultyMask      = 0;
        uint32 creatureId          = 0;
    };

    struct Loot
    {
        uint32 id              = 0;
        uint32 encounterId     = 0;
        uint32 itemId          = 0;
        uint32 difficultyMask  = 0;
        int32  factionMask     = -1;
        int32  classMask       = -1;
        uint32 flags           = 0;
        int32  sortOrder       = 0;
    };

    class Mgr
    {
    public:
        static Mgr* instance();

        // Loads all custom_ej_* tables into in-memory caches.
        // Safe to call repeatedly: previous data is dropped first.
        // Returns true on success (does not fail when tables are empty).
        bool Load();

        // Drops cached data.
        void Unload();

        // Writes the configured JSON file (always) and Lua file
        // (when Export.LuaFile is non-empty). On success @writtenPaths
        // contains the absolute paths created/overwritten.
        bool Export(std::vector<std::string>& writtenPaths, std::string& error);

        // Validates relational integrity. Returns the number of errors
        // found and pushes one human-readable line per error into @errors.
        std::size_t Validate(std::vector<std::string>& errors);

        // Imports loot rows from creature_loot_template via creature_template.lootid
        // for each creature card whose creature_id != 0. Reference rows are
        // followed one level. Existing rows in custom_ej_loot are skipped via
        // INSERT IGNORE on the unique key. Returns the number of inserted rows.
        std::size_t ImportLoot(std::vector<std::string>& warnings);

        // Generates skeleton EJ rows from existing AzerothCore tables:
        //  - custom_ej_tier      from MapEntry.expansionID buckets
        //  - custom_ej_instance  from instance_template + sMapStore (name, type)
        //  - custom_ej_encounter from instance_encounters (creditType=0 only)
        //  - custom_ej_creature  from creature_template referenced by encounters
        //  - custom_ej_section   one Overview placeholder per encounter
        // Existing rows are kept (INSERT IGNORE on PK). Returns total inserted
        // rows; per-table counts are reported via @counts (tier/instance/
        // tier_instance/encounter/creature/section).
        struct SkeletonCounts
        {
            std::size_t tiers           = 0;
            std::size_t instances       = 0;
            std::size_t tierInstances   = 0;
            std::size_t encounters      = 0;
            std::size_t creatures       = 0;
            std::size_t sections        = 0;
        };
        std::size_t ImportSkeleton(SkeletonCounts& counts,
                                   std::vector<std::string>& warnings);

        std::size_t TierCount()      const { return _tiers.size(); }
        std::size_t InstanceCount()  const { return _instances.size(); }
        std::size_t EncounterCount() const { return _encounters.size(); }
        std::size_t CreatureCount()  const { return _creatures.size(); }
        std::size_t SectionCount()   const { return _sections.size(); }
        std::size_t LootCount()      const { return _loot.size(); }

    private:
        Mgr() = default;

        std::string ResolveExportPath(std::string const& filename) const;
        std::string BuildJson() const;
        std::string BuildLua(std::string const& globalName) const;

        std::vector<Tier>            _tiers;
        std::vector<TierInstance>    _tierInstances;
        std::unordered_map<uint32, Instance>  _instances;
        std::unordered_map<uint32, Encounter> _encounters;
        std::unordered_map<uint32, Creature>  _creatures;
        std::unordered_map<uint32, Section>   _sections;
        std::vector<Loot>            _loot;

        // Sorted access caches (by sort_order, id). Filled at end of Load().
        std::vector<uint32> _instancesSorted;
        std::unordered_map<uint32, std::vector<uint32>> _encountersByInstance;
        std::unordered_map<uint32, std::vector<uint32>> _creaturesByEncounter;
        std::unordered_map<uint32, std::vector<std::size_t>> _lootByEncounter;
        std::unordered_map<uint32, std::vector<uint32>> _instancesByTier;
    };
}

#define sEncounterJournalMgr ::MoonWell::EncounterJournal::Mgr::instance()

#endif // MOONWELL_ENCOUNTER_JOURNAL_MGR_H
