/**
 * 배포 서버(Render) 데이터 → 로컬 PostgreSQL 복사
 * 사용: npm run db:sync-remote
 * 환경변수 SOURCE_API_URL 로 원본 변경 가능
 */
import 'dotenv/config';
import bcrypt from 'bcrypt';
import { PrismaClient, type Priority, type TaskStatus } from '@prisma/client';

const prisma = new PrismaClient();
const SOURCE = (process.env.SOURCE_API_URL || 'https://eene-dashboard-backend.onrender.com').replace(/\/$/, '');
const SECTIONS = ['인사관리', '학습성장', '운영관리'];

function normalizeSection(section: string | null | undefined): string | null {
  if (!section) return null;
  if (section === '전산관리' || section === '운영지원') return '운영관리';
  if (section === '성장지원') return '학습성장';
  return section;
}

type RemoteUser = { id: string; name: string; department?: string | null };
type RemoteTask = {
  id: string;
  title: string;
  description?: string | null;
  status: TaskStatus;
  priority: Priority;
  quarter: string;
  category?: string | null;
  section?: string | null;
  tag?: string | null;
  taskType?: string | null;
  progress: number;
  issueNote?: string | null;
  startDate?: string | null;
  dueDate?: string | null;
  showDate: boolean;
  showDescription: boolean;
  showStatus: boolean;
  showIssue: boolean;
  showProgress: boolean;
  creatorId: string;
  assigneeId?: string | null;
  pmMemberId?: string | null;
  creator?: RemoteUser;
  assignee?: RemoteUser | null;
  assigneeMembers?: Array<{
    id: string;
    name: string;
    rank?: string | null;
    role?: string | null;
    cell?: string | null;
    contact?: string | null;
    photoUrl?: string | null;
    sortOrder?: number;
  }>;
  pmMember?: {
    id: string;
    name: string;
    rank?: string | null;
    role?: string | null;
    cell?: string | null;
    contact?: string | null;
    photoUrl?: string | null;
    sortOrder?: number;
  } | null;
  milestones?: Array<{
    id: string;
    title: string;
    description?: string | null;
    startDate?: string | null;
    dueDate?: string | null;
    progress: number;
    links?: string | null;
    completedAt?: string | null;
    order: number;
  }>;
  details?: Array<{
    id: string;
    content: string;
    authorName?: string | null;
    milestoneId?: string | null;
    updatedBy: string;
    createdAt: string;
    updatedAt: string;
    author?: RemoteUser;
  }>;
  kpiMetrics?: Array<{
    id: string;
    quarter: string;
    target: number;
    actual: number;
    unit?: string | null;
  }>;
  files?: Array<{
    id: string;
    filename: string;
    originalName: string;
    displayName?: string | null;
    sortOrder: number;
    mimetype: string;
    size: number;
    path: string;
    milestoneId?: string | null;
    uploadedBy: string;
    createdAt: string;
  }>;
};

async function fetchJson<T>(path: string): Promise<T> {
  const res = await fetch(`${SOURCE}${path}`);
  if (!res.ok) throw new Error(`GET ${path} failed: ${res.status}`);
  return res.json() as Promise<T>;
}

async function ensureUser(remote?: RemoteUser | null, fallbackEmail?: string): Promise<string> {
  const pw = await bcrypt.hash('imported!', 10);
  const email = fallbackEmail || `${(remote?.name || 'user').replace(/\s/g, '')}@import.local`;
  const user = await prisma.user.upsert({
    where: { email },
    update: { name: remote?.name || email },
    create: {
      email,
      password: pw,
      name: remote?.name || '사용자',
      role: 'MEMBER',
      department: remote?.department || 'EENE',
    },
  });
  return user.id;
}

