import { formatInTimeZone, fromZonedTime } from 'date-fns-tz';

export interface CalculationResult {
  work_hours: number;
  break_hours: number;
  net_hours: number;
  overtime_hours: number;
}

/**
 * Combines an attendance base date string/Date with an HH:mm time string in the given timezone
 */
export function combineDateAndTime(
  baseDate: Date | string,
  timeStr: string,
  timeZone: string,
): Date {
  const dateStr =
    typeof baseDate === 'string'
      ? baseDate.substring(0, 10)
      : formatInTimeZone(baseDate, timeZone, 'yyyy-MM-dd');

  const [hours, minutes] = timeStr.split(':').map(Number);
  const formattedTime = `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:00`;

  return fromZonedTime(`${dateStr} ${formattedTime}`, timeZone);
}

/**
 * Calculates work hours, dynamic break deduction, net hours, and overtime hours
 */
export function calculateAttendanceHours(
  checkIn: Date,
  checkOut: Date,
  schedule: {
    start_time: string;
    end_time: string;
    break_start?: string | null;
    break_end?: string | null;
  },
  timeZone: string,
): CalculationResult {
  const totalWorkMs = checkOut.getTime() - checkIn.getTime();
  const work_hours = Math.max(0, Number((totalWorkMs / (1000 * 60 * 60)).toFixed(2)));

  let break_hours = 0;

  if (schedule.break_start && schedule.break_end) {
    const breakStart = combineDateAndTime(checkIn, schedule.break_start, timeZone);
    const breakEnd = combineDateAndTime(checkIn, schedule.break_end, timeZone);

    if (breakEnd > breakStart) {
      if (checkOut <= breakStart) {
        // Teacher checked out before the scheduled break started
        break_hours = 0;
      } else if (checkOut > breakStart && checkOut < breakEnd) {
        // Teacher checked out DURING the break
        const elapsedBreakMs = checkOut.getTime() - breakStart.getTime();
        break_hours = Math.max(0, Number((elapsedBreakMs / (1000 * 60 * 60)).toFixed(2)));
      } else {
        // Teacher checked out AFTER the break ended -> full scheduled break duration deducted
        const fullBreakMs = breakEnd.getTime() - breakStart.getTime();
        break_hours = Math.max(0, Number((fullBreakMs / (1000 * 60 * 60)).toFixed(2)));
      }
    }
  }

  const net_hours = Math.max(0, Number((work_hours - break_hours).toFixed(2)));

  // Calculate overtime (hours worked beyond scheduled end time)
  let overtime_hours = 0;
  if (schedule.end_time) {
    const scheduledEnd = combineDateAndTime(checkIn, schedule.end_time, timeZone);
    if (checkOut > scheduledEnd) {
      const overtimeMs = checkOut.getTime() - scheduledEnd.getTime();
      overtime_hours = Math.max(0, Number((overtimeMs / (1000 * 60 * 60)).toFixed(2)));
    }
  }

  return {
    work_hours,
    break_hours,
    net_hours,
    overtime_hours,
  };
}

/**
 * Determines whether the teacher is 'present' or 'late' on check-in
 */
export function determineAttendanceStatus(
  checkIn: Date,
  startTimeStr: string,
  gracePeriodMinutes: number,
  timeZone: string,
): 'present' | 'late' {
  const scheduledStart = combineDateAndTime(checkIn, startTimeStr, timeZone);
  const lateThreshold = new Date(
    scheduledStart.getTime() + (gracePeriodMinutes || 0) * 60 * 1000,
  );

  return checkIn <= lateThreshold ? 'present' : 'late';
}
