/**
 * SET 80 — NODE-COMPLIANT REPAIR (Andy, 2026-07-27: "do we ever take the node
 * in question, supply the substrata consisting of supergit, supercache, and
 * deterministic substrate contextually into a parsing, as well as the node,
 * with explicit instructions to repair the node compliant to the schema?").
 *
 * The answer was no. Both repair sites passed `ownedFiles: []`, and
 * `prompts.ts` stated the policy explicitly: "Deliberately NO current
 * ownedFiles / broken impl — fresh from frozen contract." The model has never
 * seen the node it is repairing — only a ~300-char exhibit EXCERPT framed as
 * "evidence, not a base to patch". Every repair was a blind whole-file
 * regeneration, which is why entropy resets carry so much of the healing.
 *
 * That anti-anchoring policy is right for an ENTROPY RESET, where the point is
 * to discard a poisoned basin. It is wrong for a targeted repair of a node
 * whose defect is localized and whose contract is known: there, withholding
 * the node forces the model to re-derive everything that already worked.
 *
 * This rung supplies all four at once — the SCHEMA (deterministic, from stubs
 * + the frozen contract), the NODE (in full), the SUBSTRATA (supergit lineage,
 * supercache/meta memory, deterministic substrate findings) — with an explicit
 * instruction to return a complete node that COMPLIES with the schema.
 */
import type { FeatureNode, GeneratedFile } from '../core/types';
import type { IRParts } from '../core/irScaffold';

/** Whole-node inline budget; larger nodes are excerpted around the defect. */
export const NODE_INLINE_CAP = 3000;

export interface NodeSchema {
  /** Exported symbols the node MUST provide, with their exact signatures. */
  signatures: string[];
  /** Behaviors the frozen contract requires, as the contract states them. */
  behaviors: { name: string; assertion: string }[];
  /** Modules this node is allowed to import from. */
  dependencies: string[];
  path: string;
}

/**
 * The schema is DERIVED, never drawn: stub signatures are the shape contract,
 * the frozen cases are the behavior contract. No model involvement.
 */
export function buildNodeSchema(
  feature: FeatureNode,
  parts: IRParts | null,
  deps: GeneratedFile[],
): NodeSchema {
  const signatures: string[] = [];
  for (const stub of feature.stubs ?? []) {
    for (const m of stub.content.matchAll(
      /export\s+(?:async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(([^)]*)\)(?:\s*:\s*([^{;]+))?/g,
    )) {
      signatures.push(`${m[1]}(${m[2]})${m[3] ? `: ${m[3].trim()}` : ''}`);
    }
    for (const m of stub.content.matchAll(/export\s+class\s+([A-Za-z_$][\w$]*)/g)) {
      signatures.push(`class ${m[1]}`);
    }
    for (const m of stub.content.matchAll(/export\s+const\s+([A-Za-z_$][\w$]*)\s*:\s*([^=]+)=/g)) {
      signatures.push(`${m[1]}: ${m[2]!.trim()}`);
    }
  }
  const behaviors = (parts?.cases ?? []).map(c => ({
    name: c.name,
    assertion: c.body.replace(/\s+/g, ' ').trim().slice(0, 200),
  }));
  return {
    signatures,
    behaviors,
    dependencies: deps.map(d => d.path),
    path: feature.files[0] ?? '(unknown)',
  };
}

export function formatNodeSchema(schema: NodeSchema): string {
  const lines = [`SCHEMA — the node at ${schema.path} must satisfy ALL of the following.`];
  if (schema.signatures.length) {
    lines.push('Exported shape (signatures are FIXED — do not rename, do not change arity):');
    for (const s of schema.signatures) lines.push(`  ${s}`);
  }
  if (schema.behaviors.length) {
    lines.push('Required behaviors, as the frozen contract states them:');
    for (const b of schema.behaviors) lines.push(`  "${b.name}": ${b.assertion}`);
  }
  if (schema.dependencies.length) {
    lines.push(`Available dependencies: ${schema.dependencies.join(', ')}.`);
  }
  return lines.join('\n');
}

/**
 * The node itself. Whole file when it fits; otherwise the spans enclosing the
 * symbols under repair, so the model still edits real code rather than
 * hallucinating a replacement for code it cannot see.
 */
export function formatNodeUnderRepair(
  source: string,
  path: string,
  focusSymbols: readonly string[],
  cap = NODE_INLINE_CAP,
): string {
  if (source.length <= cap) {
    return `NODE UNDER REPAIR — ${path} (complete current source):\n${source}`;
  }
  const spans: string[] = [];
  for (const sym of focusSymbols) {
    const esc = sym.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    const m = new RegExp(`(export\\s+)?(async\\s+)?function\\s+${esc}\\s*\\(`).exec(source);
    if (!m) continue;
    const start = source.lastIndexOf('\n', m.index) + 1;
    let depth = 0;
    let opened = false;
    let end = source.length;
    for (let i = m.index; i < source.length; i++) {
      if (source[i] === '{') { depth++; opened = true; }
      else if (source[i] === '}') { depth--; if (opened && depth === 0) { end = i + 1; break; } }
    }
    spans.push(source.slice(start, end));
  }
  const body = spans.length ? spans.join('\n\n') : source.slice(0, cap);
  return `NODE UNDER REPAIR — ${path} (excerpted: the declarations under repair; the rest of the file is unchanged and must be preserved):\n${body}`;
}

export interface NodeRepairDossier {
  text: string;
  /** True when the whole node was inlined (not excerpted). */
  whole: boolean;
}

/**
 * Assemble the complete repair parsing: schema + node + all three substrata +
 * explicit compliance instructions.
 */
export function buildNodeRepairDossier(opts: {
  schema: NodeSchema;
  source: string;
  focusSymbols: readonly string[];
  /** Deterministic substrate: what the classifier and oracles already know. */
  substrate: string;
  /** Supergit: this node's causal trail. */
  ledger: string;
  /** Supercache: the run buffer + cross-run search space. */
  supercache: string;
  /** The findings, verbatim. */
  findings: string;
}): NodeRepairDossier {
  const node = formatNodeUnderRepair(opts.source, opts.schema.path, opts.focusSymbols);
  const whole = node.includes('complete current source');
  const text = [
    'REPAIR THIS NODE SO THAT IT COMPLIES WITH THE SCHEMA.',
    'You are editing an existing artifact, not writing a new one. Return the COMPLETE node —'
    + ' every export the schema names, with its signature unchanged. Preserve everything that already'
    + ' satisfies the schema; change only what the findings show to be wrong. Do not modify or restate'
    + ' the tests. Do not add exports the schema does not name.',
    formatNodeSchema(opts.schema),
    node,
    opts.findings ? `FINDINGS — what the frozen contract observed:\n${opts.findings}` : '',
    opts.substrate ? `SUBSTRATE (deterministic analysis already performed):\n${opts.substrate}` : '',
    opts.ledger ? `SUPERGIT (how this node reached its current state):\n${opts.ledger}` : '',
    opts.supercache ? `SUPERCACHE (what has already been tried):\n${opts.supercache}` : '',
  ].filter(Boolean).join('\n\n');
  return { text, whole };
}

export function formatNodeRepairEvent(path: string, whole: boolean, symbols: readonly string[]): string {
  return `node-compliant repair: ${path} supplied ${whole ? 'in full' : `as ${symbols.length} declaration span(s)`}`
    + ' with schema + supergit + supercache + substrate — repair-in-place, not regeneration';
}
