import { Repository } from 'typeorm';
import {
  AttendanceRecord,
  ClassSchedule,
  ClassStudent,
  DingAttendanceRaw,
  DingLeaveRaw,
} from '../entities';
import {
  getLessonAttendanceWindow,
  getLessonPunchMetadata,
  mapDingTalkStatus,
  selectDingTalkRecordsForLesson,
  type LessonScheduleLike,
} from './attendance-dingtalk';

type LessonRecordSchedule = Pick<
  ClassSchedule,
  'classId' | 'startTime' | 'endTime' | 'attendanceAdvanceMinutes'
>;

/**
 * 结算（finalize）时无打卡的学生，若当天存在钉钉已审批通过的请假且与
 * 本节课时间窗口重叠，则记为 leave，而不是缺勤。
 */
export async function resolveLessonStatus(
  dingLeaveRawRepo: Repository<DingLeaveRaw>,
  studentId: number,
  raw: DingAttendanceRaw[],
  schedule: LessonScheduleLike,
  lessonDate: string,
  finalize: boolean,
): Promise<{ status: string; remark?: string }> {
  const hasPunch = raw.some((item) => item.checkInTime || item.checkOutTime);
  if (!finalize) {
    return {
      status: mapDingTalkStatus(raw, false),
      remark: hasPunch ? undefined : '未获取到钉钉打卡结果',
    };
  }
  if (hasPunch) return { status: 'present', remark: undefined };

  const leave = await findApprovedLeaveForStudent(dingLeaveRawRepo, studentId, schedule, lessonDate);
  if (leave) {
    return {
      status: 'leave',
      remark: `钉钉请假已通过（${leave.leaveType || leave.tagName || '请假'}）`,
    };
  }
  return { status: 'absent', remark: '课程截止仍未打卡' };
}

export function createLessonRecord(
  recordRepo: Repository<AttendanceRecord>,
  classStudent: ClassStudent,
  raw: DingAttendanceRaw[],
  options: {
    schedule: LessonRecordSchedule;
    scheduleId: number;
    lessonDate: string;
    lessonSessionKey: string;
    attendanceSessionId: number;
    status: string;
    remark?: string;
  },
): AttendanceRecord {
  return recordRepo.create({
    studentId: classStudent.studentId,
    student: classStudent.student,
    classId: options.schedule.classId!,
    scheduleId: options.scheduleId,
    attendanceSessionId: options.attendanceSessionId,
    attendanceDate: options.lessonDate,
    session: options.lessonSessionKey,
    status: options.status,
    source: 'dingtalk',
    ...getLessonPunchMetadata(raw, options.lessonDate, options.schedule.startTime),
    remark: options.remark,
  });
}

export async function buildLessonRecord(
  recordRepo: Repository<AttendanceRecord>,
  dingLeaveRawRepo: Repository<DingLeaveRaw>,
  rawByStudent: Map<number, DingAttendanceRaw[]>,
  classStudent: ClassStudent,
  schedule: LessonRecordSchedule,
  lessonDate: string,
  lessonSessionKey: string,
  attendanceSessionId: number,
  scheduleId: number,
  finalize: boolean,
): Promise<AttendanceRecord> {
  const raw = selectDingTalkRecordsForLesson(
    rawByStudent.get(classStudent.studentId) ?? [],
    schedule,
    lessonDate,
  );
  const resolved = await resolveLessonStatus(
    dingLeaveRawRepo,
    classStudent.studentId,
    raw,
    schedule,
    lessonDate,
    finalize,
  );
  return createLessonRecord(recordRepo, classStudent, raw, {
    schedule,
    scheduleId,
    lessonDate,
    lessonSessionKey,
    attendanceSessionId,
    status: resolved.status,
    remark: resolved.remark,
  });
}

export async function findApprovedLeaveForStudent(
  dingLeaveRawRepo: Repository<DingLeaveRaw>,
  studentId: number,
  schedule: LessonScheduleLike,
  lessonDate: string,
): Promise<DingLeaveRaw | null> {
  const leaves = await dingLeaveRawRepo.find({
    where: { matchedStudentId: studentId },
  });
  const window = getLessonAttendanceWindow(schedule, lessonDate);
  const overlapping = leaves.filter(
    (leave) =>
      leave.startTime &&
      leave.endTime &&
      leave.startTime.getTime() <= window.end &&
      leave.endTime.getTime() >= window.start,
  );
  overlapping.sort(
    (left, right) =>
      (right.approvedAt?.getTime() ?? 0) - (left.approvedAt?.getTime() ?? 0),
  );
  return overlapping[0] ?? null;
}
