import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { readFile } from "node:fs/promises";
import { join } from "node:path";
import type { WikiDocMap, WikiGenerationJob, WikiPage, WikiResearchArtifact, WikiReviewResult } from "../types/wiki-model";
import { WIKI_DIR_NAME } from "./constants";

function ensureDir(path: string) {
  mkdirSync(path, { recursive: true });
}

export function getWikiRoot(sourcePath: string): string {
  return join(sourcePath, WIKI_DIR_NAME);
}

export function getWikiArtifactDirs(sourcePath: string) {
  const wikiRoot = getWikiRoot(sourcePath);
  return {
    wikiRoot,
    pagesDir: join(wikiRoot, "pages"),
    artifactsDir: join(wikiRoot, "artifacts"),
    researchDir: join(wikiRoot, "artifacts", "research"),
    pageSpecDir: join(wikiRoot, "artifacts", "pagespecs"),
    reviewsDir: join(wikiRoot, "artifacts", "reviews"),
  };
}

export function ensureWikiArtifactDirs(sourcePath: string) {
  const dirs = getWikiArtifactDirs(sourcePath);
  ensureDir(dirs.wikiRoot);
  ensureDir(dirs.pagesDir);
  ensureDir(dirs.artifactsDir);
  ensureDir(dirs.researchDir);
  ensureDir(dirs.pageSpecDir);
  ensureDir(dirs.reviewsDir);
  return dirs;
}

function writeJson(path: string, data: unknown) {
  writeFileSync(path, JSON.stringify(data, null, 2));
}

function readJson<T>(path: string): T | null {
  if (!existsSync(path)) {
    return null;
  }
  return JSON.parse(readFileSync(path, "utf-8")) as T;
}

export function writeWikiJob(sourcePath: string, job: WikiGenerationJob) {
  const { artifactsDir } = ensureWikiArtifactDirs(sourcePath);
  writeJson(join(artifactsDir, "job.json"), job);
}

export function readWikiJob(sourcePath: string): WikiGenerationJob | null {
  return readJson<WikiGenerationJob>(join(getWikiArtifactDirs(sourcePath).artifactsDir, "job.json"));
}

export function writeWikiDocMap(sourcePath: string, docMap: WikiDocMap) {
  const { artifactsDir } = ensureWikiArtifactDirs(sourcePath);
  writeJson(join(artifactsDir, "doc-map.json"), docMap);
}

export function readWikiDocMap(sourcePath: string): WikiDocMap | null {
  return readJson<WikiDocMap>(join(getWikiArtifactDirs(sourcePath).artifactsDir, "doc-map.json"));
}

export function writeWikiResearchArtifact(sourcePath: string, artifact: WikiResearchArtifact) {
  const { researchDir } = ensureWikiArtifactDirs(sourcePath);
  writeJson(join(researchDir, `${encodeURIComponent(artifact.id)}.json`), artifact);
}

export function readWikiResearchArtifact(sourcePath: string, artifactId: string): WikiResearchArtifact | null {
  return readJson<WikiResearchArtifact>(
    join(getWikiArtifactDirs(sourcePath).researchDir, `${encodeURIComponent(artifactId)}.json`),
  );
}

export function writeWikiPageSpec(sourcePath: string, page: WikiPage) {
  const { pageSpecDir } = ensureWikiArtifactDirs(sourcePath);
  writeJson(join(pageSpecDir, `${encodeURIComponent(page.id)}.json`), page);
}

export function readWikiPageSpec(sourcePath: string, pageId: string): WikiPage | null {
  return readJson<WikiPage>(
    join(getWikiArtifactDirs(sourcePath).pageSpecDir, `${encodeURIComponent(pageId)}.json`),
  );
}

export function writeWikiReview(sourcePath: string, review: WikiReviewResult) {
  const { reviewsDir } = ensureWikiArtifactDirs(sourcePath);
  writeJson(join(reviewsDir, `${encodeURIComponent(review.pageId)}.json`), review);
}

export function readWikiReview(sourcePath: string, pageId: string): WikiReviewResult | null {
  return readJson<WikiReviewResult>(
    join(getWikiArtifactDirs(sourcePath).reviewsDir, `${encodeURIComponent(pageId)}.json`),
  );
}

/* ------------------------------------------------------------------ */
/*  Async variants                                                     */
/* ------------------------------------------------------------------ */

async function readJsonAsync<T>(path: string): Promise<T | null> {
  try {
    const raw = await readFile(path, "utf-8");
    return JSON.parse(raw) as T;
  } catch {
    return null;
  }
}

export async function readWikiDocMapAsync(sourcePath: string): Promise<WikiDocMap | null> {
  return readJsonAsync<WikiDocMap>(join(getWikiArtifactDirs(sourcePath).artifactsDir, "doc-map.json"));
}

export async function readWikiResearchArtifactAsync(sourcePath: string, artifactId: string): Promise<WikiResearchArtifact | null> {
  return readJsonAsync<WikiResearchArtifact>(
    join(getWikiArtifactDirs(sourcePath).researchDir, `${encodeURIComponent(artifactId)}.json`),
  );
}

export async function readWikiPageSpecAsync(sourcePath: string, pageId: string): Promise<WikiPage | null> {
  return readJsonAsync<WikiPage>(
    join(getWikiArtifactDirs(sourcePath).pageSpecDir, `${encodeURIComponent(pageId)}.json`),
  );
}

