'use server';

import Binned, { Binned as BinItem, PopulatedBinned } from '../../../models/binned';
import { Bin as BinEnum } from "../bin.enum";

import db from '../db/connection';

export type BinWeights = { [key in BinEnum]: number };
export type CategorizedLoss = { [key: string]: number };
export type LossByDefect = { _id: number, weight: number }[];
export type CategorizedOperatorLoss = { [key: string]: BinWeights };

const binWeightTemplate = { [BinEnum.LOSS]: 0, [BinEnum.PROCESS]: 0, [BinEnum.DONATE]: 0, [BinEnum.SHOULDER_TAP]: 0 };

export async function getAggregateBinWeights(): Promise<BinWeights> {
    await db();
    const binned = await Binned.find();
    return binned.reduce((acc: BinWeights, cur: BinItem) => ({
        ...acc,
        [cur.bin]: acc[cur.bin] + cur.weight,
    }), binWeightTemplate);
}

export async function getLossByDefect(): Promise<LossByDefect> {
    await db();
    return Binned.aggregate([
        { $group: { _id: "$reason", weight: { $sum: "$weight" } } },
    ]);
};

export async function getLossByFarm(): Promise<CategorizedLoss> {
    await db();
    const binned = await Binned.find().populate<PopulatedBinned>('farm');
    return binned.reduce((acc: { [key: string]: number }, cur) => {
        return cur.farm ? {
            ...acc,
            [cur.farm.name]: (acc[cur.farm.name] ?? 0) + cur.weight,
        } : acc;
    }, {});
};

export async function getLossCategoriesByOperator(): Promise<CategorizedOperatorLoss> {
    await db();
    const binned = await Binned.find();
    return binned.reduce((acc: { [key: string]: BinWeights }, cur: BinItem) => {
        const existingBinWeight = (acc[cur.operator] || binWeightTemplate)[cur.bin];
        return {
            ...acc,
            [cur.operator]: { ...acc[cur.operator] || binWeightTemplate, [cur.bin]: existingBinWeight + cur.weight }
        }
    }, {});
};

export async function getLossByProcessor(): Promise<CategorizedLoss> {
    await db();
    const binned = await Binned.find().populate<PopulatedBinned>('processor');
    return binned.reduce((acc: { [key: string]: number }, cur) => {
        return cur.processor ? {
            ...acc,
            [cur.processor?.name]: (acc[cur.processor.name] || 0) + cur.weight,
        } : acc;
    }, {});
};