async function ensureTeamMember(data: {
  name: string;
  rank?: string | null;
  role?: string | null;
  cell?: string | null;
  contact?: string | null;
  photoUrl?: string | null;
  sortOrder?: number;
}): Promise<string> {
  const existing = await prisma.teamMember.findFirst({
    where: { name: data.name, cell: data.cell ?? null },
  });
  if (existing) {
    return prisma.teamMember
      .update({
        where: { id: existing.id },
        data: {
          rank: data.rank ?? existing.rank,
          role: data.role ?? existing.role,
          contact: data.contact ?? existing.contact,
          photoUrl: data.photoUrl ?? existing.photoUrl,
          sortOrder: data.sortOrder ?? existing.sortOrder,
          isActive: true,
        },
      })
      .then((m) => m.id);
  }
  const created = await prisma.teamMember.create({
    data: {
      name: data.name,
      rank: data.rank ?? null,
      role: data.role ?? null,
      cell: data.cell ?? null,
      contact: data.contact ?? null,
      photoUrl: data.photoUrl ?? null,
      sortOrder: data.sortOrder ?? 0,
    },
  });
  return created.id;
}

async function clearLocalData() {
  await prisma.file.deleteMany({});
  await prisma.taskDetail.deleteMany({});
  await prisma.taskAssignee.deleteMany({});
  await prisma.milestone.deleteMany({});
  await prisma.kpiMetric.deleteMany({});
  await prisma.task.deleteMany({});
}

async function syncColumnConfigs() {
  for (const key of SECTIONS) {
    try {
      const config = await fetchJson<{
        key: string;
        title: string;
        titleEn?: string | null;
        subtitle?: string | null;
        cardOrder?: string | null;
      }>(`/api/columns/${encodeURIComponent(key)}`);

      await prisma.columnConfig.upsert({
        where: { key },
        create: {
          key,
          title: config.title,
          titleEn: config.titleEn ?? '',
          subtitle: config.subtitle ?? '',
          cardOrder: config.cardOrder ?? null,
        },
        update: {
          title: config.title,
          titleEn: config.titleEn ?? '',
          subtitle: config.subtitle ?? '',
          cardOrder: config.cardOrder ?? null,
        },
      });
      console.log(`  ✓ column: ${key}`);
    } catch (err) {
      console.warn(`  ⚠ column skip ${key}:`, (err as Error).message);
    }
  }
}

async function syncTeamMembersFromTasks(tasks: RemoteTask[]) {
  const seen = new Set<string>();
  for (const task of tasks) {
    const members = [
      ...(task.assigneeMembers ?? []),
      ...(task.pmMember ? [task.pmMember] : []),
    ];
    for (const m of members) {
      const key = `${m.name}|${m.cell ?? ''}`;
      if (seen.has(key)) continue;
      seen.add(key);
      await ensureTeamMember(m);
    }
  }
}

