/**
 * Wiki freshness checker and background regeneration trigger.
 */

import { existsSync, rmSync } from "node:fs";
import { join } from "node:path";
import { loadRepoIndex } from "./store/readers";
import { WIKI_DIR_NAME } from "./constants";
import { generateWiki } from "./generator";
import { getWikiProgress, startWikiProgress, updateWikiProgress, finishWikiProgress, createWikiAbortController, cancelWikiGeneration, wikiCancelTimestamps } from "./progress";
import type { WikiCheckpointStore } from "./checkpoint-store";

const FRESHNESS_TTL_MS = 30_000;
const CANCEL_COOLDOWN_MS = 2_000;
const lastFreshnessCheck = new Map<string, number>();
/** Tracks in-flight generation promises so we can await shutdown on cancel. */
const runningGenerations = new Map<string, Promise<void>>();
// Cancel timestamps are managed by progress.ts via wikiCancelTimestamps

export function clearFreshnessCache(repositoryId?: string): void {
  if (repositoryId) {
    lastFreshnessCheck.delete(repositoryId);
  } else {
    lastFreshnessCheck.clear();
  }
}

/**
 * Launch wiki generation in the background with progress tracking.
 * Shared by ensureWikiFresh, forceRegenerateWiki, and the repository POST route.
 */
export function launchWikiGeneration(repositoryId: string, sourcePath: string, checkpoint?: WikiCheckpointStore): void {
  const genId = startWikiProgress(repositoryId);
  const controller = createWikiAbortController(repositoryId);
  const promise = generateWiki(sourcePath, (progress) => updateWikiProgress(repositoryId, progress, genId), checkpoint, controller.signal)
    .then((result) => {
      finishWikiProgress(
        repositoryId,
        result.pagesGenerated === 0 && result.errors.length > 0 ? "error" : "done",
        `Generated ${result.pagesGenerated} pages` + (result.errors.length > 0 ? ` with ${result.errors.length} warnings` : ""),
        genId,
      );
      console.log(
        `[repo.wiki] id=${repositoryId} pages=${result.pagesGenerated} errors=${result.errors.length}`,
      );
    })
    .catch((err) => {
      finishWikiProgress(
        repositoryId,
        "error",
        err instanceof Error ? err.message : String(err),
        genId,
      );
      console.error(
        `[repo.wiki.failed] ${err instanceof Error ? err.message : err}`,
      );
    })
    .finally(() => {
      runningGenerations.delete(repositoryId);
    });
  runningGenerations.set(repositoryId, promise);
}

export function ensureWikiFresh(repositoryId: string, sourcePath: string, checkpoint?: WikiCheckpointStore): {
  triggered: boolean;
  stale: boolean;
} {
  // Skip expensive staleness check if we checked recently
  const lastCheck = lastFreshnessCheck.get(repositoryId);
  if (lastCheck && Date.now() - lastCheck < FRESHNESS_TTL_MS) {
    return { triggered: false, stale: false };
  }

  const manifestPath = join(sourcePath, WIKI_DIR_NAME, "manifest.json");
  const hasManifest = existsSync(manifestPath);

  let stale = !hasManifest;
  try {
    const index = loadRepoIndex(sourcePath);
    if (index) {
      stale = stale || index.checkStaleness(sourcePath).isStale;
    } else {
      stale = true;
    }
  } catch {
    stale = true;
  }

  lastFreshnessCheck.set(repositoryId, Date.now());

  const currentProgress = getWikiProgress(repositoryId);
  if (!stale || currentProgress?.state === "running") {
    return { triggered: false, stale };
  }

  // Don't auto-trigger immediately after a cancellation — the user may be
  // about to resume manually, and a stale-check trigger would race with that.
  const cancelledAt = wikiCancelTimestamps.get(repositoryId);
  if (cancelledAt && Date.now() - cancelledAt < CANCEL_COOLDOWN_MS) {
    return { triggered: false, stale };
  }

  launchWikiGeneration(repositoryId, sourcePath, checkpoint);
  return { triggered: true, stale };
}

export { cancelWikiGeneration } from "./progress";

/**
 * Force-trigger wiki regeneration regardless of staleness.
 *
 * By default this **resumes** from cached checkpoints — only pages that
 * were not previously completed are regenerated.  Pass `fresh: true` to
 * wipe all checkpoints and start from scratch.
 */
export async function forceRegenerateWiki(
  repositoryId: string,
  sourcePath: string,
  checkpoint?: WikiCheckpointStore,
  options?: { fresh?: boolean },
): Promise<boolean> {
  const currentProgress = getWikiProgress(repositoryId);
  if (currentProgress?.state === "running") {
    cancelWikiGeneration(repositoryId);
    // Wait for the old generation to finish unwinding so we don't run two
    // generations concurrently. The promise always resolves (never rejects)
    // because launchWikiGeneration catches all errors internally.
    const oldGeneration = runningGenerations.get(repositoryId);
    if (oldGeneration) {
      await oldGeneration;
    }
  }

  clearFreshnessCache(repositoryId);
  wikiCancelTimestamps.delete(repositoryId);

  if (options?.fresh) {
    await checkpoint?.clear();
    // Remove the entire wiki directory so legacy markdown pages do not get
    // rehydrated into a stale manifest while a fresh generation is running.
    const wikiDir = join(sourcePath, WIKI_DIR_NAME);
    if (existsSync(wikiDir)) rmSync(wikiDir, { recursive: true, force: true });
  }

  launchWikiGeneration(repositoryId, sourcePath, checkpoint);
  return true;
}
