import { formatAppMinuteOfDay } from '@/lib/i18n/format';

export const MINUTES_PER_DAY = 24 * 60;
export const SCHEDULE_SLOT_MINUTES = 15;

export const WEEKDAY_KEYS = [
  'weekdayMon',
  'weekdayTue',
  'weekdayWed',
  'weekdayThu',
  'weekdayFri',
  'weekdaySat',
  'weekdaySun',
] as const;

type WorkingHoursTranslate = (key: string, values?: { day: string }) => string;

export type WorkingHoursBlock = {
  dayOfWeek: number;
  startMinute: number;
  endMinute: number;
  sortOrder?: number;
};

export type WorkingHoursDayBlock = {
  startMinute: number;
  endMinute: number;
};

export type WorkingHoursEditorDay = {
  dayOfWeek: number;
  isWorking: boolean;
  shifts: { startMinute: number; endMinute: number }[];
};

export function localDayOfWeekMondayZero(dayOfWeekJs: number): number {
  return dayOfWeekJs === 0 ? 6 : dayOfWeekJs - 1;
}

export function minutesToTimeInput(minutes: number): string {
  const h = Math.floor(minutes / 60);
  const m = minutes % 60;
  return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`;
}

export function timeInputToMinutes(value: string): number | null {
  const match = /^(\d{1,2}):(\d{2})$/.exec(value.trim());
  if (!match) return null;
  const h = Number(match[1]);
  const m = Number(match[2]);
  if (h < 0 || h > 23 || m < 0 || m > 59) return null;
  return h * 60 + m;
}

export function formatMinuteLabel(minute: number, locale: string): string {
  return formatAppMinuteOfDay(minute, locale);
}

export function emptyWorkingHoursEditorDays(): WorkingHoursEditorDay[] {
  return WEEKDAY_KEYS.map((_, dayOfWeek) => ({
    dayOfWeek,
    isWorking: false,
    shifts: [{ startMinute: 9 * 60, endMinute: 17 * 60 }],
  }));
}

export function editorDaysFromBlocks(blocks: WorkingHoursBlock[]): WorkingHoursEditorDay[] {
  const byDay = new Map<number, WorkingHoursDayBlock[]>();
  for (const block of blocks) {
    const list = byDay.get(block.dayOfWeek) ?? [];
    list.push({ startMinute: block.startMinute, endMinute: block.endMinute });
    byDay.set(block.dayOfWeek, list);
  }

  return WEEKDAY_KEYS.map((_, dayOfWeek) => {
    const shifts = (byDay.get(dayOfWeek) ?? []).sort(
      (a, b) => a.startMinute - b.startMinute || a.endMinute - b.endMinute,
    );
    return {
      dayOfWeek,
      isWorking: shifts.length > 0,
      shifts: shifts.length > 0 ? shifts : [{ startMinute: 9 * 60, endMinute: 17 * 60 }],
    };
  });
}

export function blocksFromEditorDays(days: WorkingHoursEditorDay[]): WorkingHoursBlock[] {
  const blocks: WorkingHoursBlock[] = [];
  for (const day of days) {
    if (!day.isWorking) continue;
    day.shifts.forEach((shift, index) => {
      blocks.push({
        dayOfWeek: day.dayOfWeek,
        startMinute: shift.startMinute,
        endMinute: shift.endMinute,
        sortOrder: index,
      });
    });
  }
  return blocks;
}

export function validateEditorDays(
  days: WorkingHoursEditorDay[],
  t: WorkingHoursTranslate,
): string | null {
  for (const day of days) {
    if (!day.isWorking) continue;
    const dayLabel = t(WEEKDAY_KEYS[day.dayOfWeek]);
    if (day.shifts.length === 0) {
      return t('validationNeedsShift', { day: dayLabel });
    }
    const sorted = [...day.shifts].sort((a, b) => a.startMinute - b.startMinute);
    for (const shift of sorted) {
      if (shift.endMinute <= shift.startMinute) {
        return t('validationEndAfterStart', { day: dayLabel });
      }
    }
    for (let i = 1; i < sorted.length; i += 1) {
      if (sorted[i].startMinute < sorted[i - 1].endMinute) {
        return t('validationOverlap', { day: dayLabel });
      }
    }
  }
  return null;
}

export function blocksForDay(blocks: WorkingHoursBlock[], dayOfWeek: number): WorkingHoursDayBlock[] {
  return blocks
    .filter((b) => b.dayOfWeek === dayOfWeek)
    .sort((a, b) => a.startMinute - b.startMinute || a.endMinute - b.endMinute)
    .map((b) => ({ startMinute: b.startMinute, endMinute: b.endMinute }));
}

export function isMinuteWithinWorkingBlocks(
  minute: number,
  dayBlocks: WorkingHoursDayBlock[],
): boolean {
  return dayBlocks.some((b) => minute >= b.startMinute && minute < b.endMinute);
}

export function isSlotWithinWorkingBlocks(
  slotStartMinute: number,
  slotMinutes: number,
  dayBlocks: WorkingHoursDayBlock[],
): boolean {
  const slotEnd = slotStartMinute + slotMinutes;
  for (let m = slotStartMinute; m < slotEnd; m += 1) {
    if (!isMinuteWithinWorkingBlocks(m, dayBlocks)) {
      return false;
    }
  }
  return true;
}

export function appointmentWithinWorkingHours(
  startAt: Date,
  endAt: Date,
  dayBlocks: WorkingHoursDayBlock[],
): boolean {
  const startMinute = startAt.getHours() * 60 + startAt.getMinutes();
  const endMinute = endAt.getHours() * 60 + endAt.getMinutes();
  if (endMinute <= startMinute) return false;
  for (let m = startMinute; m < endMinute; m += 1) {
    if (!isMinuteWithinWorkingBlocks(m, dayBlocks)) {
      return false;
    }
  }
  return true;
}

export function unionDayBlockRange(dayBlocksList: WorkingHoursDayBlock[][]): {
  startMinute: number;
  endMinute: number;
} | null {
  let startMinute: number | null = null;
  let endMinute: number | null = null;

  for (const dayBlocks of dayBlocksList) {
    for (const block of dayBlocks) {
      startMinute =
        startMinute == null ? block.startMinute : Math.min(startMinute, block.startMinute);
      endMinute = endMinute == null ? block.endMinute : Math.max(endMinute, block.endMinute);
    }
  }

  if (startMinute == null || endMinute == null) {
    return null;
  }

  return { startMinute, endMinute };
}

export function snapRangeToSlots(
  startMinute: number,
  endMinute: number,
  slotMinutes: number,
): { startMinute: number; endMinute: number; slotCount: number } {
  const start = Math.floor(startMinute / slotMinutes) * slotMinutes;
  const end = Math.ceil(endMinute / slotMinutes) * slotMinutes;
  return {
    startMinute: start,
    endMinute: end,
    slotCount: Math.max(1, (end - start) / slotMinutes),
  };
}

export function generateSlotStarts(
  startMinute: number,
  endMinute: number,
  slotMinutes: number,
): number[] {
  const slots: number[] = [];
  for (let m = startMinute; m < endMinute; m += slotMinutes) {
    slots.push(m);
  }
  return slots;
}

export function generateHourLabelsInRange(startMinute: number, endMinute: number): number[] {
  const firstHour = Math.floor(startMinute / 60);
  const lastHour = Math.ceil(endMinute / 60);
  const hours: number[] = [];
  for (let h = firstHour; h < lastHour; h += 1) {
    hours.push(h);
  }
  return hours;
}
