import { GameObject } from "./gameObject.js";
import { gameTime } from "./gameTime.js";
import { loadJson } from "./loadJson.js";
import { anthill } from "./anthill.js";

const RESOURCE_SIZE = 256;
const RESOURCE_RING_STEP = 300;
const RESOURCE_COLLISION_PADDING = 24;
const RESOURCE_MIN_SPAWN_DISTANCE = 300;
const RESOURCE_SPAWN_ANGLE_ATTEMPTS = 48;
const FOOD_TIMER_OFFSET_Y = 24;
const FOOD_TIMER_FONT = "bold 14px monospace";
const RESOURCE_TIME_BASELINE_SCALE = 2;
const TUTORIAL_MUSHROOM_KEY = "mushroom";
const TUTORIAL_MUSHROOM_ANGLE_DEGREES = 40;

function getResourceNow() {
    return gameTime.now() / RESOURCE_TIME_BASELINE_SCALE;
}

function clampToPositiveNumber(value, fallback = 0) {
    return Number.isFinite(value) && value > 0 ? value : fallback;
}

function formatRemainingTime(remainingMs) {
    const totalSeconds = Math.max(0, Math.ceil(remainingMs / 1000));
    const minutes = Math.floor(totalSeconds / 60);
    const seconds = totalSeconds % 60;
    return `${minutes}:${String(seconds).padStart(2, "0")}`;
}

function getPathBaseName(path) {
    if (typeof path !== "string" || path.length === 0) {
        return "";
    }

    const segments = path.split("/");
    return segments[segments.length - 1] || "";
}

function degreesToRadians(degrees) {
    return degrees * Math.PI / 180;
}

class Resource extends GameObject {
    constructor(id, resourceKey, definition, x, y, ringIndex) {
        super(id);
        this.resourceKey = resourceKey;
        this.definition = definition;
        this.ringIndex = ringIndex;
        this.type = definition.type || "normal";
        this.spawnTime = getResourceNow();
        this.timeAlive = Math.max(0, Number(definition.timeAlive) || 0);
        this.baseWeight = clampToPositiveNumber(definition.spawnRules?.baseWeight, 1);
        this.value = Math.max(0, Number(definition.spawnRules?.value) || 0);
        this.spriteBasePath = definition.sprites;
        this.carrySpritePath = `assets/resources/carry_${getPathBaseName(definition.sprites)}.png`;

        this.setSprite(`${definition.sprites}.png`);
        this.setPosition(x, y);
        this.setSize(RESOURCE_SIZE, RESOURCE_SIZE);
    }

    isPermanent() {
        return this.type === "permanent";
    }

    getRemainingLifetimeMs() {
        if (this.isPermanent() || this.timeAlive <= 0) {
            return Infinity;
        }

        return Math.max(0, this.timeAlive - (getResourceNow() - this.spawnTime));
    }

    isExpired() {
        if (this.isPermanent()) {
            return false;
        }

        return this.getRemainingLifetimeMs() <= 0;
    }

    getHarvestRadius() {
        return Math.max(16, this.width / 2 - RESOURCE_COLLISION_PADDING);
    }

    drawTimer(ctx, cam) {
        if (this.isPermanent()) {
            return;
        }

        const relativeX = Math.round((this.x + cam.x) * cam.zoom);
        const relativeY = Math.round((this.y + cam.y) * cam.zoom);
        const timerText = formatRemainingTime(this.getRemainingLifetimeMs());
        const textY = relativeY - Math.round((this.height / 2) * cam.zoom) - FOOD_TIMER_OFFSET_Y;

        ctx.save();
        ctx.font = FOOD_TIMER_FONT;
        ctx.textAlign = "center";
        ctx.textBaseline = "middle";

        const textWidth = Math.ceil(ctx.measureText(timerText).width);
        const badgeWidth = textWidth + 12;
        const badgeHeight = 18;

        ctx.fillStyle = "rgba(33, 24, 16, 0.85)";
        ctx.fillRect(relativeX - badgeWidth / 2, textY - badgeHeight / 2, badgeWidth, badgeHeight);
        ctx.strokeStyle = "#C0C0C0";
        ctx.lineWidth = 1;
        ctx.strokeRect(relativeX - badgeWidth / 2 + 0.5, textY - badgeHeight / 2 + 0.5, badgeWidth - 1, badgeHeight - 1);

        ctx.fillStyle = "#F3E8C5";
        ctx.fillText(timerText, relativeX, textY + 1);
        ctx.restore();
    }

    draw(ctx, cam) {
        super.draw(ctx, cam);
        this.drawTimer(ctx, cam);
    }
}

export class Resources {
    constructor() {
        this.resourceDefinitions = {};
        this.resourceTypes = [];
        this.resourceArray = [];
        this.nextResourceId = 1;
        this.isLoaded = false;

        loadJson("data/resources.json")
            .then(data => {
                this.resourceDefinitions = data || {};
                this.resourceTypes = Object.entries(this.resourceDefinitions).map(([key, definition]) => ({
                    key,
                    definition,
                    lastSpawnTime: null
                }));

                this.initializeResources();
                this.isLoaded = true;
            })
            .catch(() => {
                this.resourceDefinitions = {};
                this.resourceTypes = [];
                this.isLoaded = true;
            });
    }

    initializeResources() {
        for (const resourceType of this.resourceTypes) {
            if (resourceType.definition.type === "permanent") {
                this.spawnPermanentResources(resourceType);
                continue;
            }

            resourceType.lastSpawnTime = getResourceNow();
        }
    }

