import { existsSync, readFileSync, statSync } from "node:fs";
import { readFile, stat } from "node:fs/promises";
import { join } from "node:path";
import type {
  WikiDocMap,
  WikiGenerationJob,
  WikiManifest,
  WikiPage,
  WikiResearchArtifact,
  WikiReviewResult,
} from "../../types/wiki-model";
import { RepoIndex } from "../../repopilot/graph/index";
import {
  readWikiDocMap,
  readWikiDocMapAsync,
  readWikiJob,
  readWikiPageSpec,
  readWikiPageSpecAsync,
  readWikiResearchArtifact,
  readWikiResearchArtifactAsync,
  readWikiReview,
} from "../artifact-store";
import { normalizeManifest, normalizePage } from "./normalizer";
import { buildLegacyManifest, buildLegacyPage } from "./legacy";

export { getRepositorySourcePath } from "../../types/repository";

const manifestCache = new Map<string, { manifest: WikiManifest; mtimeMs: number }>();
const pageCache = new Map<string, { page: WikiPage; mtimeMs: number }>();
const MAX_PAGE_CACHE = 100;

export function clearPageCache(sourcePath?: string): void {
  if (sourcePath) {
    const prefix = `${sourcePath}:`;
    for (const key of pageCache.keys()) {
      if (key.startsWith(prefix)) pageCache.delete(key);
    }
  } else {
    pageCache.clear();
  }
}

export function clearManifestCache(sourcePath?: string): void {
  if (sourcePath) {
    manifestCache.delete(sourcePath);
    clearPageCache(sourcePath);
  } else {
    manifestCache.clear();
    clearPageCache();
  }
}

export function readWikiManifest(sourcePath: string): WikiManifest | null {
  const manifestPath = join(sourcePath, ".codewiki", "manifest.json");
  if (!existsSync(manifestPath)) {
    return buildLegacyManifest(sourcePath);
  }

  const mtimeMs = statSync(manifestPath).mtimeMs;
  const cached = manifestCache.get(sourcePath);
  if (cached && cached.mtimeMs === mtimeMs) {
    return cached.manifest;
  }

  const manifest = normalizeManifest(
    JSON.parse(readFileSync(manifestPath, "utf-8")) as WikiManifest,
  );
  manifestCache.set(sourcePath, { manifest, mtimeMs });
  return manifest;
}

export function readWikiPage(sourcePath: string, pageId: string, manifest?: WikiManifest | null): WikiPage | null {
  const pagePath = join(
    sourcePath,
    ".codewiki",
    "pages",
    `${encodeURIComponent(pageId)}.json`,
  );

  if (!existsSync(pagePath)) {
    return buildLegacyPage(sourcePath, pageId);
  }

  const mtimeMs = statSync(pagePath).mtimeMs;
  const cacheKey = `${sourcePath}:${pageId}`;
  const cached = pageCache.get(cacheKey);
  if (cached && cached.mtimeMs === mtimeMs) {
    return cached.page;
  }

  const page = JSON.parse(readFileSync(pagePath, "utf-8")) as WikiPage;
  const resolvedManifest = manifest !== undefined ? manifest : readWikiManifest(sourcePath);
  const normalized = normalizePage(page, resolvedManifest);

  if (pageCache.size >= MAX_PAGE_CACHE) {
    const firstKey = pageCache.keys().next().value;
    if (firstKey) pageCache.delete(firstKey);
  }
  pageCache.set(cacheKey, { page: normalized, mtimeMs });
  return normalized;
}

const indexCache = new Map<string, { index: RepoIndex; mtimeMs: number }>();

export function loadRepoIndex(sourcePath: string): RepoIndex | null {
  const indexPath = join(sourcePath, ".repopilot", "index.rpidx");
  if (!existsSync(indexPath)) {
    return null;
  }

  try {
    const mtimeMs = statSync(indexPath).mtimeMs;
    const cached = indexCache.get(sourcePath);
    if (cached && cached.mtimeMs === mtimeMs) {
      return cached.index;
    }

    const index = RepoIndex.load(indexPath);
    indexCache.set(sourcePath, { index, mtimeMs });
    return index;
  } catch {
    return null;
  }
}

export function clearIndexCache(sourcePath?: string): void {
  if (sourcePath) {
    indexCache.delete(sourcePath);
  } else {
    indexCache.clear();
  }
}

export function readWikiGenerationJob(sourcePath: string): WikiGenerationJob | null {
  return readWikiJob(sourcePath);
}

export function readWikiDocMapArtifact(sourcePath: string): WikiDocMap | null {
  return readWikiDocMap(sourcePath);
}

export function readWikiResearch(sourcePath: string, artifactId: string): WikiResearchArtifact | null {
  return readWikiResearchArtifact(sourcePath, artifactId);
}

export function readWikiReviewArtifact(sourcePath: string, pageId: string): WikiReviewResult | null {
  return readWikiReview(sourcePath, pageId);
}

export function readWikiPageSpecArtifact(sourcePath: string, pageId: string): WikiPage | null {
  return readWikiPageSpec(sourcePath, pageId);
}

/* ------------------------------------------------------------------ */
/*  Async variants — use in API routes to avoid blocking the event loop */
/* ------------------------------------------------------------------ */

export async function readWikiManifestAsync(sourcePath: string): Promise<WikiManifest | null> {
  const manifestPath = join(sourcePath, ".codewiki", "manifest.json");
  try {
    const { mtimeMs } = await stat(manifestPath);
    const cached = manifestCache.get(sourcePath);
    if (cached && cached.mtimeMs === mtimeMs) {
      return cached.manifest;
    }
    const raw = await readFile(manifestPath, "utf-8");
    const manifest = normalizeManifest(JSON.parse(raw) as WikiManifest);
    manifestCache.set(sourcePath, { manifest, mtimeMs });
    return manifest;
  } catch {
    return buildLegacyManifest(sourcePath);
  }
}

export async function readWikiPageAsync(
  sourcePath: string,
  pageId: string,
  manifest?: WikiManifest | null,
): Promise<WikiPage | null> {
  const pagePath = join(sourcePath, ".codewiki", "pages", `${encodeURIComponent(pageId)}.json`);
  let raw: string;
  try {
    raw = await readFile(pagePath, "utf-8");
  } catch {
    return buildLegacyPage(sourcePath, pageId);
  }
  const page = JSON.parse(raw) as WikiPage;
  const resolvedManifest = manifest !== undefined ? manifest : await readWikiManifestAsync(sourcePath);
  return normalizePage(page, resolvedManifest);
}

export async function readWikiDocMapArtifactAsync(sourcePath: string): Promise<WikiDocMap | null> {
  return readWikiDocMapAsync(sourcePath);
}

export async function readWikiResearchAsync(sourcePath: string, artifactId: string): Promise<WikiResearchArtifact | null> {
  return readWikiResearchArtifactAsync(sourcePath, artifactId);
}

export async function readWikiPageSpecArtifactAsync(sourcePath: string, pageId: string): Promise<WikiPage | null> {
  return readWikiPageSpecAsync(sourcePath, pageId);
}
