/**
 * Evidence pack builders that assemble structured data for the
 * AI agent to use when generating wiki pages.
 */

import { KnowledgeGraph } from "../../repopilot/graph/knowledge-graph";
import { RepoIndex } from "../../repopilot/graph/index";
import type { ModuleNode } from "../../repopilot/module-tree";
import type { ModuleEvidence, RootEvidence } from "../types";
import {
  CAP_CLASSES,
  CAP_FILES,
  CAP_METHODS,
} from "../constants";
import {
  collectModuleFiles,
  countModules,
  findModule,
  toRelativePath,
} from "../utils";
import { buildCodeLink } from "../code-links";
import {
  getClassEntries,
  getMethodEntries,
  getModuleDependencyNames,
} from "./graph-extractor";
import {
  buildCrossModuleRelationships,
  buildImportPatternEvidence,
  buildTechnologyStack,
} from "./pattern-detector";
import {
  buildDependencyDiagram,
  buildModuleStructureDiagram,
  buildRootDependencyDiagram,
} from "./diagram-builder";

export function buildModuleEvidence(
  module: ModuleNode,
  tree: ModuleNode,
  graph: KnowledgeGraph,
  sourcePath: string,
): ModuleEvidence {
  const files = collectModuleFiles(module);
  const moduleFiles = new Set(files);
  const classes = getClassEntries(graph, moduleFiles);
  const methods = getMethodEntries(graph, classes);
  const dependencies = getModuleDependencyNames(module, graph);

  const fileInventory =
    files.length > 0
      ? files
          .slice(0, CAP_FILES)
          .map((fp) => {
            const rel = toRelativePath(sourcePath, fp);
            return `- ${buildCodeLink(rel, sourcePath, fp)}`;
          })
          .join("\n")
      : "- No source files were indexed for this module.";

  const classReferences =
    classes.length > 0
      ? classes
          .slice(0, CAP_CLASSES)
          .map((cls) => {
            const link = buildCodeLink(
              cls.name,
              sourcePath,
              cls.filePath,
              cls.lineStart,
              cls.lineEnd,
              cls.name,
            );
            const traits = [
              cls.type,
              cls.extendsName ? `extends ${cls.extendsName}` : null,
              cls.implementsNames.length > 0
                ? `implements ${cls.implementsNames.join(", ")}`
                : null,
            ].filter(Boolean);
            return `- ${link} — ${traits.join(" | ")}`;
          })
          .join("\n")
      : "- No class-level symbols were captured for this module.";

  const methodReferences =
    methods.length > 0
      ? [
          `*(${methods.length} methods total${methods.length > CAP_METHODS ? `, showing first ${CAP_METHODS}` : ""})*`,
          ...methods
            .slice(0, CAP_METHODS)
            .map((m) => {
              const sig = `${m.className}.${m.name}(${m.parameters.join(", ")})`;
              const link = buildCodeLink(
                sig,
                sourcePath,
                m.filePath,
                m.lineStart,
                m.lineEnd,
                sig,
              );
              const traits = [
                m.type,
                m.returnType ? `returns ${m.returnType}` : null,
                m.lineStart
                  ? `lines ${m.lineStart}–${m.lineEnd ?? "?"}`
                  : null,
                m.javadoc ? `doc: ${m.javadoc}` : null,
              ].filter(Boolean);
              return `- ${link}${traits.length > 0 ? ` — ${traits.join(" | ")}` : ""}`;
            }),
        ].join("\n")
      : "- No method-level symbols were captured for this module.";

  const childLinks =
    module.children.length > 0
      ? module.children
          .filter((child) => child.docPath)
          .map((child) => `- [${child.name}](${child.docPath})`)
          .join("\n")
      : "- No child wiki pages.";

  const dependencySummary =
    dependencies.length > 0
      ? dependencies.map((d) => `- ${d}`).join("\n")
      : "- No package dependencies were resolved for this module.";

  const facts = [
    `- Module path: \`${module.path || "(root)"}\``,
    `- Module level: \`${module.level}\``,
    `- Direct child modules: ${module.children.length}`,
    `- Indexed source files: ${files.length}`,
    `- Indexed classes/interfaces/enums: ${classes.length}`,
    `- Indexed methods/constructors: ${methods.length}`,
    `- Parent module: ${
      module.parent
        ? findModule(tree, module.parent)?.path || module.parent
        : "(root)"
    }`,
  ].join("\n");

  const referenceCatalog = [
    "### Code links",
    classReferences,
    "",
    "### Method links",
    methodReferences,
  ].join("\n");

  const crossModuleRelationships = buildCrossModuleRelationships(
    module,
    graph,
    tree,
    sourcePath,
  );

  const importPatterns = buildImportPatternEvidence(graph, moduleFiles);

  return {
    facts,
    childLinks,
    fileInventory,
    classReferences,
    methodReferences,
    dependencySummary,
    referenceCatalog,
    crossModuleRelationships,
    importPatterns,
    structureDiagram: buildModuleStructureDiagram(module, classes),
    dependencyDiagram: buildDependencyDiagram(
      module.path || module.name,
      dependencies,
    ),
    classCount: classes.length,
    methodCount: methods.length,
    fileCount: files.length,
  };
}

export function buildRootEvidence(
  tree: ModuleNode,
  index: RepoIndex,
  graph: KnowledgeGraph,
  sourcePath: string,
  /** Optional pre-computed evidence cache to avoid redundant buildModuleEvidence calls. */
  evidenceCache?: Map<string, ModuleEvidence>,
): RootEvidence {
  const topLevelLinks =
    tree.children.length > 0
      ? tree.children
          .filter((child) => child.docPath)
          .map((child) => `- [${child.name}](${child.docPath})`)
          .join("\n")
      : "- No top-level packages were indexed.";

  const topLevelSummary =
    tree.children.length > 0
      ? tree.children
          .map((child) => {
            const ev = evidenceCache?.get(child.id) ?? buildModuleEvidence(child, tree, graph, sourcePath);
            return `- **${child.name}**: ${ev.fileCount} files, ${ev.classCount} classes, ${child.children.length} sub-modules`;
          })
          .join("\n")
      : "- No package summaries available.";

  const repoStats = [
    `- Source path: \`${sourcePath}\``,
    `- Indexed files: ${String(index.metadata.file_count ?? 0)}`,
    `- Graph nodes: ${String(index.metadata.node_count ?? 0)}`,
    `- Graph edges: ${String(index.metadata.edge_count ?? 0)}`,
    `- Scan time (s): ${String(index.metadata.scan_time_seconds ?? 0)}`,
    `- Documentation modules: ${countModules(tree)}`,
  ].join("\n");

  const allClasses = getClassEntries(
    graph,
    new Set(tree.children.flatMap((child) => collectModuleFiles(child))),
  );

  const referenceCatalog =
    allClasses.length > 0
      ? allClasses
          .slice(0, 30)
          .map((cls) =>
            `- ${buildCodeLink(cls.name, sourcePath, cls.filePath, cls.lineStart, cls.lineEnd, cls.name)}`,
          )
          .join("\n")
      : "- No repository-wide class references available.";

  const technologyStack = buildTechnologyStack(graph);

  return {
    repoStats,
    topLevelLinks,
    topLevelSummary,
    referenceCatalog,
    technologyStack,
    dependencyDiagram: buildRootDependencyDiagram(tree, graph),
  };
}
