import { AttendanceRecord, DingAttendanceRaw, ClassSchedule } from '../entities';
import { toMinutes, shiftDate } from './attendance-time';

export type LessonScheduleLike = Pick<
  ClassSchedule,
  'startTime' | 'endTime' | 'attendanceAdvanceMinutes'
>;

export function getLessonAttendanceWindow(
  schedule: LessonScheduleLike,
  lessonDate: string,
): { start: number; end: number; dateFrom: string; dateTo: string } {
  const startMinuteOfDay = toMinutes(schedule.startTime);
  const endMinuteOfDay = toMinutes(schedule.endTime);
  const advanceMinutes = Math.max(0, schedule.attendanceAdvanceMinutes ?? 30);
  const lessonStart = new Date(`${lessonDate}T${schedule.startTime}:00+08:00`).getTime();
  let lessonEnd = new Date(`${lessonDate}T${schedule.endTime}:00+08:00`).getTime();
  const overnight = endMinuteOfDay <= startMinuteOfDay;
  if (overnight) lessonEnd += 24 * 60 * 60 * 1000;

  return {
    start: lessonStart - advanceMinutes * 60 * 1000,
    end: lessonEnd,
    dateFrom: advanceMinutes > startMinuteOfDay ? shiftDate(lessonDate, -1) : lessonDate,
    dateTo: overnight ? shiftDate(lessonDate, 1) : lessonDate,
  };
}

export function getLessonAttendanceImportDateRange(
  schedule: LessonScheduleLike,
  lessonDate: string,
): { startDate: string; endDate: string } {
  const window = getLessonAttendanceWindow(schedule, lessonDate);
  return { startDate: window.dateFrom, endDate: window.dateTo };
}

export function selectDingTalkRecordsForLesson(
  records: DingAttendanceRaw[],
  schedule: LessonScheduleLike,
  lessonDate: string,
): DingAttendanceRaw[] {
  const window = getLessonAttendanceWindow(schedule, lessonDate);
  return records.filter((record) => {
    // 上班、下班打卡都有效，按原始记录中实际存在的时间判断。
    const time = record.checkInTime ?? record.checkOutTime;
    return time && time.getTime() >= window.start && time.getTime() <= window.end;
  });
}

export function mapDingTalkStatus(records: DingAttendanceRaw[], finalize = false): string {
  const hasPunch = records.some((record) => record.checkInTime || record.checkOutTime);
  if (hasPunch) return 'present';
  return finalize ? 'absent' : 'pending';
}

export function getLessonPunchMetadata(
  records: DingAttendanceRaw[],
  lessonDate: string,
  startTime: string,
): Pick<AttendanceRecord, 'punchTime' | 'punchSource' | 'punchDeviceName' | 'punchDeviceId'> {
  const punches = records
    .map((record) => ({ record, time: record.checkInTime ?? record.checkOutTime }))
    .filter((item): item is { record: DingAttendanceRaw; time: Date } => !!item.time);
  if (punches.length === 0) {
    return {
      punchTime: null,
      punchSource: null,
      punchDeviceName: null,
      punchDeviceId: null,
    };
  }

  const lessonStart = new Date(`${lessonDate}T${startTime}:00+08:00`).getTime();
  punches.sort(
    (left, right) =>
      Math.abs(left.time.getTime() - lessonStart) - Math.abs(right.time.getTime() - lessonStart),
  );
  const primary = punches[0];
  const metadataRecord = [...punches]
    .filter(({ record }) =>
      !!(record.punchSource || record.punchDeviceName || record.punchDeviceId) ||
      !['OnDuty', 'OffDuty'].includes(record.attendanceType),
    )
    .sort(
      (left, right) =>
        Math.abs(left.time.getTime() - primary.time.getTime()) -
        Math.abs(right.time.getTime() - primary.time.getTime()),
    )[0]?.record;
  const source =
    metadataRecord?.punchSource ||
    (metadataRecord && !['OnDuty', 'OffDuty'].includes(metadataRecord.attendanceType)
      ? metadataRecord.attendanceType
      : primary.record.punchSource);

  return {
    punchTime: primary.time,
    punchSource: source || null,
    punchDeviceName: metadataRecord?.punchDeviceName || primary.record.punchDeviceName || null,
    punchDeviceId: metadataRecord?.punchDeviceId || primary.record.punchDeviceId || null,
  };
}
