import { produce } from 'immer';

import { CULTURE_TO_WIN } from './constants';
import { createQuest } from './create';
import { resolveMission } from './missions';
import type { ProductionType } from './types';
import { getProduction, getRegionsWithMissions, getStorage, shuffle } from './utils';
import village, { type VillageState } from "./village";


let lastFrame: number;


export default function update(timestamp: number) {
    if (!lastFrame) {
        lastFrame = timestamp;
        return;
    }

    const delta = timestamp - lastFrame;

    village.update(state => {
        if (state.victory) {
            return state;
        }

        return produce(state, (V: VillageState) => {
            // Advance building construction.
            V.buildings.filter(b => b.state.upgrade.isUpgrading).forEach(b => {
                b.state.upgrade.remainingTime -= delta;
                if (b.state.upgrade.remainingTime <= 0) {
                    b.level++;
                    b.state.upgrade.isUpgrading = false;
                    b.behavior.triggers?.onLevelUp?.(V, b);
                }
            });

            // Advance missions.
            getRegionsWithMissions(V).forEach(region => {
                if (!region.state.mission) {
                    return;
                }

                const mission = region.state.mission;
                mission.remainingTime -= delta;
                if (mission.remainingTime <= 0) {
                    resolveMission(V, region);
                }
            });

            // Advance quests.
            V.quests.filter(q => q.started).forEach(quest => {
                if (!quest.remainingTime) {
                    return;
                }

                quest.remainingTime -= delta;
                if (quest.remainingTime <= 0) {
                    V.resources.wood += quest.reward.wood;
                    V.resources.stone += quest.reward.stone;
                    V.resources.iron += quest.reward.iron;
                    V.resources.food += quest.reward.food;
                    V.resources.culture += quest.reward.culture;

                    // Replace the finished quest with a new one.
                    V.quests = V.quests.filter(q => q.id !== quest.id);
                    V.quests.push(createQuest(quest.level + 1));
                }
            });

            // Make all buildings and units produce and consume.
            const productionPerMinute = getProduction(V);
            const storage = getStorage(V);

            Object.keys(productionPerMinute).forEach((key) => {
                const resource = key as keyof ProductionType;
                const outputPerMinute = productionPerMinute[resource];
                const outputPerMilisecond = outputPerMinute /  60.0 / 1000.0;
                V.resources[resource] += outputPerMilisecond * delta;
            });

            // Kill units if food is negative.
            if (V.resources.food < 0) {
                // Choose a unit type at random.
                const type = shuffle(Object.keys(V.units).filter(t => V.units[t] > 0))[0];
                // Kill one unit of that type.
                V.units[type]--;
            }

            // Make sure resources do not overflow.
            Object.keys(productionPerMinute).forEach((key) => {
                const resource = key as keyof ProductionType;

                if (V.resources[resource] > storage[resource]) {
                    V.resources[resource] = storage[resource];
                }
                else if (V.resources[resource] < 0) {
                    V.resources[resource] = 0;
                }
            });

            // Recruit units.
            V.buildings.forEach(b => {
                if (!b.state.recruitment || !b.state.recruitment.count) {
                    return;
                }

                const recruitment = b.state.recruitment;
                recruitment.elapsedTime += delta;

                const timeToRecruit = b.behavior.units?.recruitmentTime(V, b) * 1000;
                if (recruitment.elapsedTime >= timeToRecruit) {
                    const unitType = b.behavior.units?.type || '';
                    if (!V.units.hasOwnProperty(unitType)) {
                        V.units[unitType] = 0;
                    }
                    V.units[unitType]++;
                    recruitment.count--;
                    recruitment.elapsedTime = (recruitment.count === 0) ? 0 : timeToRecruit - recruitment.elapsedTime;
                }
            });

            // Check if the game is won.
            if (V.resources.culture >= CULTURE_TO_WIN) {
                V.victory = true;
            }

            return V;
        });
    });

    lastFrame = timestamp;
}
