import { EntityManager, In } from 'typeorm';
import { Organization, Student } from '../entities';
import type { JinshujuEntry } from './jinshuju.service';

export interface JinshujuStudentSyncResult {
  matched: number; // already-existing students matched by phone or name
  created: number;
  conflicts: Array<{ serialNumber: number; name: string; reason: string }>;
  skippedNoName: number;
}

/**
 * Match Jinshuju form entries to existing students, create new ones when no match found.
 *
 * Matching strategy (first-match):
 * 1. By phone (field mapped to phone) — most reliable
 * 2. By name (field mapped to name)
 *
 * Field mapping: we assume field_1 = name, field_2 = phone by convention.
 * ponytail: hardcoded mapping; make configurable when needed.
 */
export async function syncJinshujuStudents(
  manager: EntityManager,
  entries: JinshujuEntry[],
): Promise<JinshujuStudentSyncResult> {
  // Extract name/phone from entries
  interface ParsedEntry {
    serialNumber: number;
    name: string;
    phone?: string;
  }

  const parsed: ParsedEntry[] = [];
  let skippedNoName = 0;

  for (const entry of entries) {
    const name = typeof entry.field_1 === 'string' ? entry.field_1.trim() : '';
    if (!name) { skippedNoName++; continue; }
    const phone = typeof entry.field_2 === 'string' ? entry.field_2.trim() : undefined;
    parsed.push({ serialNumber: entry.serial_number, name, phone: phone || undefined });
  }

  if (parsed.length === 0) {
    return { matched: 0, created: 0, conflicts: [], skippedNoName };
  }

  // Match by phone first
  const phones = [...new Set(parsed.filter((p) => p.phone).map((p) => p.phone!))];
  const phoneStudents = phones.length
    ? await manager.find(Student, { where: { phone: In(phones) } })
    : [];
  const studentByPhone = new Map(phoneStudents.map((s) => [s.phone, s]));

  const matchedIds = new Set<number>();
  const matchedCount = { value: 0 };
  const conflicts: JinshujuStudentSyncResult['conflicts'] = [];

  // Match remaining by name
  const names = [...new Set(parsed.filter((p) => !p.phone || !studentByPhone.has(p.phone)).map((p) => p.name))];
  const nameStudents = names.length
    ? await manager.find(Student, { where: { name: In(names) } })
    : [];
  const studentByName = new Map<string, Student[]>();
  for (const s of nameStudents) {
    const list = studentByName.get(s.name) || [];
    list.push(s);
    studentByName.set(s.name, list);
  }

  const toCreate: Array<{ name: string; phone?: string }> = [];

  for (const p of parsed) {
    // Try phone match first
    if (p.phone && studentByPhone.has(p.phone)) {
      const student = studentByPhone.get(p.phone)!;
      if (!matchedIds.has(student.id)) {
        matchedIds.add(student.id);
        matchedCount.value++;
      }
      continue;
    }

    // Try name match
    const candidates = studentByName.get(p.name);
    if (candidates && candidates.length > 0) {
      // ponytail: take first match; no ambiguity resolution
      const student = candidates[0];
      if (!matchedIds.has(student.id)) {
        matchedIds.add(student.id);
        matchedCount.value++;
      }
      continue;
    }

    // No match — create
    toCreate.push({ name: p.name, phone: p.phone });
  }

  // Create new students
  let created = 0;
  if (toCreate.length > 0) {
    const host = await manager.findOne(Organization, { where: { isHost: true, status: 'active' } });
    if (!host) throw new Error('尚未配置本机构');

    const newStudents = toCreate.map((s) => {
      const student = {
        name: s.name,
        phone: s.phone || undefined,
        organizationId: host.id,
      };
      return manager.create(Student, student);
    });
    const saved = await manager.save(Student, newStudents);
    created = saved.length;
  }

  return { matched: matchedCount.value, created, conflicts, skippedNoName };
}
