import { resolve } from "node:path";

import type { ResolvedCodemapConfig } from "./config";
import { isRuntimeSwapActive } from "./runtime-swap";

let _config: ResolvedCodemapConfig | null = null;

/**
 * Store resolved config for the current process (`getProjectRoot`, `openDb`, etc.).
 * Must run before indexing or `openDb()`; typically via `createCodemap` or the CLI.
 *
 * Throws when switching to a different `root` mid-process (audit worktree reindex
 * is the only exempt path — bracketed via `runtime-swap.ts`).
 */
export function initCodemap(config: ResolvedCodemapConfig): void {
  if (_config !== null && !isRuntimeSwapActive()) {
    const current = resolve(_config.root);
    const next = resolve(config.root);
    if (current !== next) {
      throw new Error(
        `Codemap: cannot switch project root from ${current} to ${next} in the same process (use a fresh process or audit --base worktree reindex)`,
      );
    }
  }
  _config = config;
}

/** Maintainer test helper — clears process-global config between test cases. */
export function resetCodemapForTest(): void {
  _config = null;
}

export function getCodemapConfig(): ResolvedCodemapConfig {
  if (!_config) {
    throw new Error(
      "Codemap: not initialized — call initCodemap() after resolving config",
    );
  }
  return _config;
}

export function getProjectRoot(): string {
  return getCodemapConfig().root;
}

export function getStateDir(): string {
  return getCodemapConfig().stateDir;
}

export function getDatabasePath(): string {
  return getCodemapConfig().databasePath;
}

export function getIncludePatterns(): readonly string[] {
  return getCodemapConfig().include;
}

export function getExcludeDirNames(): ReadonlySet<string> {
  return getCodemapConfig().excludeDirNames;
}

export function getTsconfigPath(): string | null {
  return getCodemapConfig().tsconfigPath;
}

export function getFts5Enabled(): boolean {
  return getCodemapConfig().fts5;
}

export function getRecipeRecencyEnabled(): boolean {
  return getCodemapConfig().recipeRecency;
}

export function getBoundaryRules(): ResolvedCodemapConfig["boundaries"] {
  return getCodemapConfig().boundaries;
}

export function getHeuristicCallsEnabled(): boolean {
  return getCodemapConfig().synthesis.heuristicCalls;
}

export function getApplyAutoApplyRecipes(): readonly string[] | undefined {
  return getCodemapConfig().applyAutoApplyRecipes;
}

export function getChurnHalfLifeDays(): number {
  return getCodemapConfig().churn.halfLifeDays;
}

export function getChurnSince(): string | null {
  return getCodemapConfig().churn.since;
}

export function getChurnFilePath(): string | null {
  return getCodemapConfig().churn.file;
}

/** True if any path segment matches an excluded directory name (e.g. `node_modules`). */
export function isPathExcluded(relPath: string): boolean {
  const set = getExcludeDirNames();
  let start = 0;
  for (let i = 0; i <= relPath.length; i++) {
    const ch = i < relPath.length ? relPath.charCodeAt(i) : 0;
    if (ch === 47 || ch === 92 || i === relPath.length) {
      if (i > start && set.has(relPath.slice(start, i))) return true;
      start = i + 1;
    }
  }
  return false;
}
