import { EntityManager, In } from 'typeorm';
import { Organization, Student, StudentDingMapping } from '../entities';

export interface DingTalkStudentInput {
  dingUserId: string;
  name: string;
  mobile?: string;
}

export interface DingTalkStudentConflict {
  dingUserId: string;
  name: string;
  reason: string;
}

export interface DingTalkStudentSyncResult {
  created: number;
  updated: number;
  studentIds: Map<string, number>;
  conflicts: DingTalkStudentConflict[];
}

export async function syncDingTalkStudents(
  manager: EntityManager,
  inputs: DingTalkStudentInput[],
): Promise<DingTalkStudentSyncResult> {
  const users = new Map<string, DingTalkStudentInput>();
  const conflicts: DingTalkStudentConflict[] = [];

  for (const input of inputs) {
    const dingUserId = input.dingUserId?.trim();
    const name = input.name?.trim();
    const mobile = input.mobile?.trim() || undefined;
    if (!dingUserId || dingUserId.length > 100 || !name || name.length > 50) {
      conflicts.push({ dingUserId: dingUserId || '', name: name || '', reason: '钉钉用户ID或姓名无效' });
      continue;
    }
    if (mobile && mobile.length > 20) {
      conflicts.push({ dingUserId, name, reason: '手机号超过20个字符' });
      continue;
    }
    if (!users.has(dingUserId)) users.set(dingUserId, { dingUserId, name, mobile });
  }

  if (users.size === 0) {
    return { created: 0, updated: 0, studentIds: new Map(), conflicts };
  }

  const dingUserIds = [...users.keys()];
  const mappings = await manager.find(StudentDingMapping, {
    where: { dingUserId: In(dingUserIds) },
  });
  const mappingByDingId = new Map(mappings.map((mapping) => [mapping.dingUserId, mapping]));
  const mappedStudentIds = mappings.map((mapping) => mapping.studentId);
  const mappedStudents = mappedStudentIds.length
    ? await manager.find(Student, { where: { id: In(mappedStudentIds) } })
    : [];
  const studentById = new Map(mappedStudents.map((student) => [student.id, student]));
  const studentIds = new Map<string, number>();
  const updates: Student[] = [];

  for (const mapping of mappings) {
    const input = users.get(mapping.dingUserId);
    const student = studentById.get(mapping.studentId);
    if (!input || !student) {
      conflicts.push({
        dingUserId: mapping.dingUserId,
        name: input?.name || '',
        reason: '钉钉映射对应的学生不存在',
      });
      continue;
    }
    studentIds.set(mapping.dingUserId, student.id);
    student.name = input.name;
    if (input.mobile) student.phone = input.mobile;
    updates.push(student);
  }

  const newUsers = [...users.values()].filter((user) => !mappingByDingId.has(user.dingUserId));
  const mobiles = [...new Set(newUsers.map((user) => user.mobile).filter((mobile): mobile is string => !!mobile))];
  const occupiedPhones = mobiles.length
    ? await manager.find(Student, { where: { phone: In(mobiles) } })
    : [];
  const studentByPhone = new Map(occupiedPhones.map((student) => [student.phone, student]));
  const creatable = newUsers.filter((user) => {
    if (!user.mobile || !studentByPhone.has(user.mobile)) return true;
    conflicts.push({ dingUserId: user.dingUserId, name: user.name, reason: '手机号已属于其他学生，请人工绑定' });
    return false;
  });

  const host = creatable.length
    ? await manager.findOne(Organization, { where: { isHost: true, status: 'active' } })
    : null;
  if (creatable.length && !host) throw new Error('尚未配置本机构');

  if (updates.length) await manager.save(Student, updates);
  const createdStudents = creatable.length
    ? await manager.save(
        Student,
        creatable.map((user) =>
          manager.create(Student, {
            name: user.name,
            phone: user.mobile,
            status: 'active',
            organizationId: host!.id,
          }),
        ),
      )
    : [];
  if (createdStudents.length) {
    await manager.save(
      StudentDingMapping,
      createdStudents.map((student, index) =>
        manager.create(StudentDingMapping, {
          dingUserId: creatable[index].dingUserId,
          studentId: student.id,
        }),
      ),
    );
    createdStudents.forEach((student, index) => studentIds.set(creatable[index].dingUserId, student.id));
  }

  return { created: createdStudents.length, updated: updates.length, studentIds, conflicts };
}
