/**
 * Han `code-review` — a bespoke pipeline, NOT a spine. Per-dimension reviewers
 * fan out, then each dimension's findings are adversarially VERIFIED (false
 * positives dropped) before they reach the report. The verification is a `code`
 * step that itself dispatches an adversarial-validator per dimension in
 * parallel — the conductor's scheduler runs the static steps; this step owns
 * the dynamic, per-dimension fan-in.
 */
import type { Band, Flow, Step, StepContext } from '../types.js';
import { dispatchAgent } from '../dispatch.js';
import { fastNote, readBand } from '../spine.js';
import { produceContract, reviewContract } from '../contracts.js';
import { slugify } from '../render.js';
import { q, repoLine } from './_util.js';

const BAND_ORDER: Record<Band, number> = { small: 0, medium: 1, large: 2 };

interface Dimension {
  id: string;
  agent: string;
  label: string;
  minBand: Band;
  lens: string;
}

const DIMENSIONS: Dimension[] = [
  { id: 'correctness', agent: 'behavioral-analyst', label: 'Correctness & behaviour', minBand: 'small', lens: 'logic errors, incorrect behaviour, mishandled data flow and error propagation' },
  { id: 'structure', agent: 'structural-analyst', label: 'Structure & coupling', minBand: 'small', lens: 'coupling, boundary violations, duplication, dependency-direction problems' },
  { id: 'security', agent: 'adversarial-security-analyst', label: 'Security', minBand: 'medium', lens: 'exploitable vulnerabilities, each with file:line + an exploit path or a CVE' },
  { id: 'resilience', agent: 'on-call-engineer', label: 'Resilience', minBand: 'medium', lens: 'missing timeouts, retries without backoff, swallowed errors, unbounded results, blocking I/O in async paths' },
  { id: 'concurrency', agent: 'concurrency-analyst', label: 'Concurrency', minBand: 'large', lens: 'races, lock ordering, shared-resource contention, async error handling' },
];

function dimEnabled(ctx: StepContext, min: Band): boolean {
  return BAND_ORDER[readBand(ctx.input)] >= BAND_ORDER[min];
}

function hasFindings(out: string | undefined): boolean {
  return Boolean(out) && !/^\s*no findings/i.test(out!.trim());
}

const findSteps: Step[] = DIMENSIONS.map((d) => ({
  id: d.id,
  kind: 'agent',
  agent: d.agent,
  when: (ctx) => dimEnabled(ctx, d.minBand),
  run: (ctx) =>
    `Review the target below for ${d.lens}. Return a NUMBERED list of findings; for each: the issue, file:line, and why it matters. If there are none, reply exactly "No findings."${repoLine(ctx)}\n\nTARGET: ${q(ctx)}` +
    produceContract(['evidence']) +
    fastNote(ctx),
}));

const verifyStep: Step = {
  id: 'verify',
  kind: 'code',
  deps: DIMENSIONS.map((d) => d.id),
  run: async (ctx) => {
    const withFindings = DIMENSIONS.filter((d) => hasFindings(ctx.results[d.id]));
    if (withFindings.length === 0) return '_No findings to verify._';
    const verified = await Promise.all(
      withFindings.map(async (d) => {
        const out = await dispatchAgent(
          'adversarial-validator',
          `Below are code-review findings in the "${d.label}" dimension. For EACH finding, try to refute it — is it a real, correct issue or a false positive? Return ONLY the surviving findings (drop refuted/false-positive ones), each with a one-line note on why it holds, and state how many you dropped.${reviewContract(['evidence'])}\n\n----- FINDINGS -----\n${ctx.results[d.id]}` +
            fastNote(ctx),
        );
        return `### ${d.label}\n\n${out}`;
      }),
    );
    return verified.join('\n\n');
  },
};

function renderCodeReview(ctx: StepContext): string {
  const model = process.env.CONDUCTOR_MODEL ?? 'llama-swap/qwen3.6-35b-a3b-mxfp4';
  const band = readBand(ctx.input);
  const parts: string[] = [
    `# Conductor Report — code-review: ${q(ctx)}`,
    `> BooCode code conductor · band=${band}${ctx.input.concise ? ' · fast' : ''} · workers on \`${model}\`. Per-dimension reviewers fan out, then each dimension's findings are adversarially verified — false positives dropped — before reaching this report.`,
    `## Confirmed findings (after adversarial verification)\n\n${ctx.results.verify ?? '_none_'}`,
  ];
  const raw = DIMENSIONS.filter((d) => ctx.results[d.id]).map((d) => `### ${d.label} (raw)\n\n${ctx.results[d.id]}`);
  if (raw.length) parts.push(`## Appendix — raw findings before verification\n\n${raw.join('\n\n')}`);
  return parts.join('\n\n') + '\n';
}

export const codeReview: Flow = {
  name: 'code-review',
  description: 'per-dimension review → adversarially verify each dimension (drops false positives)',
  steps: [...findSteps, verifyStep],
  render: renderCodeReview,
  output: (ctx) => `conductor-report-code-review-${slugify(q(ctx))}.md`,
};
