
import { gameTime } from "./gameTime.js";
import { sharedMessage } from "./message.js";

const FOOD_RATE_WINDOW_MS = 60000;

const COLOR_DEFINITIONS = [
    { key: "red", label: "RED", fill: "#7F2727" },
    { key: "green", label: "GREEN", fill: "#627F27" },
    { key: "purple", label: "PURPLE", fill: "#7F276E" },
    { key: "blue", label: "BLUE", fill: "#27427F" },
    { key: "yellow", label: "YELLOW", fill: "#A08D06" }
];

function createColorStats() {
    return {
        spawned: 0,
        returned: 0,
        emptyReturns: 0,
        foodLooted: 0,
        producedThisMinute: 0,
        maxDistance: 0,
        longestTrip: 0,
        bestHaul: 0,
        recentFoodEvents: []
    };
}

function refreshFoodRate(colorStats, now = gameTime.now()) {
    colorStats.recentFoodEvents = colorStats.recentFoodEvents.filter(event => now - event.time <= FOOD_RATE_WINDOW_MS);
    colorStats.producedThisMinute = colorStats.recentFoodEvents.reduce((sum, event) => sum + event.amount, 0);
}

function formatInteger(value, minimumDigits = 0) {
    return String(Math.max(0, Math.round(value))).padStart(minimumDigits, "0");
}

function formatFixed(value, fractionDigits = 1) {
    return Number.isFinite(value) ? value.toFixed(fractionDigits) : (0).toFixed(fractionDigits);
}

function formatMillimeters(value, minimumDigits = 0) {
    return `${formatInteger(value, minimumDigits)} millimeter`;
}

function formatColorName(color) {
    if (typeof color !== "string" || color.length === 0) {
        return "Unknown";
    }

    return color.charAt(0).toUpperCase() + color.slice(1).toLowerCase();
}

export class TotalStats {
    constructor() {
        this.colorDefinitions = COLOR_DEFINITIONS;
        this.reset();
    }

    reset() {
        this.colors = {};
        for (const colorDefinition of this.colorDefinitions) {
            this.colors[colorDefinition.key] = createColorStats();
        }
    }

    normalizeColor(color) {
        return typeof color === "string" ? color.toLowerCase() : "";
    }

    getColorDefinition(color) {
        const normalizedColor = this.normalizeColor(color);
        return this.colorDefinitions.find(colorDefinition => colorDefinition.key === normalizedColor) || null;
    }

    getColorStats(color) {
        const normalizedColor = this.normalizeColor(color);
        return this.colors[normalizedColor] || null;
    }

    recordSpawn(color) {
        const colorStats = this.getColorStats(color);
        if (!colorStats) {
            return;
        }

        colorStats.spawned += 1;
    }

    recordReturn(color) {
        const colorStats = this.getColorStats(color);
        if (!colorStats) {
            return;
        }

        colorStats.returned += 1;
    }

    recordEmptyReturn(color) {
        const colorStats = this.getColorStats(color);
        if (!colorStats) {
            return;
        }

        colorStats.emptyReturns += 1;
    }

    recordFoodLooted(color, foodAmount, haulAmount = foodAmount) {
        const colorStats = this.getColorStats(color);
        if (!colorStats || !Number.isFinite(foodAmount) || foodAmount <= 0) {
            return;
        }

        const normalizedFoodAmount = Math.max(0, foodAmount);
        const normalizedHaulAmount = Number.isFinite(haulAmount) ? Math.max(0, haulAmount) : normalizedFoodAmount;

        colorStats.foodLooted += normalizedFoodAmount;
        colorStats.bestHaul = Math.max(colorStats.bestHaul, normalizedHaulAmount);
        colorStats.recentFoodEvents.push({ amount: normalizedFoodAmount, time: gameTime.now() });
        refreshFoodRate(colorStats);
    }

    refreshProducedThisMinute() {
        const now = gameTime.now();
        for (const colorDefinition of this.colorDefinitions) {
            refreshFoodRate(this.colors[colorDefinition.key], now);
        }
    }

    recordMaxDistance(color, distance) {
        const colorStats = this.getColorStats(color);
        if (!colorStats || !Number.isFinite(distance)) {
            return;
        }

        if (distance <= colorStats.maxDistance) {
            return;
        }

        colorStats.maxDistance = distance;
        this.notifyNewRecord(color, `A ${formatColorName(color)} ant just set a new farthest-from-hill record: ${formatMillimeters(distance, 3)}.`);
    }

    recordLongestTrip(color, distance) {
        const colorStats = this.getColorStats(color);
        if (!colorStats || !Number.isFinite(distance)) {
            return;
        }

        if (distance <= colorStats.longestTrip) {
            return;
        }

        colorStats.longestTrip = distance;
        this.notifyNewRecord(color, `A ${formatColorName(color)} ant just finished the longest trip so far: ${formatMillimeters(distance, 3)}.`);
    }

