import fs from "node:fs";
import path from "node:path";
import fg from "fast-glob";
import { APP_PATHS } from "../../lib/paths";
import type { CheckError, SyncCheckResult } from "../../lib/sync-check/shared";

/**
 * Parse actor names from the `export type Actor = "a" | "b"` union in e2e/fixtures/user.ts.
 */
export function parseActorNamesFromFixture(content: string): string[] {
  const match = /export\s+type\s+Actor\s*=\s*([^;]+)/.exec(content);
  if (!match) return [];
  const names: string[] = [];
  const regex = /["']([^"']+)["']/g;
  let m: RegExpExecArray | null;
  while ((m = regex.exec(match[1])) !== null) {
    names.push(m[1]);
  }
  return names;
}

/**
 * Actor sync check: validates that each actor defined in e2e/fixtures/user.ts
 * has a corresponding doc file in docs/actor/, and vice versa.
 */
export async function runActorSyncCheck(appRoot: string, cwd: string): Promise<SyncCheckResult> {
  const errors: CheckError[] = [];
  const fixturePath = `${appRoot}/${APP_PATHS.tests.e2e}/fixtures/user.ts`;
  const fixtureFullPath = path.join(cwd, fixturePath);

  if (!fs.existsSync(fixtureFullPath)) {
    return { errors, filesChecked: 0 };
  }

  const content = fs.readFileSync(fixtureFullPath, "utf-8");
  const fixtureActors = new Set(parseActorNamesFromFixture(content));

  const actorDocPattern = `${appRoot}/${APP_PATHS.docs.actor}/*.md`;
  const actorDocs = await fg(actorDocPattern, { cwd });
  const docActors = new Set(actorDocs.map((p) => path.basename(p, ".md")));

  for (const actor of fixtureActors) {
    if (!docActors.has(actor)) {
      errors.push({
        type: "missing-actor-doc",
        category: "actor",
        sourcePath: fixturePath,
        expectedBasename: `${actor}.md`,
      });
    }
  }

  for (const actor of docActors) {
    if (!fixtureActors.has(actor)) {
      errors.push({
        type: "missing-actor-fixture",
        category: "actor",
        docPath: `${appRoot}/${APP_PATHS.docs.actor}/${actor}.md`,
        expectedBasename: actor,
      });
    }
  }

  return { errors, filesChecked: 1 + actorDocs.length };
}
