import { unwrapAndClone } from "src/utils/unwrap-and-clone";
import { TIME_FORMAT_PATTERN } from "src/modules/timesheets/constants/shift.constants";
import { GenericApiError } from "src/modules/timesheets/models/expense.validation";
import { useShiftStore } from "src/stores/shift-store";
import { default_shift, type Shift, type UpsertShift } from "src/modules/timesheets/models/shift.models";
import { deepEqual } from "src/utils/deep-equal";

export const useShiftApi = () => {
    const shift_store = useShiftStore();

    const normalizeShiftPayload = (shift: Shift): Shift => {
        const comment = shift.comment?.trim() || undefined;

        return {
            date: shift.date,
            start_time: shift.start_time,
            end_time: shift.end_time,
            type: shift.type,
            is_approved: false,
            is_remote: shift.is_remote,
            comment: comment,
        };
    };

    const parseHHMM = (s: string): [number, number] => {
        const m = /^(\d{2}):(\d{2})$/.exec(s);

        if (!m) {
            throw new GenericApiError({ status_code: 400, message: `Invalid time format: ${s}. Expected HH:MM.` });
        }

        const h = Number(m[1]);
        const min = Number(m[2]);

        if (Number.isNaN(h) || Number.isNaN(min) || h < 0 || h > 23 || min < 0 || min > 59) {
            throw new GenericApiError({ status_code: 400, message: `Invalid time value: ${s}.` })
        }
        return [h, min];
    };

    const toMinutes = (hhmm: string): number => {
        const [h, m] = parseHHMM(hhmm);
        return h * 60 + m;
    };

    const validateShift = (shift: Shift, label: 'old_shift' | 'new_shift') => {
        if (!TIME_FORMAT_PATTERN.test(shift.start_time) || !TIME_FORMAT_PATTERN.test(shift.end_time)) {
            throw new GenericApiError({
                status_code: 400,
                message: `Invalid time format in ${label}. Expected HH:MM`,
                context: { [label]: shift }
            });
        }

        if (toMinutes(shift.end_time) <= toMinutes(shift.start_time)) {
            throw new GenericApiError({
                status_code: 400,
                message: `Invalid time range in ${label}. The End time must be after the Start time`,
                context: { [label]: shift }
            });
        }
    };

    const upsertOrDeleteShiftByEmployeeEmail = async (employee_email: string): Promise<void> => {
        const flat_upsert_shift: UpsertShift = {
            ...(deepEqual(shift_store.initial_shift, default_shift) ? { old_shift: unwrapAndClone(shift_store.initial_shift) } : {}),
            ...(deepEqual(shift_store.current_shift, default_shift) ? { new_shift: unwrapAndClone(shift_store.current_shift) } : {}),
        };

        const normalized_upsert_shift: UpsertShift = {
            ...(flat_upsert_shift.old_shift ? { old_shift: normalizeShiftPayload(flat_upsert_shift.old_shift) } : {}),
            ...(flat_upsert_shift.new_shift ? { new_shift: normalizeShiftPayload(flat_upsert_shift.new_shift) } : {}),
        };

        if (normalized_upsert_shift.old_shift) validateShift(normalized_upsert_shift.old_shift, 'old_shift');
        if (normalized_upsert_shift.new_shift) validateShift(normalized_upsert_shift.new_shift, 'new_shift');

        await shift_store.upsertOrDeleteShiftByEmployeeEmail(employee_email, normalized_upsert_shift);
    };

    return {
        upsertOrDeleteShiftByEmployeeEmail,
    };
}