async function main() {
  console.log(`📡 Source: ${SOURCE}`);
  console.log('📥 Fetching remote tasks...');

  const list = await fetchJson<RemoteTask[]>('/api/tasks');
  console.log(`   Found ${list.length} tasks`);

  const fullTasks: RemoteTask[] = [];
  for (const item of list) {
    const full = await fetchJson<RemoteTask>(`/api/tasks/${item.id}`);
    fullTasks.push(full);
  }

  console.log('🗑️  Clearing local task data...');
  await clearLocalData();

  const adminId = await ensureUser({ id: '', name: '관리자' }, 'admin@eene.com');
  const userMap = new Map<string, string>();
  userMap.set('admin', adminId);

  console.log('👥 Syncing team members...');
  try {
    const remoteMembers = await fetchJson<
      Array<{
        name: string;
        rank?: string | null;
        role?: string | null;
        cell?: string | null;
        contact?: string | null;
        photoUrl?: string | null;
        sortOrder?: number;
      }>
    >('/api/team-members');
    for (const m of remoteMembers) await ensureTeamMember(m);
    console.log(`   ${remoteMembers.length} team members from API`);
  } catch {
    await syncTeamMembersFromTasks(fullTasks);
    console.log('   team members inferred from tasks');
  }

  console.log('📋 Importing tasks...');
  for (const remote of fullTasks) {
    let creatorId = userMap.get(remote.creatorId);
    if (!creatorId) {
      creatorId = await ensureUser(remote.creator, `creator-${remote.creatorId}@import.local`);
      userMap.set(remote.creatorId, creatorId);
    }

    let assigneeId: string | null = null;
    if (remote.assignee) {
      let mapped = userMap.get(remote.assignee.id);
      if (!mapped) {
        mapped = await ensureUser(remote.assignee, `assignee-${remote.assignee.id}@import.local`);
        userMap.set(remote.assignee.id, mapped);
      }
      assigneeId = mapped;
    }

    let pmMemberId: string | null = null;
    if (remote.pmMember) {
      pmMemberId = await ensureTeamMember(remote.pmMember);
    }

    const task = await prisma.task.create({
      data: {
        title: remote.title,
        description: remote.description ?? null,
        status: remote.status,
        priority: remote.priority,
        quarter: remote.quarter,
        category: remote.category ?? null,
        section: normalizeSection(remote.section),
        tag: remote.tag ?? null,
        taskType: remote.taskType ?? null,
        progress: remote.progress ?? 0,
        issueNote: remote.issueNote ?? null,
        startDate: remote.startDate ? new Date(remote.startDate) : null,
        dueDate: remote.dueDate ? new Date(remote.dueDate) : null,
        showDate: remote.showDate,
        showDescription: remote.showDescription,
        showStatus: remote.showStatus,
        showIssue: remote.showIssue,
        showProgress: remote.showProgress,
        creatorId,
        assigneeId,
        pmMemberId,
      },
    });

    const milestoneIdMap = new Map<string, string>();
    for (const ms of remote.milestones ?? []) {
      const created = await prisma.milestone.create({
        data: {
          taskId: task.id,
          title: ms.title,
          description: ms.description ?? null,
          startDate: ms.startDate ? new Date(ms.startDate) : null,
          dueDate: ms.dueDate ? new Date(ms.dueDate) : null,
          progress: ms.progress ?? 0,
          links: ms.links ?? null,
          completedAt: ms.completedAt ? new Date(ms.completedAt) : null,
          order: ms.order ?? 0,
        },
      });
      milestoneIdMap.set(ms.id, created.id);
    }

    for (const d of remote.details ?? []) {
      let authorId = userMap.get(d.updatedBy);
      if (!authorId) {
        authorId = await ensureUser(d.author, `author-${d.updatedBy}@import.local`);
        userMap.set(d.updatedBy, authorId);
      }
      await prisma.taskDetail.create({
        data: {
          taskId: task.id,
          milestoneId: d.milestoneId ? milestoneIdMap.get(d.milestoneId) ?? null : null,
          content: d.content,
          authorName: d.authorName ?? null,
          updatedBy: authorId,
          createdAt: new Date(d.createdAt),
          updatedAt: new Date(d.updatedAt),
        },
      });
    }

    for (const k of remote.kpiMetrics ?? []) {
      await prisma.kpiMetric.create({
        data: {
          taskId: task.id,
          quarter: k.quarter,
          target: k.target,
          actual: k.actual,
          unit: k.unit ?? null,
        },
      });
    }

    const assigneeMemberIds: string[] = [];
    for (const m of remote.assigneeMembers ?? []) {
      assigneeMemberIds.push(await ensureTeamMember(m));
    }
    if (assigneeMemberIds.length > 0) {
      await prisma.taskAssignee.createMany({
        data: [...new Set(assigneeMemberIds)].map((memberId) => ({ taskId: task.id, memberId })),
      });
    }

    for (const f of remote.files ?? []) {
      let uploaderId = userMap.get(f.uploadedBy);
      if (!uploaderId) {
        uploaderId = creatorId;
        userMap.set(f.uploadedBy, uploaderId);
      }
      await prisma.file.create({
        data: {
          taskId: task.id,
          milestoneId: f.milestoneId ? milestoneIdMap.get(f.milestoneId) ?? null : null,
          filename: f.filename,
          originalName: f.originalName,
          displayName: f.displayName ?? null,
          sortOrder: f.sortOrder ?? 0,
          mimetype: f.mimetype,
          size: f.size,
          path: f.path,
          uploadedBy: uploaderId,
          createdAt: new Date(f.createdAt),
        },
      });
    }

    console.log(`  ✓ ${remote.title}`);
  }

  console.log('📐 Syncing column order...');
  await syncColumnConfigs();

  const count = await prisma.task.count();
  console.log(`\n✅ Done! Local DB now has ${count} tasks (from ${SOURCE})`);
  console.log('   Refresh http://localhost:3000');
}

main()
  .catch((err) => {
    console.error('❌ Sync failed:', err);
    process.exit(1);
  })
  .finally(() => prisma.$disconnect());
