/**
 * Galaxy Strike Online - Client Skill System
 * Skill definitions are loaded from the server; this file handles
 * local progression tracking, UI rendering, and skill-point spending.
 */

class SkillSystem {
    constructor(gameEngine) {
        this.game = gameEngine;

        // Populated after server responds to 'get_skills'
        this.skills = { combat: {}, science: {}, crafting: {} };

        this.categories = {
            combat:   'Combat',
            science:  'Science',
            crafting: 'Crafting'
        };

        this.experienceRates = { combat: 1.0, science: 0.8, crafting: 0.6 };
        this.activeBuffs = {};

        // Loading state
        this._loaded = false;
        this._loading = false;
    }

    // ------------------------------------------------------------------ //
    // Initialisation — request skill definitions from the server
    // ------------------------------------------------------------------ //
    async initialize() {
        if (this._loaded || this._loading) return;
        this._loading = true;

        console.log('[SKILL SYSTEM] Requesting skill definitions from server');

        if (!window.game?.socket) {
            console.warn('[SKILL SYSTEM] No socket connection — skills will load when connected');
            this._loading = false;
            return;
        }

        try {
            const serverSkills = await this._fetchSkillsFromServer();
            this._applyServerDefinitions(serverSkills);
            this._loaded = true;
            console.log('[SKILL SYSTEM] Skill definitions loaded from server');
        } catch (err) {
            console.error('[SKILL SYSTEM] Failed to load skills from server:', err);
        } finally {
            this._loading = false;
        }
    }

    _fetchSkillsFromServer() {
        return new Promise((resolve, reject) => {
            const socket = window.game.socket;

            const timeout = setTimeout(() => {
                socket.off('skills_data', handler);
                reject(new Error('Skill data request timed out'));
            }, 10000);

            const handler = (data) => {
                clearTimeout(timeout);
                socket.off('skills_data', handler);
                if (data && (Array.isArray(data) || typeof data === 'object')) {
                    resolve(data);
                } else {
                    reject(new Error('Invalid skill data from server'));
                }
            };

            socket.on('skills_data', handler);
            socket.emit('get_skills');
        });
    }

    /**
     * Merge server skill definitions into the local skill map.
     * Preserves any progress already loaded from playerData.
     */
    _applyServerDefinitions(serverSkills) {
        // Server may return an array or a categorised object
        const asList = Array.isArray(serverSkills)
            ? serverSkills
            : Object.values(serverSkills).flat();

        // Reset to empty categories first
        this.skills = { combat: {}, science: {}, crafting: {} };

        for (const skill of asList) {
            const cat = skill.category || 'combat';
            if (!this.skills[cat]) this.skills[cat] = {};

            // Keep any existing progress if already loaded from save data
            const existing = this.skills[cat][skill.id] || {};
            this.skills[cat][skill.id] = {
                name:           skill.name,
                description:    skill.description,
                maxLevel:       skill.maxLevel || 100,
                effects:        skill.bonuses || skill.effects || {},
                icon:           skill.icon || 'fa-star',
                // Progress fields — kept from existing save data if present
                currentLevel:   existing.currentLevel   ?? 0,
                experience:     existing.experience     ?? 0,
                experienceToNext: existing.experienceToNext ?? (skill.experiencePerLevel || 1000),
                unlocked:       existing.unlocked       ?? (skill.defaultUnlocked !== false),
                requiredLevel:  skill.requiredLevel     ?? null
            };
        }

        console.log('[SKILL SYSTEM] Applied server definitions. Categories:', Object.keys(this.skills));
    }

    // ------------------------------------------------------------------ //
    // Skill progression
    // ------------------------------------------------------------------ //
    addSkillExperience(category, skillId, amount) {
        const skill = this.skills[category]?.[skillId];
        if (!skill || skill.currentLevel >= skill.maxLevel) return false;

        skill.experience += amount;

        while (skill.experience >= skill.experienceToNext && skill.currentLevel < skill.maxLevel) {
            this.levelUpSkill(category, skillId);
        }

        this.applySkillEffects();
        return true;
    }

    levelUpSkill(category, skillId) {
        const skill = this.skills[category][skillId];
        const excess = skill.experience - skill.experienceToNext;

        skill.currentLevel++;
        skill.experienceToNext = Math.floor(skill.experienceToNext * 1.5);
        skill.experience = Math.max(0, excess);

        this.applySkillEffects();
        this.game.showNotification(`${skill.name} leveled up to ${skill.currentLevel}!`, 'success', 4000);
    }

