import { WORKDAYS, OptionalNullable } from '@humand-packages/common';
import moment from 'moment';
import { Incidence } from '../../infrastructure/constants/incidences';
import { State } from '../../infrastructure/constants/state';
import { TimeTrackingPolicyAutoCloseMode } from '../enum/timeTracking/policy';
import { TimeTrackingEntryType } from '../types/entry';
import { DaySummaryScheduleSource } from '../types/scheduleSource';
import { isUserGeneratedSource } from '../types/source';
import { TimeTrackingDaySummaryHours } from './daySummaryHours';
import { TIME_FORMAT, TimeSlot } from './timeTracking/scheduleData';
import { TimeTrackingCategorizedHourForDaySummary } from './timeTracking/timeTrackingCategorizedHour';
import { TimeTrackingDaySummaryHoliday, TimeTrackingDaySummaryHolidayApi } from './timeTrackingDaySummaryHoliday';
import { TimeTrackingDaySummaryTimeOff, TimeTrackingDaySummaryTimeOffApi } from './timeTrackingDaySummaryTimeOff';
import { TimeTrackingEntry, TimeTrackingEntryApi } from './timeTrackingEntry';

export class TimeTrackingDaySummary {
    constructor(properties: OptionalNullable<TimeTrackingDaySummary>) {
        this.id = properties.id;
        this.date = properties.date;
        this.dateString = properties.dateString;
        this.estimatedHours = properties.estimatedHours;
        this.incidences = properties.incidences;
        this.instanceId = properties.instanceId;
        this.userId = properties.userId;
        this.workScheduleId = properties.workScheduleId;
        this.workedHours = properties.workedHours;
        this.timeTrackingEntries = properties.timeTrackingEntries;
        this.holidays = properties.holidays;
        this.timeOffRequests = properties.timeOffRequests;
        this.autoCloseTime = properties.autoCloseTime;
        this.autoCloseMode = properties.autoCloseMode;
        this.autoCloseEntryTime = properties.autoCloseEntryTime;
        this.dayChange = properties.dayChange;
        this.state = properties.state;
        this.extraHours = properties.extraHours;
        this.scheduledHours = properties.scheduledHours;
        this.weekday = properties.weekday;
        this.timeSlots = properties.timeSlots;
        this.nightShift = properties.nightShift;
        this.isWorkday = properties.isWorkday;
        this.shiftAssignedDayId = properties.shiftAssignedDayId;
        this.scheduleSource = properties.scheduleSource;
        this.isDeleted = properties.isDeleted;
        this.timeOffHours = properties.timeOffHours ?? null;
        this.latenessTolerance = properties.latenessTolerance;
        this.createPolicyHistory = properties.createPolicyHistory;
    }

    id: number;
    date: Date;
    dateString: string;
    estimatedHours: number;
    incidences: Incidence[];
    userId: number;
    timeTrackingEntries: TimeTrackingEntry[];
    workScheduleId?: number;
    workedHours: number;
    holidays?: TimeTrackingDaySummaryHoliday[];
    timeOffRequests?: TimeTrackingDaySummaryTimeOff[];
    autoCloseTime?: Date;
    autoCloseMode: TimeTrackingPolicyAutoCloseMode;
    autoCloseEntryTime?: Date;
    dayChange: Date;
    state: State;
    instanceId: number;
    extraHours: number; // transient
    scheduledHours: number;
    weekday: WORKDAYS;
    timeSlots?: TimeSlot[];
    nightShift: boolean;
    isWorkday: boolean;
    shiftAssignedDayId?: number;
    scheduleSource: DaySummaryScheduleSource;
    isDeleted: boolean;
    timeOffHours: number | null;
    latenessTolerance?: number | null;
    createPolicyHistory?: boolean;

    public getLatenessInMinutes(timezone: string): number {
        const firstSlot = this.timeSlots?.sort((a, b) => {
            // Normalize time strings to 'HH:mm' format
            const normalizedStartTimeA = a.startTime.length === 4 ? `0${a.startTime}` : a.startTime;
            const normalizedStartTimeB = b.startTime.length === 4 ? `0${b.startTime}` : b.startTime;
            return normalizedStartTimeA.localeCompare(normalizedStartTimeB);
        })[0];
        const scheduledStartTime = firstSlot ? moment.tz(firstSlot.startTime, TIME_FORMAT, timezone) : undefined;
        if (!scheduledStartTime) return 0;
        const firstEntry = this.timeTrackingEntries
            .filter((entry) => entry.type === TimeTrackingEntryType.START && isUserGeneratedSource(entry.source))
            .sort((a, b) => a.time.getTime() - b.time.getTime())[0]; // ascending order
        if (firstEntry) {
            const entryTime = moment(firstEntry.time).tz(timezone);
            scheduledStartTime.year(entryTime.year()).month(entryTime.month()).date(entryTime.date());
            const difference = entryTime.diff(scheduledStartTime, 'minutes');
            if (scheduledStartTime && difference > this.latenessTolerance!) {
                return difference;
            }
        }
        return 0;
    }
}

export class TimeTrackingDaySummaryApi {
    constructor(properties: OptionalNullable<TimeTrackingDaySummaryApi>) {
        this.id = properties.id;
        this.date = properties.date;
        this.referenceDate = properties.referenceDate;
        this.employeeId = properties.employeeId;
        this.entries = properties.entries;
        this.userId = properties.userId;
        this.incidences = properties.incidences;
        this.holidays = properties.holidays;
        this.timeOffRequests = properties.timeOffRequests;
        this.weekday = properties.weekday;
        this.categorizedHours = properties.categorizedHours;
        this.isWorkday = properties.isWorkday;
        this.hasSchedule = properties.hasSchedule;
        this.hours = properties.hours;
    }

    id: number;
    date: Date;
    referenceDate: string;
    employeeId: string;
    userId: number;
    isWorkday: boolean;
    hasSchedule: boolean;
    weekday: WORKDAYS;

    hours: TimeTrackingDaySummaryHours;
    categorizedHours: TimeTrackingCategorizedHourForDaySummary[];

    holidays?: TimeTrackingDaySummaryHolidayApi[];
    timeOffRequests?: TimeTrackingDaySummaryTimeOffApi[];
    incidences: Incidence[];
    entries: TimeTrackingEntryApi[];
    timeSlots?: TimeSlot[];
}
