import { Injectable } from "@nestjs/common";
import { Result } from "src/common/errors/result-error.factory";
import { computeHours } from "src/common/utils/date-utils";
import { PrismaPostgresService } from "prisma/postgres/prisma-postgres.service";
import { BankedHoursService } from "src/time-and-attendance/domains/services/banking-hours.service";
import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service";
import { VacationService } from "src/time-and-attendance/domains/services/vacation.service";
import { paid_time_off_mapping, paid_time_off_types } from "src/time-and-attendance/paid-time-off/paid-time-off.dto";

@Injectable()
export class PaidTimeOFfBankHoursService {
    constructor(
        private readonly prisma: PrismaPostgresService,
        private readonly bankingService: BankedHoursService,
        private readonly vacationService: VacationService,
        private readonly sickLeaveService: SickLeaveService,
    ) { }

    //called during update function of Shifts Module
    updatePaidTimeOffBankHoursWhenShiftUpdate = async (
        start_time: Date,
        end_time: Date,
        type: string,
        employee_id: number,
        og_start: Date,
        og_end: Date
    ): Promise<Result<boolean, string>> => {
        let original_hours = computeHours(og_start, og_end);
        let ajusted_hours = computeHours(start_time, end_time);

        if (type === 'BANKING') {
            original_hours = original_hours * 1.5;
            ajusted_hours = ajusted_hours * 1.5;
        }

        const diff_hours = Math.abs(ajusted_hours - original_hours);

        if (diff_hours === 0) return { success: true, data: true };
        if (!paid_time_off_types.includes(type)) return { success: false, error: 'INVALID_SHIFT_TYPE' };

        if (ajusted_hours > original_hours) {
            const validation = await this.validateAndDeductPaidTimeOff(employee_id, type, diff_hours);
            if (!validation.success) return { success: false, error: validation.error };
        } else {
            const restoration = await this.restorePaidTimeOffHours(employee_id, type, diff_hours);
            if (!restoration.success) return { success: false, error: restoration.error };
        }
        return { success: true, data: true };
    }

    validateAndDeductPaidTimeOff = async (employee_id: number, type: string, hours: number): Promise<Result<number, string>> => {
        const banking_types: string[] = ['BANKING', 'WITHDRAW_BANKED'];
        if (banking_types.includes(type)) {
            return await this.bankingService.manageBankingHours(employee_id, hours, type);
        }

        switch (type) {
            case 'VACATION': {
                return await this.vacationService.manageVacationHoursBank(employee_id, hours);
            }
            case 'SICK': {
                return await this.sickLeaveService.takeSickLeaveHours(employee_id, hours);
            }
            default:
                return { success: false, error: 'INVALID_PAID_TIME_OFF_TYPE' };
        }
    }


    restorePaidTimeOffHours = async (employee_id: number, type: string, hours: number): Promise<Result<boolean, string>> => {
        try {
            const config = paid_time_off_mapping[type];
            if (!config) return { success: false, error: 'INVALID_PAID_TIME_OFF_TYPE' }
            const operation = config.invert_logic ? 'decrement' : 'increment';

            await this.prisma.paidTimeOff.update({
                where: { employee_id },
                data: {
                    [config.field]: { [operation]: hours },
                    last_updated: new Date(),
                },
            });
            return { success: true, data: true };
        } catch (error) {
            return { success: false, error: 'PAID_TIME_OFF_NOT_FOUND' };
        }
    };


    //called during delete function of Shifts Module
    updatePaidTimeoffBankHoursWhenShiftDelete = async (start: Date, end: Date, type: string, employee_id: number) => {
        let ajusted_hours = computeHours(start, end);
        if (!paid_time_off_types.includes(type)) return;
        if (type === 'BANKING') {
            ajusted_hours = ajusted_hours * 1.5;
        }
        if (ajusted_hours < 0) ajusted_hours = 0;

        const config = paid_time_off_mapping[type];

        const last_updated = new Date();

        await this.prisma.paidTimeOff.update({
            where: { employee_id },
            data: {
                [config.field]: { [config.operation]: ajusted_hours },
                last_updated: last_updated,
            },
        });

    }
}
