import { GameObject } from "./gameObject.js";
import { Ant } from './ant.js';
import { Queen } from './queen.js';
import { loadAntData } from "./loadJson.js";
import { Timer } from "./gameTime.js";
import { totalStats } from "./totalStats.js";
import { currency } from "./currency.js";
import { tutorial } from "./tutorial.js";

let nextAntId = 1;
const UPGRADEABLE_STAT_KEYS = ["speed", "maxTravelDistance", "harvestValue", "respawnTime"];
const INITIAL_PURCHASE_COST = 100;
const SIGNALANT_UNLOCKED_ANT_COUNT = 2;
const UPGRADE_MULTIPLIER = 1.1;
const RESPAWN_TIME_UPGRADE_MULTIPLIER = 0.9;
const NEXT_PURCHASE_COST_MULTIPLIER = 1.25;
const SIGNALANT_PURCHASE_KEY = "signalant";

export class Anthill extends GameObject {
    constructor(options = {}) {
        super("anthill", options);
        this.queen = new Queen();
        this.setSprite(options.sprite || "assets/anthill.png");
        this.setPosition(options.x || 0, options.y || 0);
        this.setSize(options.width || 256, options.height || 256);
        this.antSpawnDirection = options.antSpawnDirection || this.queen.direction || 0;

        this.unlockedAntsData = [];
        this.antDefinitions = {};
        this.upgradeLevels = {};
        this.totalPurchaseCount = 0;
        this.purchaseStates = {};
        this.unlockedAnts = ["red_forager"];
        this.loadUnlockedAntsData();

    }

    update(options = {}) {
        this.queen.update(options);
        this.antSpawnDirection = this.queen.direction;
    }

    draw(ctx, cam) {
        super.draw(ctx, cam);
        if (this.queen.active) {
            this.queen.draw(ctx, cam);
        }
    }

    changeSpawnDirection(newDirection) {
        this.antSpawnDirection = newDirection;
    }

    loadUnlockedAntsData() {
        loadAntData()
            .then(data => {
                this.antDefinitions = data || {};
                this.refreshUnlockedAnts();
                this.syncUnlockedAntsData();
            });
    }

    getUnlockAfterBuys(antDefinition = {}) {
        return Number.isFinite(antDefinition.unlockAfterBuys) ? antDefinition.unlockAfterBuys : 0;
    }

    refreshUnlockedAnts() {
        const unlockedAnts = new Set(this.unlockedAnts);

        for (const [antId, antDefinition] of Object.entries(this.antDefinitions)) {
            if (this.getUnlockAfterBuys(antDefinition) <= this.totalPurchaseCount) {
                unlockedAnts.add(antId);
            }
        }

        this.unlockedAnts = Object.keys(this.antDefinitions)
            .filter(antId => unlockedAnts.has(antId));
    }

    syncUnlockedAntsData() {
        const existingAntDataById = new Map(this.unlockedAntsData.map(antData => [antData.antId, antData]));

        this.unlockedAntsData = this.unlockedAnts
            .map(antId => {
                const antDefinition = this.antDefinitions[antId];
                if (!antDefinition) {
                    return null;
                }

                const resolvedAntId = `${antDefinition.color}_${antDefinition.type}`;
                const existingAntData = existingAntDataById.get(resolvedAntId);
                this.ensureUpgradeState(resolvedAntId, antDefinition.stats);
                this.ensurePurchaseState(resolvedAntId);

                return {
                    antId: resolvedAntId,
                    antDefinition,
                    spawnTimer: existingAntData?.spawnTimer || new Timer()
                };
            })
            .filter(antData => antData);
    }

    updateUnlockedAnts() {
        if (!Object.keys(this.antDefinitions).length) {
            return;
        }

        this.refreshUnlockedAnts();
        this.syncUnlockedAntsData();
    }

    ensureUpgradeState(antId, baseStats = {}) {
        if (this.upgradeLevels[antId]) {
            return this.upgradeLevels[antId];
        }

        const levels = {};
        for (const statKey of UPGRADEABLE_STAT_KEYS) {
            if (typeof baseStats[statKey] === "number") {
                levels[statKey] = 0;
            }
        }

        this.upgradeLevels[antId] = levels;
        return levels;
    }

