import { readFile, writeFile, mkdir } from "node:fs/promises";
import { existsSync } from "node:fs";
import path from "node:path";

export interface PlaceholderEntry {
  fileId: string;
  filename: string;
  description: string;
  replacedByFileId?: string;
}

const MANIFEST_FILENAME = "PLACEHOLDERS.md";

function serialize(active: PlaceholderEntry[], replaced: PlaceholderEntry[]): string {
  const activeRows =
    active.length === 0
      ? "_None_"
      : active.map((e) => `| ${e.filename} | ${e.description} | ${e.fileId} |`).join("\n");

  const replacedRows =
    replaced.length === 0
      ? "_None_"
      : replaced
          .map((e) => `| ${e.filename} | ${e.description} | ${e.replacedByFileId ?? ""} |`)
          .join("\n");

  return `# Placeholder Assets

Auto-maintained by Nexus. Do not edit manually.

## Active Placeholders

| File | Description | File ID |
|------|-------------|---------|
${activeRows}

## Replaced

| Original | Description | Replaced By |
|----------|-------------|-------------|
${replacedRows}
`;
}

function parse(content: string): { active: PlaceholderEntry[]; replaced: PlaceholderEntry[] } {
  const active: PlaceholderEntry[] = [];
  const replaced: PlaceholderEntry[] = [];

  let section: "active" | "replaced" | null = null;
  const rowRe = /^\|\s*(.+?)\s*\|\s*(.+?)\s*\|\s*(.+?)\s*\|$/;

  for (const line of content.split("\n")) {
    if (line.includes("## Active Placeholders")) {
      section = "active";
      continue;
    }
    if (line.includes("## Replaced")) {
      section = "replaced";
      continue;
    }

    if (!section) continue;

    const match = rowRe.exec(line);
    if (!match) continue;

    const [, col1, col2, col3] = match;
    // Skip header row and separator row
    if (
      col1 === "File" ||
      col1 === "Original" ||
      col1 === "---" ||
      col1?.startsWith("-")
    ) {
      continue;
    }
    // Skip "_None_" placeholder rows
    if (col1?.trim() === "_None_") continue;

    if (section === "active") {
      active.push({
        filename: col1 ?? "",
        description: col2 ?? "",
        fileId: col3 ?? "",
      });
    } else {
      replaced.push({
        filename: col1 ?? "",
        description: col2 ?? "",
        replacedByFileId: col3 ?? undefined,
        fileId: col3 ?? "",
      });
    }
  }

  return { active, replaced };
}

async function readManifest(projectDir: string): Promise<{ active: PlaceholderEntry[]; replaced: PlaceholderEntry[] }> {
  const manifestPath = path.join(projectDir, MANIFEST_FILENAME);
  if (!existsSync(manifestPath)) {
    return { active: [], replaced: [] };
  }
  const content = await readFile(manifestPath, "utf-8");
  return parse(content);
}

async function writeManifest(
  projectDir: string,
  active: PlaceholderEntry[],
  replaced: PlaceholderEntry[],
): Promise<void> {
  await mkdir(projectDir, { recursive: true });
  const manifestPath = path.join(projectDir, MANIFEST_FILENAME);
  await writeFile(manifestPath, serialize(active, replaced), "utf-8");
}

export function placeholderService() {
  return {
    async addEntry(
      projectDir: string,
      entry: { fileId: string; filename: string; description: string },
    ): Promise<void> {
      const { active, replaced } = await readManifest(projectDir);
      // Avoid duplicates
      if (!active.some((e) => e.fileId === entry.fileId)) {
        active.push({
          fileId: entry.fileId,
          filename: entry.filename,
          description: entry.description,
        });
      }
      await writeManifest(projectDir, active, replaced);
    },

    async replaceEntry(
      projectDir: string,
      oldFileId: string,
      newFileId: string,
    ): Promise<void> {
      const { active, replaced } = await readManifest(projectDir);
      const idx = active.findIndex((e) => e.fileId === oldFileId);
      if (idx === -1) return;

      const [entry] = active.splice(idx, 1);
      replaced.push({
        fileId: entry!.fileId,
        filename: entry!.filename,
        description: entry!.description,
        replacedByFileId: newFileId,
      });
      await writeManifest(projectDir, active, replaced);
    },

    async listEntries(
      projectDir: string,
    ): Promise<{ active: PlaceholderEntry[]; replaced: PlaceholderEntry[] }> {
      return readManifest(projectDir);
    },
  };
}
