import 'dotenv/config';
import fs from 'fs';
import path from 'path';
import crypto from 'crypto';
import { PrismaClient } from '@prisma/client';
import { getProjectRoot, getUploadDir, getTeamUploadDir } from '../src/lib/projectPaths';

const prisma = new PrismaClient();

const TEAM_ORDER = ['조태희', '최근혜', '류원준', '주완기', '정성호'];

function fileHash(filePath: string): string {
  const buf = fs.readFileSync(filePath);
  return crypto.createHash('md5').update(buf).digest('hex');
}

function resolveUploadPath(relative: string): string {
  const clean = relative.replace(/^\//, '').replace(/^uploads\//, '');
  return path.join(getUploadDir(), clean);
}

async function syncTeamPhotos() {
  const teamDir = getTeamUploadDir();
  if (!fs.existsSync(teamDir)) return;

  const pngs = fs
    .readdirSync(teamDir)
    .filter((f) => /\.(png|jpe?g|webp|gif)$/i.test(f))
    .map((f) => path.join(teamDir, f));

  const byHash = new Map<string, string>();
  const duplicates: string[] = [];

  for (const filePath of pngs) {
    const hash = fileHash(filePath);
    const existing = byHash.get(hash);
    if (existing) {
      const keep =
        fs.statSync(existing).birthtimeMs <= fs.statSync(filePath).birthtimeMs
          ? existing
          : filePath;
      duplicates.push(keep === existing ? filePath : existing);
      byHash.set(hash, keep);
    } else {
      byHash.set(hash, filePath);
    }
  }

  for (const dup of duplicates) {
    fs.unlinkSync(dup);
    console.log(`  🗑 duplicate removed: ${path.basename(dup)}`);
  }

  const unique = [...byHash.values()].sort(
    (a, b) => fs.statSync(a).birthtimeMs - fs.statSync(b).birthtimeMs,
  );

  const members = await prisma.teamMember.findMany();
  const byName = new Map(members.map((m) => [m.name, m]));

  for (let i = 0; i < TEAM_ORDER.length; i++) {
    const name = TEAM_ORDER[i];
    const member = byName.get(name);
    if (!member) continue;

    const photoPath = unique[i];
    const photoUrl = photoPath
      ? `/uploads/team/${path.basename(photoPath)}`
      : null;

    await prisma.teamMember.update({
      where: { id: member.id },
      data: { photoUrl, sortOrder: i },
    });

    console.log(`  ✓ ${name} → ${photoUrl ?? '(none)'}`);
  }

  for (const member of members) {
    if (TEAM_ORDER.includes(member.name)) continue;
    const url = member.photoUrl;
    if (!url) continue;
    const abs = resolveUploadPath(url);
    if (!fs.existsSync(abs)) {
      await prisma.teamMember.update({
        where: { id: member.id },
        data: { photoUrl: null },
      });
      console.log(`  ✓ cleared broken photo: ${member.name}`);
    }
  }
}

async function clearBrokenTeamPhotoUrls() {
  const members = await prisma.teamMember.findMany();
  for (const member of members) {
    if (!member.photoUrl) continue;
    if (member.photoUrl.startsWith('http') || member.photoUrl.startsWith('data:')) continue;
    const abs = resolveUploadPath(member.photoUrl);
    if (!fs.existsSync(abs)) {
      await prisma.teamMember.update({
        where: { id: member.id },
        data: { photoUrl: null },
      });
      console.log(`  ✓ cleared missing file: ${member.name} (${member.photoUrl})`);
    }
  }
}

async function pruneOrphanUploads() {
  const referenced = new Set<string>();

  const dbFiles = await prisma.file.findMany({ select: { path: true, filename: true } });
  for (const f of dbFiles) {
    if (f.path) referenced.add(path.normalize(f.path));
    referenced.add(path.join(getUploadDir(), f.filename));
  }

  const members = await prisma.teamMember.findMany({ select: { photoUrl: true } });
  for (const m of members) {
    if (!m.photoUrl || m.photoUrl.startsWith('http')) continue;
    referenced.add(resolveUploadPath(m.photoUrl));
  }

  const uploadRoot = getUploadDir();
  const walk = (dir: string) => {
    for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
      const full = path.join(dir, entry.name);
      if (entry.name === '.gitkeep') continue;
      if (entry.isDirectory()) {
        walk(full);
        continue;
      }
      const norm = path.normalize(full);
      if (!referenced.has(norm)) {
        fs.unlinkSync(full);
        console.log(`  🗑 orphan: ${path.relative(getProjectRoot(), full)}`);
      }
    }
  };

  walk(uploadRoot);
}

async function patchHrDataTeamPhotos() {
  const seedPath = path.join(getProjectRoot(), 'data', 'seed', 'hr-data.json');
  if (!fs.existsSync(seedPath)) return;

  const data = JSON.parse(fs.readFileSync(seedPath, 'utf-8')) as {
    TEAM?: { name: string; photo?: string }[];
  };

  if (!Array.isArray(data.TEAM)) return;

  const members = await prisma.teamMember.findMany();
  const byName = new Map(members.map((m) => [m.name, m]));

  let changed = false;
  for (const entry of data.TEAM) {
    const parsed = entry.name.match(/^(\S+)/);
    const name = parsed?.[1];
    const member = name ? byName.get(name) : null;
    const nextPhoto = member?.photoUrl ?? undefined;
    if (entry.photo !== nextPhoto) {
      if (nextPhoto) entry.photo = nextPhoto;
      else delete entry.photo;
      changed = true;
    }
  }

  if (changed) {
    fs.writeFileSync(seedPath, JSON.stringify(data, null, 2), 'utf-8');
    console.log('  ✓ hr-data.json TEAM photos synced to uploads/team paths');
  }
}

async function main() {
  console.log('📷 Sync team photos from uploads/team/ ...');
  await syncTeamPhotos();

  console.log('🔍 Clear broken photo URLs ...');
  await clearBrokenTeamPhotoUrls();

  console.log('🧹 Remove orphan uploads (not in DB) ...');
  await pruneOrphanUploads();

  console.log('📝 Update hr-data.json TEAM photo paths ...');
  await patchHrDataTeamPhotos();

  console.log('Done.');
}

main()
  .catch((err) => {
    console.error(err);
    process.exit(1);
  })
  .finally(() => prisma.$disconnect());