    ensurePurchaseState(purchaseKey) {
        if (this.purchaseStates[purchaseKey]) {
            return this.purchaseStates[purchaseKey];
        }

        this.purchaseStates[purchaseKey] = {
            purchaseCount: 0,
            nextCost: INITIAL_PURCHASE_COST
        };

        return this.purchaseStates[purchaseKey];
    }

    getSignalantPurchaseKey() {
        return SIGNALANT_PURCHASE_KEY;
    }

    getPurchaseState(purchaseKey) {
        return this.ensurePurchaseState(purchaseKey);
    }

    getAntIdByColor(color) {
        const unlockedAntId = this.unlockedAnts.find(antId => antId.startsWith(`${color}_`));
        if (unlockedAntId) {
            return unlockedAntId;
        }

        const definedAntId = Object.keys(this.antDefinitions).find(antId => antId.startsWith(`${color}_`));
        return definedAntId || `${color}_forager`;
    }

    isAntUnlocked(antId) {
        return this.unlockedAnts.includes(antId);
    }

    isColorUnlocked(color) {
        return this.isAntUnlocked(this.getAntIdByColor(color));
    }

    getUnlockedAntCount() {
        return this.unlockedAnts.length;
    }

    canAccessSignalants() {
        return this.totalPurchaseCount >= 1 || this.getUnlockedAntCount() >= SIGNALANT_UNLOCKED_ANT_COUNT;
    }

    getNextUnlock() {
        const lockedAnts = Object.entries(this.antDefinitions)
            .filter(([antId]) => !this.isAntUnlocked(antId))
            .sort(([, leftDefinition], [, rightDefinition]) => this.getUnlockAfterBuys(leftDefinition) - this.getUnlockAfterBuys(rightDefinition));

        const [antId, antDefinition] = lockedAnts[0] || [];
        if (!antId || !antDefinition) {
            return null;
        }

        const unlockAfterBuys = this.getUnlockAfterBuys(antDefinition);
        return {
            antId,
            antDefinition,
            unlockAfterBuys,
            remainingBuys: Math.max(0, unlockAfterBuys - this.totalPurchaseCount)
        };
    }

    getAntDefinition(antId) {
        return this.antDefinitions[antId] || null;
    }

    getBaseStats(antId) {
        const antDefinition = this.getAntDefinition(antId);
        return antDefinition?.stats ? { ...antDefinition.stats } : null;
    }

    getUpgradeLevels(antId) {
        const baseStats = this.getBaseStats(antId);
        return this.ensureUpgradeState(antId, baseStats || {});
    }

    getUpgradedValue(baseValue, level) {
        if (!Number.isFinite(baseValue)) {
            return 0;
        }

        if (!Number.isFinite(level) || level <= 0) {
            return baseValue;
        }

        return baseValue * (UPGRADE_MULTIPLIER ** level);
    }

    getStatValueForLevel(statKey, baseValue, level) {
        if (statKey === "respawnTime") {
            if (!Number.isFinite(baseValue)) {
                return 0;
            }

            if (!Number.isFinite(level) || level <= 0) {
                return baseValue;
            }

            return baseValue * (RESPAWN_TIME_UPGRADE_MULTIPLIER ** level);
        }

        return this.getUpgradedValue(baseValue, level);
    }

    getEffectiveStats(antId) {
        const baseStats = this.getBaseStats(antId);
        if (!baseStats) {
            return null;
        }

        const levels = this.getUpgradeLevels(antId);
        const effectiveStats = {};

        for (const statKey of Object.keys(baseStats)) {
            const level = levels[statKey] || 0;
            effectiveStats[statKey] = this.getStatValueForLevel(statKey, baseStats[statKey], level);
        }

        return effectiveStats;
    }

