import { type APIRoute } from "astro";
import validateAccessToken from "../../../lib/utils/validateAccessToken";
import { getUserByAccessToken } from "../../../lib/db/users";
import { ObjectId } from "mongodb";
import locationManager from "../../../lib/classes/managers/LocationManager";
import { getAllResources } from "../../../lib/db/resources";
import Defense from "../../../lib/classes/Defense";
import { Planet } from "../../../lib/classes/managers/PlanetManager";
import SystemManager from "../../../lib/classes/managers/SystemManager";

export const POST: APIRoute = async({ request }) => {
    const response = await validateAccessToken(request);
    if(response instanceof Response) return response;

    const user = await getUserByAccessToken(response);
    if(user === null) {
        return new Response(
            JSON.stringify({
                code: 401,
                message: "Unauthorized"
            }), { status: 401 }
        )
    }

    let body;
    try {
        body = await request.json()
    } catch(e) {
        return new Response(
            JSON.stringify({
                code: 400,
                message: "Bad Request",
                error: "Invalid JSON body"
            }), { status: 400 }
        )
    }

    if(!body.id || !body.defense || !body.amount) {
        return new Response(
            JSON.stringify({
                code: 400,
                message: "Bad Request",
                error: "Missing required fields: id, defense, amount"
            }), { status: 400 }
        )
    }

    const amount = parseInt(body.amount);
    if(isNaN(amount) || amount <= 0) {
        return new Response(
            JSON.stringify({
                code: 400,
                message: "Bad Request",
                error: "Invalid amount"
            }), { status: 400 }
        )
    }
    
    let id: ObjectId;

    try {
        id = new ObjectId(body.id);
    } catch(e) {
        return new Response(
            JSON.stringify({
                code: 400,
                message: "Bad Request",
                error: "Invalid ID"
            }), { status: 400 }
        )
    }

    let planetOrSystem: Planet | SystemManager | undefined = locationManager.getPlanet(id);
    if(!planetOrSystem) planetOrSystem = locationManager.getSystem(id);
    if(!planetOrSystem) {
        return new Response(
            JSON.stringify({
                code: 400,
                message: "Bad Request",
                error: "Invalid ID"
            }), { status: 400 }
        )
    }

    const defenseDB = planetOrSystem.defenses.defensesDB.find(d => d.id === body.defense);

    if(!defenseDB) {
        return new Response(
            JSON.stringify({
                code: 400,
                message: "Bad Request",
                error: "Invalid defense ID"
            }), { status: 400 }
        )
    }
    
    const defense = new Defense(planetOrSystem.defenses, defenseDB, amount);

    const requirements = await defense.checkRequirements();
    const resources = await defense.checkRequiredResources();

    if(!requirements.canBuild || !resources) {
        return new Response(
            JSON.stringify({
                code: 400,
                message: "Bad Request",
                error: `${requirements.error} | ${resources ? "" : "Not enough resources"}`
            }), { status: 400 }
        )
    }

    const resourcesDiff = await planetOrSystem.resources.getDifference(defense.data.requirements.resources.map(res => {
        return {
            id: res.id,
            amount: amount * res.amount
        }
    }));

    const resourceDB = await getAllResources();
    const resourcesAfter = resourcesDiff.map(res => {
        const data = resourceDB.find(r => r.id === res.id);

        if(!data) throw new Error("Resource not found");

        return {
            id: res.id,
            amount: res.amount,
            lastUpdated: res.lastUpdated,
            perHourMiningRate: res.perHourMiningRate,
            data
        }
    });

    planetOrSystem.resources.updateAmount(resourcesAfter.map(res => { return { id: res.id, amount: res.amount } }));
    planetOrSystem.defenses.addDefenses(defense.data.id, amount);

    await planetOrSystem.defenses.sync();
    await planetOrSystem.resources.sync();

    return new Response(
        JSON.stringify({
            code: 200,
            message: "OK"
        }), { status: 200 }
    );
}