    notifyNewRecord(color, text) {
        if (!this.getColorStats(color) || !text) {
            return;
        }

        sharedMessage.setNextMessage(text);
    }

    getTotalForKey(key) {
        return this.colorDefinitions.reduce((sum, colorDefinition) => {
            return sum + this.colors[colorDefinition.key][key];
        }, 0);
    }

    getAntsOutsideNow(color) {
        const colorStats = this.getColorStats(color);
        if (colorStats) {
            return Math.max(0, colorStats.spawned - colorStats.returned);
        }

        return this.colorDefinitions.reduce((sum, colorDefinition) => {
            return sum + this.getAntsOutsideNow(colorDefinition.key);
        }, 0);
    }

    getEmptyPercent(color) {
        const colorStats = this.getColorStats(color);
        if (!colorStats) {
            return 100;
        }

        return colorStats.returned > 0
            ? (colorStats.emptyReturns / colorStats.returned) * 100
            : 100;
    }

    getGeneralStats() {
        this.refreshProducedThisMinute();

        return {
            totalFoodLooted: this.getTotalForKey("foodLooted"),
            totalAntsEverSpawned: this.getTotalForKey("spawned"),
            totalReturnTrips: this.getTotalForKey("returned"),
            totalEmptyReturns: this.getTotalForKey("emptyReturns"),
            totalAntsOutsideNow: this.getAntsOutsideNow(),
            totalProducedThisMinute: this.getTotalForKey("producedThisMinute")
        };
    }

    getColorDisplayStats(color) {
        this.refreshProducedThisMinute();

        const colorDefinition = this.getColorDefinition(color);
        const colorStats = this.getColorStats(color);

        if (!colorDefinition || !colorStats) {
            return null;
        }

        return {
            color: colorDefinition.key,
            label: colorDefinition.label,
            fill: colorDefinition.fill,
            out: this.getAntsOutsideNow(colorDefinition.key),
            emptyPercent: this.getEmptyPercent(colorDefinition.key),
            foodPerMinute: colorStats.producedThisMinute,
            maxDistance: colorStats.maxDistance,
            longestTrip: colorStats.longestTrip,
            bestHaul: colorStats.bestHaul
        };
    }

    getColorRows() {
        return this.colorDefinitions.map(colorDefinition => {
            const colorStats = this.getColorDisplayStats(colorDefinition.key);
            return {
                label: colorDefinition.label,
                fill: colorDefinition.fill,
                values: [
                    formatInteger(colorStats.out, 2),
                    `${formatInteger(colorStats.emptyPercent, 2)}%`,
                    formatFixed(colorStats.foodPerMinute, 1),
                    formatInteger(colorStats.maxDistance, 3),
                    formatInteger(colorStats.longestTrip, 3),
                    formatInteger(colorStats.bestHaul, 2)
                ]
            };
        });
    }

    getBestByKey(key) {
        let bestDefinition = this.colorDefinitions[0];
        let bestValue = this.colors[bestDefinition.key][key];

        for (let index = 1; index < this.colorDefinitions.length; index++) {
            const colorDefinition = this.colorDefinitions[index];
            const candidateValue = this.colors[colorDefinition.key][key];
            if (candidateValue > bestValue) {
                bestDefinition = colorDefinition;
                bestValue = candidateValue;
            }
        }

        return {
            key: bestDefinition.key,
            color: bestDefinition.label,
            fill: bestDefinition.fill,
            value: bestValue
        };
    }

    getBestStats() {
        this.refreshProducedThisMinute();

        const bestFoodPerMinute = this.getBestByKey("producedThisMinute");
        const farthestFromHill = this.getBestByKey("maxDistance");
        const longestTrip = this.getBestByKey("longestTrip");
        const biggestHaul = this.getBestByKey("bestHaul");

        return {
            bestFoodPerMinute: {
                fill: bestFoodPerMinute.fill,
                value: formatFixed(bestFoodPerMinute.value, 1)
            },
            farthestFromHill: {
                fill: farthestFromHill.fill,
                value: formatInteger(farthestFromHill.value, 3)
            },
            longestTrip: {
                fill: longestTrip.fill,
                value: formatInteger(longestTrip.value, 3)
            },
            biggestHaul: {
                fill: biggestHaul.fill,
                value: formatInteger(biggestHaul.value, 2)
            }
        };
    }

    getFormattedGeneralStats() {
        const generalStats = this.getGeneralStats();
        return {
            totalFoodLooted: formatInteger(generalStats.totalFoodLooted, 4),
            totalAntsEverSpawned: formatInteger(generalStats.totalAntsEverSpawned, 4),
            totalReturnTrips: formatInteger(generalStats.totalReturnTrips, 4),
            totalEmptyReturns: formatInteger(generalStats.totalEmptyReturns, 4),
            totalAntsOutsideNow: formatInteger(generalStats.totalAntsOutsideNow, 4),
            totalProducedThisMinute: formatFixed(generalStats.totalProducedThisMinute, 1)
        };
    }
}

export const totalStats = new TotalStats();