import { Repository, In } from 'typeorm';
import { ClassSchedule, ClassStudent, StudentDingMapping, Class } from '../entities';
import type { DingTalkScheduleItem } from '../integration/dingtalk.service';

export interface DailySchedulePeriod {
  startTime: string;
  endTime: string;
  scheduleId: number;
}

export interface DailySchedulePlan {
  classId: number;
  date: string;
  shiftKey: string;
  periods: DailySchedulePeriod[];
}

/** 单次排班同步的结果 */
export interface ScheduleSyncResult {
  /** 参与同步的排课记录数 */
  scheduleCount: number;
  /** 创建/复用的班次数 */
  shiftCount: number;
  /** 创建/复用的考勤组数 */
  groupCount: number;
  /** 实际写入钉钉的排班条数 */
  syncedItems: number;
  /** 因无学生或无钉钉映射而跳过的排课数 */
  skippedNoMapping: number;
  /** 写入失败的排班批次数 */
  failedBatchCount: number;
  /** 写入失败的排班条数 */
  failedItems: number;
  /** 失败批次错误详情 */
  errors: string[];
  /** 按班级分组的详情 */
  groups: Array<{
    className: string;
    groupId: number;
    itemCount: number;
  }>;
}

export async function buildClassDingUserMap(
  classStudentRepo: Repository<ClassStudent>,
  mappingRepo: Repository<StudentDingMapping>,
  classIds: number[],
): Promise<Map<number, string[]>> {
  const result = new Map<number, string[]>();
  if (classIds.length === 0) return result;

  // 班级 → 活跃学生
  const links = await classStudentRepo.find({
    where: { classId: In(classIds), status: 'active' },
  });
  if (links.length === 0) return result;

  // 学生 → 钉钉 userId
  const studentIds = [...new Set(links.map((l) => l.studentId))];
  const mappings = await mappingRepo.find({
    where: { studentId: In(studentIds) },
  });
  const studentToDing = new Map(mappings.map((m) => [m.studentId, m.dingUserId]));

  for (const link of links) {
    const dingId = studentToDing.get(link.studentId);
    if (!dingId) continue;
    if (!result.has(link.classId)) result.set(link.classId, []);
    const arr = result.get(link.classId)!;
    if (!arr.includes(dingId)) arr.push(dingId);
  }
  return result;
}

export async function loadClassNames(
  classRepo: Repository<Class>,
  classIds: number[],
): Promise<Map<number, string>> {
  const map = new Map<number, string>();
  if (classIds.length === 0) return map;
  const classes = await classRepo.find({ where: { id: In(classIds) } });
  for (const c of classes) map.set(c.id, c.name);
  return map;
}

/**
 * 把本地排课转换为“班级 + 日期”的日排班计划。
 * 同一天相同时间段会去重，多节课按开始时间排序并合并为一个钉钉班次。
 */
export function buildDailySchedulePlans(
  schedules: ClassSchedule[],
  syncFrom: string,
  syncTo: string,
): DailySchedulePlan[] {
  const periodMapByClassDate = new Map<string, Map<string, DailySchedulePeriod>>();
  const fromDate = new Date(`${syncFrom}T00:00:00.000Z`);
  const toDate = new Date(`${syncTo}T00:00:00.000Z`);

  for (let date = new Date(fromDate); date <= toDate; date.setUTCDate(date.getUTCDate() + 1)) {
    const dateStr = date.toISOString().slice(0, 10);
    const weekDay = date.getUTCDay() === 0 ? 7 : date.getUTCDay();

    for (const schedule of schedules) {
      if (schedule.classId == null || schedule.weekDay !== weekDay) continue;
      if (dateStr < schedule.startDate || dateStr > schedule.endDate) continue;

      const classDateKey = `${schedule.classId}|${dateStr}`;
      if (!periodMapByClassDate.has(classDateKey)) {
        periodMapByClassDate.set(classDateKey, new Map());
      }
      const periods = periodMapByClassDate.get(classDateKey)!;
      const periodKey = `${schedule.startTime}-${schedule.endTime}`;
      const existing = periods.get(periodKey);
      if (!existing || schedule.id < existing.scheduleId) {
        periods.set(periodKey, {
          startTime: schedule.startTime,
          endTime: schedule.endTime,
          scheduleId: schedule.id,
        });
      }
    }
  }

  const plans: DailySchedulePlan[] = [];
  for (const [classDateKey, periodMap] of periodMapByClassDate) {
    const separator = classDateKey.indexOf('|');
    const classId = Number(classDateKey.slice(0, separator));
    const date = classDateKey.slice(separator + 1);
    const periods = [...periodMap.values()].sort(
      (left, right) =>
        left.startTime.localeCompare(right.startTime) ||
        left.endTime.localeCompare(right.endTime) ||
        left.scheduleId - right.scheduleId,
    );
    const periodSignature = periods
      .map((period) => `${period.startTime}-${period.endTime}`)
      .join('+');
    plans.push({
      classId,
      date,
      shiftKey: `${classId}|${periodSignature}`,
      periods,
    });
  }

  return plans.sort(
    (left, right) => left.date.localeCompare(right.date) || left.classId - right.classId,
  );
}

/** 每个学生每天仅生成一条钉钉排班，shift 内可包含多个课程卡段。 */
export function expandDailySchedulePlans(
  plans: DailySchedulePlan[],
  dingUserIds: string[],
  planToShiftId: Map<string, number>,
): DingTalkScheduleItem[] {
  const items: DingTalkScheduleItem[] = [];
  for (const plan of plans) {
    const shiftId = planToShiftId.get(plan.shiftKey);
    if (!shiftId) continue;
    const workDate = new Date(`${plan.date}T00:00:00+08:00`).getTime();
    for (const userid of dingUserIds) {
      items.push({ userid, work_date: workDate, shift_id: shiftId, is_rest: false });
    }
  }
  return items;
}

export function toMinutes(time: string): number {
  const [hour, minute] = time.split(':').map(Number);
  return hour * 60 + minute;
}

export function minutesBetween(startTime: string, endTime: string): number {
  const start = toMinutes(startTime);
  let end = toMinutes(endTime);
  if (end <= start) end += 24 * 60;
  return end - start;
}

export function addDays(dateStr: string, days: number): string {
  const d = new Date(`${dateStr}T00:00:00.000Z`);
  d.setUTCDate(d.getUTCDate() + days);
  return d.toISOString().slice(0, 10);
}
