import DBResource from "../../../../types/db/DBResource";

export type Resource = {
    id: string,
    amount: number,
    lastUpdated: Date | null,
    perHourMiningRate: number,
    data: DBResource
}

export default abstract class ResourceManager {
    resources: Resource[] = [];
    resourcesDB: DBResource[] = [];

    abstract sync(): Promise<void>;

    abstract getStorageCapacities(): { id: string, capacity: number }[];

    getResourceById(resId: string) {
        return this.resources.find(res => res.id === resId);
    }

    async calculateCurrentAvailableResources() {
        const storage = this.getStorageCapacities();
        for(const res of this.resources) {
            if(!res.lastUpdated || !res.perHourMiningRate) continue;

            const maxStorage = 10_000 + (storage.find(s => s.id === res.id)?.capacity ?? 0);
            const timeDiff = Math.abs((new Date()).getTime() - res.lastUpdated.getTime());
            const hours = timeDiff / (1000 * 60 * 60);
            let amountToAdd = hours * res.perHourMiningRate;
            if(res.amount + amountToAdd > maxStorage) amountToAdd = Math.max(0, maxStorage - res.amount);

            res.amount += amountToAdd;
            res.lastUpdated = new Date();
        };

        await this.sync();

        return this.resources;
    }

    async getDifference(resources: { id: string, amount: number }[]): Promise<Array<Resource>> {
        const currentResources = await this.calculateCurrentAvailableResources();
        const difference: Resource[] = [];

        currentResources.forEach(res => {
            const currentRes = resources.find(r => r.id === res.id);
            if(currentRes) difference.push({
                id: res.id,
                amount: res.amount - currentRes.amount,
                lastUpdated: res.lastUpdated,
                perHourMiningRate: res.perHourMiningRate,
                data: res.data
            });
            else difference.push(res);
        });

        return difference;
    }

    add(resources: Resource[]) {
        for(const res of resources) {
            const resource = this.resources.find(r => r.id === res.id);
            if(resource) resource.amount += res.amount;
            else this.resources.push(res);
        }
    }

    async updateAmount(resources: { id: string, amount: number }[]) {
        await this.calculateCurrentAvailableResources();
        for(const res of resources) {
            const resource = this.resources.find(r => r.id === res.id);
            if(resource) resource.amount += res.amount;
            else this.resources.push({
                id: res.id,
                amount: res.amount,
                lastUpdated: new Date(),
                perHourMiningRate: 0,
                data: this.resourcesDB.find(r => r.id === res.id) as DBResource
            });
        }
    }

    setAmount(resources: { id: string, amount: number }[]) {
        for(const res of resources) {
            const resource = this.resources.find(r => r.id === res.id);
            if(resource) resource.amount = res.amount;
            else this.resources.push({
                id: res.id,
                amount: res.amount,
                lastUpdated: new Date(),
                perHourMiningRate: 0,
                data: this.resourcesDB.find(r => r.id === res.id) as DBResource
            });
        }
    }
}