/**
 * Language Provider interface — the complete capability contract for a supported language.
 *
 * Each language implements this interface in a single file under `languages/`.
 * The pipeline accesses all per-language behavior through this interface.
 *
 * Design pattern: Strategy pattern with compile-time exhaustiveness.
 * The providers table in `languages/index.ts` uses `satisfies Record<SupportedLanguages, LanguageProvider>`
 * so adding a language to the enum without creating a provider is a compiler error.
 */

import type { SupportedLanguages, MroStrategy } from 'gitnexus-shared';
import type { LanguageTypeConfig } from './type-extractors/types.js';
import type { CallRouter } from './call-routing.js';
import type { ClassExtractor } from './class-types.js';
import type { ExportChecker } from './export-detection.js';
import type { FieldExtractor } from './field-extractor.js';
import type { MethodExtractor } from './method-types.js';
import type { ImportResolverFn } from './import-resolvers/types.js';
import type { NamedBindingExtractorFn } from './named-bindings/types.js';
import type { SyntaxNode } from './utils/ast-helpers.js';
import type { NodeLabel } from 'gitnexus-shared';

// ── Shared type aliases ────────────────────────────────────────────────────
/** Tree-sitter query captures: capture name → AST node (or undefined if not captured). */
export type CaptureMap = Record<string, SyntaxNode | undefined>;

// ── Strategy tag types ─────────────────────────────────────────────────────
// NOTE: `MroStrategy` is defined in `gitnexus-shared` and re-exported above
// so `core/ingestion/model/resolve.ts` can consume it without importing from
// this file (which would pull in the full language-registry dependency graph).

/**
 * How a language handles imports — determines wildcard synthesis behavior.
 *
 * Import resolution is a graph-traversal policy with multiple distinct strategies,
 * analogous to MRO for method resolution. Each tag picks a strategy:
 *
 * | Tag                   | Mechanism                                      | Traversal           | Languages                                  |
 * |-----------------------|------------------------------------------------|---------------------|--------------------------------------------|
 * | `named`               | Per-symbol imports                             | None (use-site)     | JS/TS, Java, C#, Rust, PHP, Kotlin, Vue    |
 * | `wildcard-transitive` | Textual paste, symbols chain through files     | BFS closure         | C, C++ (future: Obj-C, Fortran, Nim)       |
 * | `wildcard-leaf`       | Whole public API, single hop                   | None (direct only)  | Go, Ruby, Swift, Dart                      |
 * | `namespace`           | Qualified handle; symbols resolved at call site| None at import      | Python                                     |
 * | `explicit-reexport`   | Opt-in per-symbol re-export (SCAFFOLD)         | Topological DAG     | (future: TS `export *`, Rust `pub use`)    |
 *
 * The `explicit-reexport` tag is a compile-time scaffold; no provider claims it yet.
 * It falls through to `wildcard-leaf` behavior in synthesis so today's TS/Rust
 * handling is unchanged. A future PR will implement the DAG walk for `export *`.
 */
export type ImportSemantics =
  | 'named'
  | 'wildcard-transitive'
  | 'wildcard-leaf'
  | 'namespace'
  | 'explicit-reexport';

/**
 * Everything a language needs to provide.
 * Required fields must be explicitly set; optional fields have defaults
 * applied by defineLanguage().
 */
interface LanguageProviderConfig {
  // ── Identity ──────────────────────────────────────────────────────
  readonly id: SupportedLanguages;
  /** File extensions that map to this language (e.g., ['.ts', '.tsx']) */
  readonly extensions: readonly string[];

  // ── Parser ────────────────────────────────────────────────────────
  /** Parse strategy: 'tree-sitter' (default) uses AST parsing via tree-sitter.
   *  'standalone' means the language has its own regex-based processor and
   *  should be skipped by the tree-sitter pipeline (e.g., COBOL, Markdown). */
  readonly parseStrategy?: 'tree-sitter' | 'standalone';
  /** Tree-sitter query strings for definitions, imports, calls, heritage.
   *  Required for tree-sitter languages; empty string for standalone processors. */
  readonly treeSitterQueries: string;