    upgradeSkill(category, skillId) {
        const skill  = this.skills[category]?.[skillId];
        const player = this.game.systems.player;

        if (!skill)                              { this.game.showNotification('Skill not found', 'error', 3000); return false; }
        if (!skill.unlocked)                     { this.game.showNotification('Skill is locked', 'error', 3000); return false; }
        if (skill.currentLevel >= skill.maxLevel){ this.game.showNotification('Skill is at maximum level', 'warning', 3000); return false; }
        if (player.stats.skillPoints < 1)        { this.game.showNotification('Not enough skill points', 'error', 3000); return false; }

        player.stats.skillPoints--;
        this.levelUpSkill(category, skillId);

        if (window.smartSaveManager?.isMultiplayer || this.game?.isRunning) {
            this.updateUI();
        }
        return true;
    }

    unlockSkill(category, skillId) {
        const skill  = this.skills[category]?.[skillId];
        const player = this.game.systems.player;

        if (!skill)          { this.game.showNotification('Skill not found', 'error', 3000); return false; }
        if (skill.unlocked)  { this.game.showNotification('Skill is already unlocked', 'warning', 3000); return false; }
        if (skill.requiredLevel && player.stats.level < skill.requiredLevel) {
            this.game.showNotification(`Requires level ${skill.requiredLevel}`, 'error', 3000);
            return false;
        }
        if (player.stats.skillPoints < 2) {
            this.game.showNotification('Requires 2 skill points to unlock', 'error', 3000);
            return false;
        }

        player.stats.skillPoints -= 2;
        skill.unlocked = true;
        skill.currentLevel = 1;
        this.applySkillEffects();

        if (window.smartSaveManager?.isMultiplayer || this.game?.isRunning) {
            this.updateUI();
        }
        this.game.showNotification(`${skill.name} unlocked!`, 'success', 4000);
        return true;
    }

    applySkillEffects() {
        const player = this.game.systems.player;
        this.resetToBaseStats();

        for (const category of Object.values(this.skills)) {
            for (const skill of Object.values(category)) {
                if (!skill.unlocked || skill.currentLevel <= 0) continue;

                for (const [effect, value] of Object.entries(skill.effects || {})) {
                    const total = value * skill.currentLevel;
                    switch (effect) {
                        case 'attack':          player.attributes.attack          += total; break;
                        case 'defense':         player.attributes.defense         += total; break;
                        case 'speed':           player.attributes.speed           += total; break;
                        case 'health':
                        case 'maxHealth':       player.attributes.maxHealth       += total; break;
                        case 'maxEnergy':       player.attributes.maxEnergy       += total; break;
                        case 'criticalChance':  player.attributes.criticalChance  += total; break;
                        case 'criticalDamage':  player.attributes.criticalDamage  += total; break;
                        default:
                            if (!this.activeBuffs[effect]) this.activeBuffs[effect] = 0;
                            this.activeBuffs[effect] += total;
                    }
                }
            }
        }

        player.updateUI();
    }

    resetToBaseStats() {
        const player = this.game.systems.player;
        const lvl = player.stats.level || 1;
        Object.assign(player.attributes, {
            attack:         10  + (lvl - 1) * 2,
            defense:         5  + (lvl - 1) * 1,
            speed:          10,
            maxHealth:     100  + (lvl - 1) * 10,
            maxEnergy:     100  + (lvl - 1) * 5,
            criticalChance: 0.05,
            criticalDamage: 1.5
        });
        this.activeBuffs = {};
    }

    // ------------------------------------------------------------------ //
    // Combat / science / crafting XP helpers
    // ------------------------------------------------------------------ //
    awardCombatExperience(amount) {
        this.addSkillExperience('combat', 'weapons_mastery',   amount);
        this.addSkillExperience('combat', 'tactical_analysis', amount * 0.5);
    }

    awardScienceExperience(amount) {
        this.addSkillExperience('science', 'energy_manipulation', amount);
        this.addSkillExperience('science', 'alien_technology',    amount * 0.3);
    }

    awardCraftingExperience(amount) {
        this.addSkillExperience('crafting', 'weapons_crafting', amount);
        this.addSkillExperience('crafting', 'armor_forging',    amount * 0.5);
    }

    // ------------------------------------------------------------------ //
    // Queries
    // ------------------------------------------------------------------ //
    getSkillLevel(category, skillId) {
        // Support single-arg form used by CraftingSystem: getSkillLevel('crafting')
        if (skillId === undefined) {
            let max = 0;
            for (const cat of Object.values(this.skills)) {
                if (cat[category]) max = Math.max(max, cat[category].currentLevel || 0);
            }
            return max;
        }
        return this.skills[category]?.[skillId]?.currentLevel || 0;
    }

    getSkillExperience(skillId) {
        for (const cat of Object.values(this.skills)) {
            if (cat[skillId]) return cat[skillId].experience || 0;
        }
        return 0;
    }

