/**
 * Galaxy Strike Online — ContentLoader
 * Single source of truth for all game content.
 * Reads from data/gso/ JSON files at startup; never hardcodes data.
 *
 * Usage:
 *   const loader = new ContentLoader();
 *   await loader.load();
 *   const skills  = loader.getAllSkills();
 *   const recipes = loader.getAllRecipes();
 *   // etc.
 */

const fs   = require('fs');
const path = require('path');

const DATA_ROOT = path.join(__dirname, '..', 'data', 'gso');

class ContentLoader {
    constructor() {
        // Raw maps — populated by load()
        this.skills    = new Map();  // id -> skill
        this.enemies   = new Map();  // id -> enemy
        this.dungeons  = new Map();  // id -> dungeon
        this.recipes   = new Map();  // id -> recipe
        this.items     = new Map();  // id -> item
        this.quests    = new Map();  // id -> quest
        this.craftingTabs = [];
        this.questCategories = new Map(); // category -> category index

        this._loaded = false;
    }

    // ─────────────────────────────────────────────────────────────────
    // Public bootstrap
    // ─────────────────────────────────────────────────────────────────

    async load() {
        if (this._loaded) return;
        console.log('[CONTENT LOADER] Loading all game content from data/gso/…');

        this._loadSkills();
        this._loadEnemies();
        this._loadDungeons();
        this._loadItems();
        this._loadRecipes();
        this._loadQuests();

        this._loaded = true;

        console.log('[CONTENT LOADER] Done.');
        console.log(`  Skills:   ${this.skills.size}`);
        console.log(`  Enemies:  ${this.enemies.size}`);
        console.log(`  Dungeons: ${this.dungeons.size}`);
        console.log(`  Items:    ${this.items.size}`);
        console.log(`  Recipes:  ${this.recipes.size}`);
        console.log(`  Quests:   ${this.quests.size}`);
    }

    // ─────────────────────────────────────────────────────────────────
    // Skills
    // ─────────────────────────────────────────────────────────────────

    _loadSkills() {
        const root = path.join(DATA_ROOT, 'skills');
        for (const cat of this._subdirs(root)) {
            for (const file of this._jsonFiles(path.join(root, cat))) {
                const skill = this._readJson(path.join(root, cat, file));
                if (skill && skill.id) {
                    this.skills.set(skill.id, skill);
                }
            }
        }
    }

    getAllSkills()               { return Array.from(this.skills.values()); }
    getSkill(id)                { return this.skills.get(id) || null; }
    getSkillsByCategory(cat)    { return this.getAllSkills().filter(s => s.category === cat); }

    // ─────────────────────────────────────────────────────────────────
    // Enemies
    // ─────────────────────────────────────────────────────────────────

    _loadEnemies() {
        const root = path.join(DATA_ROOT, 'enemies');
        for (const file of this._jsonFiles(root)) {
            const enemy = this._readJson(path.join(root, file));
            if (enemy && enemy.id) this.enemies.set(enemy.id, enemy);
        }
    }

    getAllEnemies()           { return Array.from(this.enemies.values()); }
    getEnemy(id)             { return this.enemies.get(id) || null; }
    getEnemiesByRarity(r)    { return this.getAllEnemies().filter(e => e.rarity === r); }

    // ─────────────────────────────────────────────────────────────────
    // Dungeons
    // ─────────────────────────────────────────────────────────────────

    _loadDungeons() {
        const root = path.join(DATA_ROOT, 'dungeons');
        for (const file of this._jsonFiles(root)) {
            const dungeon = this._readJson(path.join(root, file));
            if (dungeon && dungeon.id) this.dungeons.set(dungeon.id, dungeon);
        }
    }

    getAllDungeons()               { return Array.from(this.dungeons.values()); }
    getDungeon(id)                { return this.dungeons.get(id) || null; }
    getDungeonsByDifficulty(d)    { return this.getAllDungeons().filter(dn => dn.difficulty === d); }
    getDungeonsGroupedByDifficulty() {
        const order = ['easy','medium','hard','extreme'];
        const out = {};
        for (const dn of this.getAllDungeons()) {
            if (!out[dn.difficulty]) out[dn.difficulty] = [];
            out[dn.difficulty].push(dn);
        }
        return out;
    }
    getAvailableDungeons(playerLevel = 1) {
        return this.getAllDungeons().filter(d => d.minLevel <= playerLevel);
    }

    // ─────────────────────────────────────────────────────────────────
    // Items — multi-type, multi-subdir
    // ─────────────────────────────────────────────────────────────────

    _loadItems() {
        const root = path.join(DATA_ROOT, 'items');
        this._walkJsonDir(root, (filePath) => {
            const data = this._readJson(filePath);
            if (!data || !data.templates) return;

            // Each file has { templates: { <type>: { ...itemData } } }
            for (const [type, itemData] of Object.entries(data.templates)) {
                if (itemData && itemData.id) {
                    this.items.set(itemData.id, { ...itemData, type });
                }
            }
        });
    }

    getAllItems()              { return Array.from(this.items.values()); }
    getItem(id)               { return this.items.get(id) || null; }
    getItemsByType(type)      { return this.getAllItems().filter(i => i.type === type); }
    getShopItems()            {
        return this.getAllItems().filter(i =>
            Array.isArray(i.categories) && i.categories.includes('shop')
        );
    }
    getShopItemsByCategory()  {
        const out = {};
        for (const item of this.getShopItems()) {
            const t = item.type;
            if (!out[t]) out[t] = [];
            out[t].push(item);
        }
        return out;
    }