    getUpgradePreview(antId, statKey) {
        const baseStats = this.getBaseStats(antId);
        if (!baseStats || typeof baseStats[statKey] !== "number") {
            return null;
        }

        const levels = this.getUpgradeLevels(antId);
        const currentLevel = levels[statKey] || 0;
        const currentValue = this.getStatValueForLevel(statKey, baseStats[statKey], currentLevel);
        const nextValue = this.getStatValueForLevel(statKey, baseStats[statKey], currentLevel + 1);

        return {
            statKey,
            level: currentLevel,
            currentValue,
            nextValue,
            nextCost: this.getNextUpgradeCost(antId)
        };
    }

    getNextPurchaseCost(purchaseKey) {
        const purchaseState = this.getPurchaseState(purchaseKey);
        return Math.max(INITIAL_PURCHASE_COST, Math.floor(purchaseState.nextCost));
    }

    getNextUpgradeCost(antId) {
        return this.getNextPurchaseCost(antId);
    }

    getTotalPurchaseCount() {
        return this.totalPurchaseCount;
    }

    canAffordPurchase(purchaseKey) {
        return currency.amount >= this.getNextPurchaseCost(purchaseKey);
    }

    canAffordUpgrade(antId) {
        return this.canAffordPurchase(antId);
    }

    spendPurchaseCost(purchaseKey) {
        const purchaseState = this.getPurchaseState(purchaseKey);
        const cost = this.getNextPurchaseCost(purchaseKey);
        if (currency.amount < cost) {
            return false;
        }

        currency.subtract(cost);
        purchaseState.purchaseCount += 1;
        purchaseState.nextCost = Math.floor(cost * NEXT_PURCHASE_COST_MULTIPLIER);
        this.totalPurchaseCount += 1;
        this.updateUnlockedAnts();
        tutorial.trigger("purchase.any");
        return true;
    }

    upgradeStat(antId, statKey) {
        const baseStats = this.getBaseStats(antId);
        if (!baseStats || typeof baseStats[statKey] !== "number") {
            return false;
        }

        if (!this.spendPurchaseCost(antId)) {
            return false;
        }

        const levels = this.getUpgradeLevels(antId);
        levels[statKey] = (levels[statKey] || 0) + 1;
        return true;
    }

    spawnAnt(color, type) {
        const antId = `ant_${nextAntId++}`;
        const definitionId = `${color}_${type}`;
        const effectiveStats = this.getEffectiveStats(definitionId);
        // adjust spawn position based on number of unlocked ants to prevent overcrowding at the anthill
        let antX, antY;
        if ( this.queen.active ) {
            antX = this.x + (Math.random() * 2 - 1) * (this.width / Math.max(1, 21 - this.unlockedAnts.length * 3));
            antY = this.y + (Math.random() * 2 - 1) * (this.height / Math.max(1, 21 - this.unlockedAnts.length * 3));
        } else {
            antX = this.x + (Math.random() * 2 - 1) * (this.width / 10);
            antY = this.y + (Math.random() * 2 - 1) * (this.height / 10);
        }
        const ant = new Ant(antId, color, type, antX, antY, this.antSpawnDirection, {
            homeX: this.x,
            homeY: this.y,
            stats: effectiveStats ? { ...effectiveStats } : null
        });
        totalStats.recordSpawn(color);
        return ant;
    }

    spawn() {
        for (let antData of this.unlockedAntsData) {
            const effectiveStats = this.getEffectiveStats(antData.antId);
            const respawnTime = effectiveStats?.respawnTime ?? antData.antDefinition.stats.respawnTime;

            if (antData.spawnTimer.hasElapsed(respawnTime)) {
                // change between random direction and fixed spawn direction
                if( !this.queen.active ) this.antSpawnDirection = Math.random() * 360;
                this.spawnAnt(antData.antDefinition.color, antData.antDefinition.type);
                antData.spawnTimer.reset();
                //console.log(`Spawned ${antData.antDefinition.color} ${antData.antDefinition.type} ant.`);
            }
            //console.log(`Time until next spawn for ${antData.antDefinition.color} ${antData.antDefinition.type}: ${antData.spawnTimer.remainingMs(antData.antDefinition.stats.respawnTime)} ms`);
        }
    }
}

export const anthill = new Anthill();