/**
 * Shared utility functions for the wiki generation pipeline.
 */

import { relative } from "node:path";
import type { ModuleNode } from "../repopilot/module-tree";
import type { WikiCodeReference } from "../types/wiki-model";
import { normalizeMermaidSource } from "./mermaid-validation";
import { MANAGED_APPENDIX_START, MANAGED_APPENDIX_END } from "./constants";

/* ------------------------------------------------------------------ */
/*  Module-tree helpers                                                */
/* ------------------------------------------------------------------ */

export function countModules(node: ModuleNode): number {
  let count = node.level !== "root" ? 1 : 0;
  for (const child of node.children) {
    count += countModules(child);
  }
  return count;
}

export function findModule(
  node: ModuleNode,
  id: string,
): ModuleNode | undefined {
  if (node.id === id) return node;
  for (const child of node.children) {
    const found = findModule(child, id);
    if (found) return found;
  }
  return undefined;
}

export function collectModuleFiles(module: ModuleNode): string[] {
  const files = [...module.files];
  for (const child of module.children) {
    files.push(...collectModuleFiles(child));
  }
  return uniqueStrings(files);
}

export function resolveTopLevelName(
  packageName: string,
  topLevelNames: string[],
): string | null {
  for (const topLevelName of topLevelNames) {
    const pattern = new RegExp(
      `(^|\\.)${escapeRegExp(topLevelName)}(\\.|$)`,
    );
    if (pattern.test(packageName)) {
      return topLevelName;
    }
  }
  return null;
}

/* ------------------------------------------------------------------ */
/*  String helpers                                                     */
/* ------------------------------------------------------------------ */

export function uniqueStrings(values: string[]): string[] {
  return [...new Set(values)];
}

export function toRelativePath(sourcePath: string, filePath: string): string {
  return relative(sourcePath, filePath).replace(/\\/g, "/");
}

export function getNumber(
  attrs: Record<string, unknown>,
  key: string,
): number | undefined {
  const value = attrs[key];
  return typeof value === "number" ? value : undefined;
}

export function getString(
  attrs: Record<string, unknown>,
  key: string,
): string | undefined {
  const value = attrs[key];
  return typeof value === "string" && value.length > 0 ? value : undefined;
}

export function escapeRegExp(value: string): string {
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

export function humanTitle(modulePath: string): string {
  return modulePath
    .split(/[/.]/)
    .filter(Boolean)
    .map((seg) =>
      seg
        .split(/[_\-]+/)
        .filter(Boolean)
        .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
        .join(" "),
    )
    .join(" / ");
}

/* ------------------------------------------------------------------ */
/*  Markdown / Mermaid helpers                                         */
/* ------------------------------------------------------------------ */

export function stripManagedAppendix(markdown: string): string {
  const start = markdown.indexOf(MANAGED_APPENDIX_START);
  const end = markdown.indexOf(MANAGED_APPENDIX_END);
  if (start === -1 || end === -1 || end < start) {
    return markdown.trim();
  }
  return markdown.slice(0, start).trim();
}

export function mermaidText(value: string): string {
  return value
    .replace(/&/g, "&amp;")
    .replace(/"/g, "&quot;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/#/g, "&num;");
}

export function mermaidId(value: string): string {
  return value.replace(/[^a-zA-Z0-9_]/g, "_");
}

export function sanitizeMermaidSource(chart: string): string {
  return normalizeMermaidSource(chart);
}

/* ------------------------------------------------------------------ */
/*  Code link builders                                                 */
/* ------------------------------------------------------------------ */

export function buildCodeHref(
  sourcePath: string,
  filePath: string,
  lineStart?: number,
  lineEnd?: number,
  symbol?: string,
): string {
  const params = new URLSearchParams({
    path: toRelativePath(sourcePath, filePath),
  });
  if (lineStart) params.set("line", String(lineStart));
  if (lineEnd) params.set("end", String(lineEnd));
  if (symbol) params.set("symbol", symbol);
  return `/__code__?${params.toString()}`;
}

export function buildCodeLink(
  label: string,
  sourcePath: string,
  filePath: string,
  lineStart?: number,
  lineEnd?: number,
  symbol?: string,
): string {
  return `[${label}](${buildCodeHref(sourcePath, filePath, lineStart, lineEnd, symbol)})`;
}

export function buildCodeReference(
  label: string,
  sourcePath: string,
  filePath: string,
  line?: number,
  endLine?: number,
  symbol?: string,
  nodeId?: string,
  relation?: string,
): WikiCodeReference {
  return {
    label,
    path: toRelativePath(sourcePath, filePath),
    line,
    endLine,
    symbol,
    nodeId,
    relation,
  };
}
