import fg from "fast-glob";
import { collectMeasureStructure } from "../internal/measure/structure/structure";
import { collectMeasureVersions } from "../internal/measure/versions/versions";
import { type CommandResult, silentFailure, success } from "../lib/command-result";
import { findMissingComponentTests } from "./component-tests";

export interface VerifyConfig {
  modulesRoot: string;
  appsRoot: string;
  cwd: string;
  installedVersion: string;
}

export interface CheckOutcome {
  name: string;
  ok: boolean;
}

/** Pass only when every check passes. */
export function verifyResult(outcomes: CheckOutcome[]): CommandResult {
  return outcomes.every((o) => o.ok) ? success() : silentFailure(1);
}

function section(title: string): void {
  console.log(`\n=== ${title} ===`);
}

/**
 * Run the deterministic checks and exit non-zero on any violation:
 *
 * - versions        — version consistency across package manifests
 * - structure       — required directories and file naming rules
 * - component-tests — every app component has a co-located test
 */
export async function runVerify(config: VerifyConfig): Promise<CommandResult> {
  const { modulesRoot, appsRoot, cwd, installedVersion } = config;
  const outcomes: CheckOutcome[] = [];

  const moduleDirs = await fg(`${modulesRoot}/*`, { cwd, onlyDirectories: true, deep: 1 });
  const appDirs = await fg(`${appsRoot}/*`, { cwd, onlyDirectories: true, deep: 1 });

  section("versions");
  const versions = await collectMeasureVersions({ appsRoot, modulesRoot, cwd, installedVersion });
  if (versions.status === "ok") {
    console.log("  ok");
  } else {
    for (const f of versions.findings.filter((f) => f.severity !== "info")) {
      console.log(`  ${f.severity}: ${f.subject} — ${f.message}`);
    }
  }
  outcomes.push({ name: "versions", ok: versions.status === "ok" });

  section("structure");
  const structureViolations = [];
  if (moduleDirs.length > 0) {
    const s = await collectMeasureStructure({ modulesRoot }, cwd);
    structureViolations.push(...(s.modules?.violations ?? []));
  }
  for (const appDir of appDirs) {
    const s = await collectMeasureStructure({ appRoot: appDir }, cwd);
    structureViolations.push(...(s.apps?.violations ?? []));
  }
  if (structureViolations.length === 0) {
    console.log("  ok");
  } else {
    for (const v of structureViolations) {
      console.log(`  ${v.type}: ${v.path}${v.rule ? ` (${v.rule})` : ""}`);
    }
  }
  outcomes.push({ name: "structure", ok: structureViolations.length === 0 });

  section("component-tests");
  const missingTests = [];
  for (const appDir of appDirs) {
    missingTests.push(...(await findMissingComponentTests(appDir, cwd)));
  }
  if (missingTests.length === 0) {
    console.log("  ok");
  } else {
    for (const m of missingTests) {
      console.log(
        `  missing-component-test: ${m.sourcePath} (add ${m.expectedTest}, or mark the file /* no-test: <reason> */)`,
      );
    }
  }
  outcomes.push({ name: "component-tests", ok: missingTests.length === 0 });

  section("verify summary");
  for (const o of outcomes) {
    console.log(`  ${o.ok ? "PASS" : "FAIL"}  ${o.name}`);
  }

  return verifyResult(outcomes);
}