    getExperienceNeeded(skillId) {
        for (const cat of Object.values(this.skills)) {
            if (cat[skillId]) return cat[skillId].experienceToNext || 0;
        }
        return 0;
    }

    hasSkill(category, skillId, minimumLevel = 1) {
        const skill = this.skills[category]?.[skillId];
        return skill && skill.unlocked && skill.currentLevel >= minimumLevel;
    }

    getSkillBonus(effect) {
        return this.activeBuffs[effect] || 0;
    }

    // ------------------------------------------------------------------ //
    // UI
    // ------------------------------------------------------------------ //
    updateUI() {
        this.updateSkillsGrid();
        this.updateSkillPointsDisplay();
    }

    updateSkillsGrid() {
        const grid = document.getElementById('skillsGrid');
        if (!grid) return;

        const activeCategory = document.querySelector('.skill-cat-btn.active')?.dataset.category || 'combat';
        const skills = this.skills[activeCategory] || {};

        if (!this._loaded) {
            grid.innerHTML = '<div style="text-align:center;padding:2rem;color:var(--text-secondary)"><i class="fas fa-spinner fa-spin"></i><p>Loading skills from server...</p></div>';
            return;
        }

        grid.innerHTML = '';

        Object.entries(skills).forEach(([skillId, skill]) => {
            const el = document.createElement('div');
            el.className = `skill-item ${!skill.unlocked ? 'locked' : ''}`;

            const progressPercent = skill.currentLevel > 0
                ? (skill.experience / skill.experienceToNext) * 100
                : 0;

            const iconClass = this.game.systems.textureManager
                ? this.game.systems.textureManager.getIcon(skill.icon)
                : (skill.icon || 'fa-question');

            el.innerHTML = `
                <div class="skill-header">
                    <div class="skill-icon"><i class="fas ${iconClass}"></i></div>
                    <div class="skill-info">
                        <div class="skill-name">${skill.name}</div>
                        <div class="skill-level">Lv. ${skill.currentLevel}/${skill.maxLevel}</div>
                    </div>
                </div>
                <div class="skill-description">${skill.description}</div>
                ${skill.currentLevel > 0 && skill.currentLevel < skill.maxLevel ? `
                    <div class="skill-progress">
                        <div class="progress-bar">
                            <div class="progress-fill" style="width:${progressPercent}%"></div>
                        </div>
                        <span>${skill.experience}/${skill.experienceToNext} XP</span>
                    </div>
                ` : skill.currentLevel >= skill.maxLevel ? `
                    <div class="skill-max-level"><span>MAX LEVEL</span></div>
                ` : ''}
                <div class="skill-actions">
                    ${!skill.unlocked ? `
                        <button class="btn btn-warning" onclick="if(window.game&&window.game.systems)window.game.systems.skillSystem.unlockSkill('${activeCategory}','${skillId}')">
                            Unlock (2 Points)
                        </button>
                    ` : skill.currentLevel < skill.maxLevel ? `
                        <button class="btn btn-primary" onclick="if(window.game&&window.game.systems)window.game.systems.skillSystem.upgradeSkill('${activeCategory}','${skillId}')">
                            Upgrade (1 Point)
                        </button>
                    ` : `<span class="max-level">MAX LEVEL</span>`}
                </div>
                ${skill.requiredLevel && !skill.unlocked ? `
                    <div class="skill-requirement">Requires Level ${skill.requiredLevel}</div>
                ` : ''}
            `;

            grid.appendChild(el);
        });
    }

    updateSkillPointsDisplay() {
        const player = this.game.systems.player;
        document.querySelectorAll('.skill-points').forEach(el => {
            el.textContent = `Skill Points: ${player.stats.skillPoints}`;
        });
    }

    // ------------------------------------------------------------------ //
    // Save / Load
    // ------------------------------------------------------------------ //
    save() {
        return { skills: this.skills, activeBuffs: this.activeBuffs };
    }

    load(data) {
        if (data.skills) {
            for (const [category, skills] of Object.entries(data.skills)) {
                if (!this.skills[category]) this.skills[category] = {};
                for (const [skillId, skillData] of Object.entries(skills)) {
                    if (this.skills[category][skillId]) {
                        Object.assign(this.skills[category][skillId], skillData);
                    } else {
                        // Store progress even before server definitions arrive
                        this.skills[category][skillId] = { ...skillData };
                    }
                }
            }
        }
        if (data.activeBuffs) this.activeBuffs = data.activeBuffs;
        this.applySkillEffects();
    }

    reset() {
        this.activeBuffs = {};
        for (const category of Object.values(this.skills)) {
            for (const skill of Object.values(category)) {
                skill.currentLevel = 0;
                skill.experience   = 0;
            }
        }
    }

    clear() { this.reset(); }
}