  // ── Core (required) ───────────────────────────────────────────────
  /** Type extraction: declarations, initializers, for-loop bindings */
  readonly typeConfig: LanguageTypeConfig;
  /** Export detection: is this AST node a public/exported symbol? */
  readonly exportChecker: ExportChecker;
  /** Import resolution: resolves raw import path to file system path */
  readonly importResolver: ImportResolverFn;

  // ── Calls & Imports (optional) ────────────────────────────────────
  /** Call routing for languages that express imports/heritage as calls (e.g., Ruby).
   *  Default: no routing (all calls are normal call expressions). */
  readonly callRouter?: CallRouter;
  /** Named binding extraction from import statements.
   *  Default: undefined (language uses wildcard/whole-module imports). */
  readonly namedBindingExtractor?: NamedBindingExtractorFn;
  /** How this language handles imports. See `ImportSemantics` for the full taxonomy.
   *  - 'named': per-symbol imports (JS/TS, Java, C#, Rust, PHP, Kotlin)
   *  - 'wildcard-transitive': textual-include closure; imports chain through files (C, C++)
   *  - 'wildcard-leaf': whole-module single-hop imports; no transitive chaining (Go, Ruby, Swift, Dart)
   *  - 'namespace': qualified namespace imports, needs moduleAliasMap (Python)
   *  - 'explicit-reexport': opt-in per-symbol re-export (scaffold; no provider uses yet)
   *  Default: 'named'. */
  readonly importSemantics?: ImportSemantics;
  /** Language-specific transformation of raw import path text before resolution.
   *  Called after sanitization. E.g., Kotlin appends wildcard suffixes.
   *  Default: undefined (no preprocessing). */
  readonly importPathPreprocessor?: (cleaned: string, importNode: SyntaxNode) => string;
  /** Wire implicit inter-file imports for languages where all files in a module
   *  see each other (e.g., Swift targets, C header inclusion units).
   *  Called with only THIS language's files (pre-grouped by the processor).
   *  Default: undefined (no implicit imports). */
  readonly implicitImportWirer?: (
    languageFiles: string[],
    importMap: ReadonlyMap<string, ReadonlySet<string>>,
    addImportEdge: (src: string, target: string) => void,
    projectConfig: unknown,
  ) => void;

  // ── Enclosing owner resolution ─────────────────────────────────
  /** Resolve a container node during enclosing-owner tree walks.
   *  Called when a CLASS_CONTAINER_TYPES node is found while walking up.
   *  - Return a different SyntaxNode to remap the container (e.g., Ruby
   *    singleton_class → enclosing class/module).
   *  - Return null to skip this container and keep walking up.
   *  - Omit (undefined) to use the container node as-is (default).
   *  Default: undefined (no remapping). */
  readonly resolveEnclosingOwner?: (node: SyntaxNode) => SyntaxNode | null;

  // ── Enclosing function resolution ───────────────────────────────
  /** Resolve the enclosing function name + label from an AST ancestor node
   *  that is NOT a standard FUNCTION_NODE_TYPE.  For languages where the
   *  function body is a sibling of the signature (e.g. Dart: function_body ↔
   *  function_signature are siblings under program/class_body), the default
   *  parent walk cannot find the enclosing function.  This hook lets the
   *  language provider inspect each ancestor and return the resolved result.
   *  Return null to continue the default walk.
   *  Default: undefined (standard parent walk only). */
  readonly enclosingFunctionFinder?: (
    ancestorNode: SyntaxNode,
  ) => { funcName: string; label: NodeLabel } | null;

