/**
 * 로컬 PostgreSQL → 배포 서버(Render API) 데이터 업로드
 * 사용: npm run db:push-remote
 * 환경변수 TARGET_API_URL 로 대상 변경 가능
 */
import 'dotenv/config';
import fs from 'fs';
import path from 'path';
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();
const TARGET = (process.env.TARGET_API_URL || 'https://eene-dashboard-backend.onrender.com').replace(/\/$/, '');
const SECTIONS = ['인사관리', '학습성장', '운영관리'];
const PHOTOS_ONLY = process.argv.includes('--photos-only');
const UPLOAD_DIR = path.resolve(process.env.UPLOAD_DIR || path.join(__dirname, '../../uploads'));
const TEAM_DIR = path.join(UPLOAD_DIR, 'team');

function resolveLocalPhotoPath(photoUrl: string | null | undefined): string | null {
  if (!photoUrl || /^https?:\/\//i.test(photoUrl) || photoUrl.startsWith('data:')) return null;
  const filename = photoUrl.replace(/^\/uploads\/team\//, '').replace(/^uploads\/team\//, '');
  if (!filename || filename.includes('..')) return null;
  const filePath = path.join(TEAM_DIR, filename);
  return fs.existsSync(filePath) ? filePath : null;
}

function mimeForExt(ext: string): string {
  if (ext === '.png') return 'image/png';
  if (ext === '.webp') return 'image/webp';
  if (ext === '.gif') return 'image/gif';
  return 'image/jpeg';
}

async function uploadPhotoToRemote(filePath: string): Promise<string> {
  const buf = fs.readFileSync(filePath);
  const ext = path.extname(filePath).toLowerCase();
  const form = new FormData();
  form.append('photo', new Blob([buf], { type: mimeForExt(ext) }), path.basename(filePath));

  const res = await fetch(`${TARGET}/api/team-members/photo`, { method: 'POST', body: form });
  if (!res.ok) {
    const text = await res.text().catch(() => '');
    throw new Error(`photo upload failed: ${res.status} ${text}`);
  }
  const data = (await res.json()) as { url: string };
  return data.url;
}

async function api<T>(method: string, path: string, body?: unknown): Promise<T> {
  const res = await fetch(`${TARGET}${path}`, {
    method,
    headers: body ? { 'Content-Type': 'application/json' } : undefined,
    body: body ? JSON.stringify(body) : undefined,
  });
  if (!res.ok) {
    const text = await res.text().catch(() => '');
    throw new Error(`${method} ${path} → ${res.status} ${text}`);
  }
  if (res.status === 204) return undefined as T;
  return res.json() as Promise<T>;
}

function memberKey(name: string, cell: string | null) {
  return `${name}|${cell ?? ''}`;
}

async function ensureRemoteApiReady() {
  const res = await fetch(`${TARGET}/api/team-members`);
  if (res.status === 404) {
    throw new Error(
      '배포 서버에 team-members API가 없습니다. 코드 배포(Render) 완료 후 다시 실행하세요.',
    );
  }
  if (!res.ok) throw new Error(`team-members check failed: ${res.status}`);
}

async function syncTeamMembers(): Promise<Map<string, string>> {
  const locals = await prisma.teamMember.findMany({
    where: { isActive: true },
    orderBy: [{ sortOrder: 'asc' }, { name: 'asc' }],
  });

  type RemoteMember = {
    id: string;
    name: string;
    cell: string | null;
    rank?: string | null;
    role?: string | null;
    contact?: string | null;
    photoUrl?: string | null;
    sortOrder?: number;
  };

  let remotes: RemoteMember[] = [];
  try {
    remotes = await api<RemoteMember[]>('GET', '/api/team-members?all=1');
  } catch {
    remotes = await api<RemoteMember[]>('GET', '/api/team-members');
  }

  const remoteByKey = new Map(remotes.map((m) => [memberKey(m.name, m.cell), m]));
  const idMap = new Map<string, string>();

  for (const local of locals) {
    const key = memberKey(local.name, local.cell);
    let photoUrl = local.photoUrl;

    const localPhotoPath = resolveLocalPhotoPath(local.photoUrl);
    if (localPhotoPath) {
      try {
        photoUrl = await uploadPhotoToRemote(localPhotoPath);
        console.log(`  📷 ${local.name} — ${path.basename(localPhotoPath)}`);
      } catch (err) {
        console.warn(`  ⚠ ${local.name} photo skip:`, (err as Error).message);
      }
    } else if (local.photoUrl) {
      console.warn(`  ⚠ ${local.name} — local file not found: ${local.photoUrl}`);
    }

    const payload = {
      name: local.name,
      rank: local.rank,
      role: local.role,
      cell: local.cell,
      contact: local.contact,
      photoUrl,
      sortOrder: local.sortOrder,
      isActive: true,
    };

    const existing = remoteByKey.get(key);
    if (existing) {
      await api('PATCH', `/api/team-members/${existing.id}`, payload);
      idMap.set(local.id, existing.id);
      console.log(`  ✓ team ${local.name} (updated)`);
    } else {
      const created = await api<RemoteMember>('POST', '/api/team-members', payload);
      idMap.set(local.id, created.id);
      remoteByKey.set(key, created);
      console.log(`  ✓ team ${local.name} (created)`);
    }
  }

  return idMap;
}

async function clearRemoteTasks() {
  const remoteTasks = await api<Array<{ id: string }>>('GET', '/api/tasks');
  for (const t of remoteTasks) {
    await api('DELETE', `/api/tasks/${t.id}`);
  }
  console.log(`  removed ${remoteTasks.length} remote tasks`);
}

async function syncTasks(memberIdMap: Map<string, string>) {
  const tasks = await prisma.task.findMany({
    include: {
      milestones: { orderBy: { order: 'asc' } },
      details: { orderBy: { createdAt: 'asc' } },
      kpiMetrics: true,
      taskAssignees: true,
      pmMember: true,
    },
    orderBy: { createdAt: 'asc' },
  });

  for (const task of tasks) {
    const assigneeMemberIds = task.taskAssignees
      .map((ta) => memberIdMap.get(ta.memberId))
      .filter((id): id is string => Boolean(id));

    const pmMemberId = task.pmMemberId ? memberIdMap.get(task.pmMemberId) ?? null : null;

    const created = await api<{ id: string }>('POST', '/api/tasks', {
      title: task.title,
      description: task.description,
      status: task.status,
      priority: task.priority,
      quarter: task.quarter,
      category: task.category,
      section: task.section,
      tag: task.tag,
      taskType: task.taskType,
      progress: task.progress,
      issueNote: task.issueNote,
      startDate: task.startDate?.toISOString() ?? null,
      dueDate: task.dueDate?.toISOString() ?? null,
      showDate: task.showDate,
      showDescription: task.showDescription,
      showStatus: task.showStatus,
      showIssue: task.showIssue,
      showProgress: task.showProgress,
      pmMemberId,
      assigneeMemberIds,
    });

    const milestoneIdMap = new Map<string, string>();
    for (const ms of task.milestones) {
      const remoteMs = await api<{ id: string }>('POST', `/api/milestones/${created.id}`, {
        title: ms.title,
        description: ms.description,
        startDate: ms.startDate?.toISOString() ?? null,
        dueDate: ms.dueDate?.toISOString() ?? null,
        progress: ms.progress,
        links: ms.links,
      });
      milestoneIdMap.set(ms.id, remoteMs.id);
    }

    for (const d of task.details) {
      await api('POST', `/api/details/${created.id}`, {
        content: d.content,
        authorName: d.authorName,
        milestoneId: d.milestoneId ? milestoneIdMap.get(d.milestoneId) ?? null : null,
      });
    }

    for (const k of task.kpiMetrics) {
      await api('POST', '/api/kpi', {
        taskId: created.id,
        quarter: k.quarter,
        target: k.target,
        actual: k.actual,
        unit: k.unit,
      });
    }

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

  return tasks.length;
}

async function syncColumnConfigs() {
  const configs = await prisma.columnConfig.findMany();
  for (const config of configs) {
    await api('PATCH', `/api/columns/${encodeURIComponent(config.key)}`, {
      title: config.title,
      titleEn: config.titleEn,
      subtitle: config.subtitle,
      cardOrder: config.cardOrder,
    });
    console.log(`  ✓ column ${config.key}`);
  }

  for (const key of SECTIONS) {
    if (!configs.some((c) => c.key === key)) {
      const local = await prisma.columnConfig.findUnique({ where: { key } });
      if (local) continue;
      try {
        await api('GET', `/api/columns/${encodeURIComponent(key)}`);
      } catch {
        /* ensure exists on remote */
      }
    }
  }
}

async function main() {
  console.log(`📤 Target: ${TARGET}`);
  console.log('🔍 Checking remote API...');
  await ensureRemoteApiReady();

  const localTasks = await prisma.task.count();
  const localMembers = await prisma.teamMember.count({ where: { isActive: true } });
  console.log(`   Local: ${localTasks} tasks, ${localMembers} team members`);

  if (!PHOTOS_ONLY && localTasks === 0) {
    throw new Error('로컬 DB에 업무가 없습니다. 먼저 데이터가져오기.bat 또는 작업을 진행하세요.');
  }

  console.log(`👥 Uploading team members${PHOTOS_ONLY ? ' + photos' : ''}...`);
  console.log(`   Local photos dir: ${TEAM_DIR}`);
  const memberIdMap = await syncTeamMembers();

  if (PHOTOS_ONLY) {
    console.log('\n✅ Photos sync complete!');
    console.log('   Site: https://eene-dashboard.vercel.app/');
    return;
  }

  console.log('🗑️  Clearing remote tasks...');
  await clearRemoteTasks();

  console.log('📋 Uploading tasks...');
  await syncTasks(memberIdMap);

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

  const remoteTasks = await api<unknown[]>('GET', '/api/tasks');
  console.log(`\n✅ Done! Remote now has ${remoteTasks.length} tasks`);
  console.log(`   Site: https://eene-dashboard.vercel.app/`);
}

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