    update() {
        if (!this.isLoaded) {
            return;
        }

        this.removeExpiredResources();

        for (const resourceType of this.resourceTypes) {
            if (resourceType.definition.type === "permanent") {
                continue;
            }

            const respawnEvery = Math.max(0, Number(resourceType.definition.respawnEvery) || 0);
            if (respawnEvery <= 0) {
                continue;
            }

            if (this.hasResourceRespawnElapsed(resourceType, respawnEvery)) {
                this.spawnNearestResource(resourceType);
                resourceType.lastSpawnTime = getResourceNow();
            }
        }
    }

    hasResourceRespawnElapsed(resourceType, durationMs) {
        if (resourceType.lastSpawnTime === null) {
            return true;
        }

        return getResourceNow() - resourceType.lastSpawnTime >= durationMs;
    }

    draw(ctx, cam) {
        for (const resource of this.resourceArray) {
            resource.draw(ctx, cam);
        }
    }

    removeExpiredResources() {
        for (let index = this.resourceArray.length - 1; index >= 0; index--) {
            if (this.resourceArray[index].isExpired()) {
                this.resourceArray.splice(index, 1);
            }
        }
    }

    spawnPermanentResources(resourceType) {
        const max = Math.max(0, Number(resourceType.definition.max) || 0);
        while (this.getResourcesByKey(resourceType.key).length < max) {
            this.spawnNearestResource(resourceType);
        }
    }

    getResourcesByKey(resourceKey) {
        return this.resourceArray.filter(resource => resource.resourceKey === resourceKey);
    }

    getNextRingIndex(resourceKey) {
        const usedRingIndexes = new Set(this.getResourcesByKey(resourceKey).map(resource => resource.ringIndex));
        let nextRingIndex = 0;

        while (usedRingIndexes.has(nextRingIndex)) {
            nextRingIndex += 1;
        }

        return nextRingIndex;
    }

    isTutorialMushroom(resourceType, ringIndex) {
        return resourceType.key === TUTORIAL_MUSHROOM_KEY && ringIndex === 0;
    }

    isFarEnoughFromOtherResources(x, y) {
        const minimumDistanceSquared = RESOURCE_MIN_SPAWN_DISTANCE * RESOURCE_MIN_SPAWN_DISTANCE;

        for (const resource of this.resourceArray) {
            const dx = resource.x - x;
            const dy = resource.y - y;
            if (dx * dx + dy * dy < minimumDistanceSquared) {
                return false;
            }
        }

        return true;
    }

    getSpawnPositionAtAngle(distance, angleRadians) {
        return {
            x: anthill.x + Math.cos(angleRadians) * distance,
            y: anthill.y + Math.sin(angleRadians) * distance
        };
    }

    findSpawnPosition(resourceType, ringIndex) {
        const minDistanceFromColony = Math.max(0, Number(resourceType.definition.spawnRules?.minDistanceFromColony) || 0);
        const distance = minDistanceFromColony + ringIndex * RESOURCE_RING_STEP;

        if (this.isTutorialMushroom(resourceType, ringIndex)) {
            const tutorialPosition = this.getSpawnPositionAtAngle(distance, degreesToRadians(TUTORIAL_MUSHROOM_ANGLE_DEGREES));
            if (this.isFarEnoughFromOtherResources(tutorialPosition.x, tutorialPosition.y)) {
                return tutorialPosition;
            }
        }

        const startAngle = Math.random() * Math.PI * 2;
        for (let index = 0; index < RESOURCE_SPAWN_ANGLE_ATTEMPTS; index++) {
            const angle = startAngle + (index / RESOURCE_SPAWN_ANGLE_ATTEMPTS) * Math.PI * 2;
            const position = this.getSpawnPositionAtAngle(distance, angle);
            if (this.isFarEnoughFromOtherResources(position.x, position.y)) {
                return position;
            }
        }

        return null;
    }

    spawnNearestResource(resourceType) {
        const ringIndex = this.getNextRingIndex(resourceType.key);
        const position = this.findSpawnPosition(resourceType, ringIndex);
        if (!position) {
            return false;
        }

        const resourceId = `resource_${this.nextResourceId++}`;

        this.resourceArray.push(new Resource(resourceId, resourceType.key, resourceType.definition, position.x, position.y, ringIndex));
        return true;
    }

    getNearestHarvestableResource(ant) {
        let closestResource = null;
        let closestDistanceSquared = Infinity;

        for (const resource of this.resourceArray) {
            const dx = resource.x - ant.x;
            const dy = resource.y - ant.y;
            const distanceSquared = dx * dx + dy * dy;
            const harvestRadius = resource.getHarvestRadius() + Math.max(6, ant.width / 2);

            if (distanceSquared > harvestRadius * harvestRadius) {
                continue;
            }

            if (distanceSquared < closestDistanceSquared) {
                closestDistanceSquared = distanceSquared;
                closestResource = resource;
            }
        }

        return closestResource;
    }

    tryHarvestWithAnt(ant) {
        if (!this.isLoaded || ant.state !== "outbound" || ant.isCarryingResource()) {
            return false;
        }

        const carryingCapacity = Number(ant.stats?.harvestValue) || 0;
        if (carryingCapacity <= 0) {
            return false;
        }

        const resource = this.getNearestHarvestableResource(ant);
        if (!resource) {
            return false;
        }

        const harvestAmount = Math.floor(carryingCapacity / resource.baseWeight);
        if (harvestAmount <= 0) {
            return false;
        }

        ant.collectResource(resource, harvestAmount);
        return true;
    }
}

export const resources = new Resources();