  // ── Labels ────────────────────────────────────────────────────────
  /** Override the default node label for definition.function captures.
   *  Return null to skip (C/C++ duplicate), a different label to reclassify
   *  (e.g., 'Method' for Kotlin), or defaultLabel to keep as-is.
   *  Default: undefined (standard label assignment). */
  readonly labelOverride?: (functionNode: SyntaxNode, defaultLabel: NodeLabel) => NodeLabel | null;

  // ── Heritage & MRO ────────────────────────────────────────────────
  /** Default edge type when parent symbol is ambiguous (interface vs class).
   *  Default: 'EXTENDS'. */
  readonly heritageDefaultEdge?: 'EXTENDS' | 'IMPLEMENTS';
  /** Regex to detect interface names by convention (e.g., /^I[A-Z]/ for C#/Java).
   *  When matched, IMPLEMENTS edge is used instead of heritageDefaultEdge. */
  readonly interfaceNamePattern?: RegExp;
  /** MRO strategy for multiple inheritance resolution.
   *  Default: 'first-wins'. */
  readonly mroStrategy?: MroStrategy;

  // ── Language-specific extraction hooks ────────────────────────────
  /** Field extractor for extracting field/property definitions from class/struct
   *  declarations. Produces FieldInfo[] with name, type, visibility, static,
   *  readonly metadata. Default: undefined (no field extraction). */
  readonly fieldExtractor?: FieldExtractor;
  /** Method extractor for extracting method/function definitions from class/struct/interface
   *  declarations. Produces MethodInfo[] with name, parameters, visibility, isAbstract,
   *  isFinal, annotations metadata. Default: undefined (no method extraction). */
  readonly methodExtractor?: MethodExtractor;
  /** Class/type extractor for deriving canonical qualified names for class-like symbols.
   *  Uses the same provider-driven strategy pattern as method/field extraction so
   *  namespace/package/module rules stay language-specific. */
  readonly classExtractor?: ClassExtractor;
  /** Extract a semantic description for a definition node (e.g., PHP Eloquent
   *  property arrays, relation method descriptions).
   *  Default: undefined (no description extraction). */
  readonly descriptionExtractor?: (
    nodeLabel: NodeLabel,
    nodeName: string,
    captureMap: CaptureMap,
  ) => string | undefined;
  /** Detect if a file contains framework route definitions (e.g., Laravel routes.php).
   *  When true, the worker extracts routes via the language's route extraction logic.
   *  Default: undefined (no route files). */
  readonly isRouteFile?: (filePath: string) => boolean;

  // ── Noise filtering ────────────────────────────────────────────────
  /** Built-in/stdlib names that should be filtered from the call graph for this language.
   *  Default: undefined (no language-specific filtering). */
  readonly builtInNames?: ReadonlySet<string>;
}

/** Runtime type — same as LanguageProviderConfig but with defaults guaranteed present. */
export interface LanguageProvider extends Omit<
  LanguageProviderConfig,
  'importSemantics' | 'heritageDefaultEdge' | 'mroStrategy'
> {
  readonly importSemantics: ImportSemantics;
  readonly heritageDefaultEdge: 'EXTENDS' | 'IMPLEMENTS';
  readonly mroStrategy: MroStrategy;
  /** Check if a name is a built-in/stdlib function that should be filtered from the call graph. */
  readonly isBuiltInName: (name: string) => boolean;
}

const DEFAULTS: Pick<LanguageProvider, 'importSemantics' | 'heritageDefaultEdge' | 'mroStrategy'> =
  {
    importSemantics: 'named',
    heritageDefaultEdge: 'EXTENDS',
    mroStrategy: 'first-wins',
  };

/** Define a language provider — required fields must be supplied, optional fields get sensible defaults. */
export function defineLanguage(config: LanguageProviderConfig): LanguageProvider {
  const builtIns = config.builtInNames;
  return {
    ...DEFAULTS,
    ...config,
    isBuiltInName: builtIns ? (name: string) => builtIns.has(name) : () => false,
  };
}
