import fs from "node:fs/promises";
import path from "node:path";
import { prisma } from "../utils/prisma";
import { datasetRoot } from "./paths";

type ExportMeta = {
  exportedAt: string;
  version: number;
};

async function ensureDir(p: string) {
  await fs.mkdir(p, { recursive: true });
}

async function writeJson(p: string, value: unknown) {
  await fs.writeFile(p, JSON.stringify(value, null, 2) + "\n", "utf8");
}

function jsonlLine(value: unknown) {
  return JSON.stringify(value) + "\n";
}

export async function exportDatasetFromPrisma() {
  throw new Error("exportDatasetFromPrisma now requires { teamId, userId }");
}

export async function exportDatasetFromPrismaFor(input: { teamId: string; userId: string }) {
  const root = datasetRoot(input);
  const tmp = root + ".tmp";

  await fs.rm(tmp, { recursive: true, force: true });
  await ensureDir(tmp);

  const contactsDir = path.join(tmp, "contacts");
  const notesDir = path.join(tmp, "notes");
  const messagesDir = path.join(tmp, "messages");
  const eventsDir = path.join(tmp, "events");
  const indexDir = path.join(tmp, "index");
  await Promise.all([
    ensureDir(contactsDir),
    ensureDir(notesDir),
    ensureDir(messagesDir),
    ensureDir(eventsDir),
    ensureDir(indexDir),
  ]);

  const contacts = await prisma.contact.findMany({
    where: { teamId: input.teamId },
    orderBy: { updatedAt: "desc" },
    include: {
      note: { select: { content: true, updatedAt: true } },
      messages: {
        select: {
          kind: true,
          direction: true,
          channel: true,
          content: true,
          durationSec: true,
          transcriptJson: true,
          occurredAt: true,
        },
        orderBy: { occurredAt: "asc" },
      },
      events: {
        select: { title: true, startsAt: true, endsAt: true, isArchived: true, note: true },
        orderBy: { startsAt: "asc" },
      },
    },
    take: 5000,
  });

  const contactIndex = [];

  for (const c of contacts) {
    const contactFile = path.join(contactsDir, `${c.id}.json`);
    await writeJson(contactFile, {
      id: c.id,
      teamId: c.teamId,
      name: c.name,
      company: c.company ?? null,
      country: c.country ?? null,
      location: c.location ?? null,
      avatarUrl: c.avatarUrl ?? null,
      email: c.email ?? null,
      phone: c.phone ?? null,
      createdAt: c.createdAt,
      updatedAt: c.updatedAt,
    });

    const noteFile = path.join(notesDir, `${c.id}.md`);
    await fs.writeFile(
      noteFile,
      (c.note?.content?.trim() ? c.note.content.trim() : "") + "\n",
      "utf8",
    );

    const msgFile = path.join(messagesDir, `${c.id}.jsonl`);
    const msgLines = c.messages.map((m) =>
      jsonlLine({
        kind: m.kind,
        direction: m.direction,
        channel: m.channel,
        occurredAt: m.occurredAt,
        content: m.content,
        durationSec: m.durationSec ?? null,
        transcript: m.transcriptJson ?? null,
      }),
    );
    await fs.writeFile(msgFile, msgLines.join(""), "utf8");

    const evFile = path.join(eventsDir, `${c.id}.jsonl`);
    const evLines = c.events.map((e) =>
      jsonlLine({
        title: e.title,
        startsAt: e.startsAt,
        endsAt: e.endsAt,
        isArchived: e.isArchived,
        note: e.note ?? null,
      }),
    );
    await fs.writeFile(evFile, evLines.join(""), "utf8");

    const lastMessageAt = c.messages.at(-1)?.occurredAt ?? null;
    const nextEventAt = c.events.find((e) => new Date(e.startsAt).getTime() >= Date.now())?.startsAt ?? null;

    contactIndex.push({
      id: c.id,
      name: c.name,
      company: c.company ?? null,
      lastMessageAt,
      nextEventAt,
      updatedAt: c.updatedAt,
    });
  }

  await writeJson(path.join(indexDir, "contacts.json"), contactIndex);

  const meta: ExportMeta = { exportedAt: new Date().toISOString(), version: 1 };
  await writeJson(path.join(tmp, "meta.json"), meta);

  await ensureDir(path.dirname(root));
  await fs.rm(root, { recursive: true, force: true });
  await fs.rename(tmp, root);
}

export async function ensureDataset(input: { teamId: string; userId: string }) {
  const root = datasetRoot(input);
  try {
    const metaPath = path.join(root, "meta.json");
    await fs.access(metaPath);
    return;
  } catch {
    // fallthrough
  }
  await exportDatasetFromPrismaFor(input);
}