    // ─────────────────────────────────────────────────────────────────
    // Recipes
    // ─────────────────────────────────────────────────────────────────

    _loadRecipes() {
        const root = path.join(DATA_ROOT, 'recipes');

        // crafting_tabs.json
        const tabsFile = path.join(root, 'crafting_tabs.json');
        if (fs.existsSync(tabsFile)) {
            this.craftingTabs = this._readJson(tabsFile)?.craftingTypes || [];
        }

        // One JSON per recipe, grouped into subdirs
        this._walkJsonDir(root, (filePath) => {
            if (filePath.endsWith('crafting_tabs.json')) return;
            if (filePath.endsWith('manifest.json'))      return;

            const data = this._readJson(filePath);
            if (!data || !data.recipe) return;

            // Derive a stable ID from the craft.id field or the path
            const id = data.craft?.id || data.path?.replace(/\//g, ':') || path.basename(filePath, '.json');

            // Normalise inputs → array format expected by client
            const inputs = data.recipe.inputs || {};
            const materials = Object.entries(inputs).map(([itemId, qty]) => ({ id: itemId, quantity: qty }));

            // Output item id
            const outputObj = data.recipe.output || {};
            const outputId  = Object.keys(outputObj)[0] || null;
            const outputQty = outputId ? outputObj[outputId] : 0;

            // Time — any *_time_seconds key
            const timeKey = Object.keys(data.recipe).find(k => k.endsWith('_time_seconds'));
            const craftingTime = timeKey ? (data.recipe[timeKey] * 1000) : 5000;

            this.recipes.set(id, {
                id,
                path:         data.path  || '',
                name:         outputId   ? this._itemName(outputId) : id,
                type:         data.craft?.type        || 'misc',
                category:     data.craft?.type        || 'misc',
                subCategory:  data.craft?.subCategory || 'misc',
                craftId:      data.craft?.id          || id,
                materials,
                output:       outputId,
                outputQty,
                craftingTime,
                experience:   Math.round(craftingTime / 200), // scaled default
                requirements: {},
                unlocked:     false,
                rawRecipe:    data.recipe
            });
        });
    }

    getAllRecipes()            { return Array.from(this.recipes.values()); }
    getRecipe(id)             { return this.recipes.get(id) || null; }
    getRecipesByType(type)    { return this.getAllRecipes().filter(r => r.type === type); }
    getRecipesByCategory(cat) { return this.getAllRecipes().filter(r => r.category === cat); }
    getCraftingTabs()         { return this.craftingTabs; }

    // ─────────────────────────────────────────────────────────────────
    // Quests
    // ─────────────────────────────────────────────────────────────────

    _loadQuests() {
        const root = path.join(DATA_ROOT, 'quests');
        for (const cat of this._subdirs(root)) {
            for (const file of this._jsonFiles(path.join(root, cat))) {
                const quest = this._readJson(path.join(root, cat, file));
                if (quest && quest.id) this.quests.set(quest.id, quest);
            }
        }

        // Category index files
        const catRoot = path.join(DATA_ROOT, 'category', 'quests');
        if (fs.existsSync(catRoot)) {
            for (const file of this._jsonFiles(catRoot)) {
                const cat = this._readJson(path.join(catRoot, file));
                if (cat && cat.tabId) this.questCategories.set(cat.tabId, cat);
            }
        }
    }

    getAllQuests()               { return Array.from(this.quests.values()); }
    getQuest(id)                { return this.quests.get(id) || null; }
    getQuestsByCategory(cat)    { return this.getAllQuests().filter(q => q.category === cat); }
    getQuestCategories()        { return Array.from(this.questCategories.values()); }
    getQuestsGroupedByCategory() {
        const out = {};
        for (const q of this.getAllQuests()) {
            if (!out[q.category]) out[q.category] = [];
            out[q.category].push(q);
        }
        return out;
    }

    // ─────────────────────────────────────────────────────────────────
    // Helpers
    // ─────────────────────────────────────────────────────────────────

    _readJson(filePath) {
        try {
            return JSON.parse(fs.readFileSync(filePath, 'utf8'));
        } catch (e) {
            console.warn(`[CONTENT LOADER] Failed to read ${filePath}:`, e.message);
            return null;
        }
    }

    _subdirs(dir) {
        if (!fs.existsSync(dir)) return [];
        return fs.readdirSync(dir).filter(f => {
            try { return fs.statSync(path.join(dir, f)).isDirectory(); } catch { return false; }
        });
    }

    _jsonFiles(dir) {
        if (!fs.existsSync(dir)) return [];
        return fs.readdirSync(dir).filter(f => f.endsWith('.json'));
    }

    /** Recursively walk a directory and call cb(filePath) for every .json file */
    _walkJsonDir(dir, cb) {
        if (!fs.existsSync(dir)) return;
        for (const entry of fs.readdirSync(dir)) {
            const full = path.join(dir, entry);
            const stat = fs.statSync(full);
            if (stat.isDirectory()) {
                this._walkJsonDir(full, cb);
            } else if (entry.endsWith('.json') && !entry.endsWith('manifest.json')) {
                cb(full);
            }
        }
    }

    /** Best-effort human name from an item id (snake_case → Title Case) */
    _itemName(id) {
        return id.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
    }
}

module.exports = ContentLoader;
