import { Injectable } from "@nestjs/common";
import { computeHours, getWeekStart } from "src/common/utils/date-utils";
import { PrismaPostgresService } from "prisma/postgres/prisma-postgres.service";
import { EmailToIdResolver } from "src/common/mappers/email-id.mapper";
import { MS_PER_WEEK } from "src/common/utils/constants.utils";
import { Result } from "src/common/errors/result-error.factory";
/* 
    le calcul est 1/20 des 4 dernières semaines, précédent la semaine incluant le férier. 
    Un maximum de 08h00 est allouable pour le férier
    Un maximum de 40hrs par semaine est retenue pour faire le calcul.
    le bank-code à soumettre à Desjardins doit être le G104
*/

@Injectable()
export class HolidayService {
    constructor(
        private readonly prisma: PrismaPostgresService,
        private readonly emailResolver: EmailToIdResolver,
    ) { }

    private async computeHoursPrevious4WeeksByEmail(email: string, holiday_date: Date): Promise<Result<number, string>> {
        const employee_id = await this.emailResolver.findIdByEmail(email);
        if (!employee_id.success) return { success: false, error: employee_id.error };
        return this.computeHoursPrevious4Weeks(employee_id.data, holiday_date);
    }

    private async computeHoursPrevious4Weeks(employee_id: number, holiday_date: Date): Promise<Result<number, string>> {
        try {
            const holiday_week_start = getWeekStart(holiday_date);
            const window_start = new Date(holiday_week_start.getTime() - 4 * MS_PER_WEEK);
            const window_end = new Date(holiday_week_start.getTime() - 1);

            const valid_codes = ['G1', 'G43', 'G56', 'G104', 'G105', 'G305', 'G700', 'G720'];
            const shifts = await this.prisma.shifts.findMany({
                where: {
                    timesheet: { employee_id: employee_id },
                    date: { gte: window_start, lte: window_end },
                    bank_code: { bank_code: { in: valid_codes } },
                },
                select: { date: true, start_time: true, end_time: true },
            });

            const hours_by_week = new Map<number, number>();
            for (const shift of shifts) {
                const hours = computeHours(shift.start_time, shift.end_time);
                if (hours <= 0) continue;
                const shift_week_start = getWeekStart(shift.date);
                const key = shift_week_start.getTime();
                hours_by_week.set(key, (hours_by_week.get(key) ?? 0) + hours);
            }

            let capped_total = 0;
            for (let offset = 1; offset <= 4; offset++) {
                const week_start = new Date(holiday_week_start.getTime() - offset * MS_PER_WEEK);
                const key = week_start.getTime();
                const weekly_hours = hours_by_week.get(key) ?? 0;
                capped_total += Math.min(weekly_hours, 40);
            }

            const average_daily_hours = capped_total / 20;
            return { success: true, data: average_daily_hours };
        } catch (error) {
            return { success: false, error: `an error occureded during holiday calculation` }
        }
    }

    async calculateHolidayPay(email: string, holiday_date: Date, modifier: number): Promise<Result<number, string>> {
        const average_daily_hours = await this.computeHoursPrevious4WeeksByEmail(email, holiday_date);
        if (!average_daily_hours.success) return { success: false, error: average_daily_hours.error };

        const daily_rate = (Math.min(average_daily_hours.data, 8)) * modifier;
        return { success: true, data: daily_rate };
    }
}