import { computeValidateRows } from "../application/validate-engine";
import type { ValidateRow } from "../application/validate-engine";
import { closeDb, openDb } from "../db";
import { getProjectRoot } from "../runtime";
import { bootstrapCodemap } from "./bootstrap-codemap";

interface ValidateOpts {
  root: string;
  configFile: string | undefined;
  stateDir?: string | undefined;
  paths: string[];
  json?: boolean;
}

/**
 * Print **`codemap validate`** usage.
 */
export function printValidateCmdHelp(): void {
  console.log(`Usage: codemap validate [--json] [paths...]

Compare the SHA-256 stored in the codemap index (default \`.codemap/index.db\`) against the on-disk content of each
file. Prints rows for entries that are out of sync — without the agent paying
to re-read every file.

  paths       Project-relative or absolute file paths to check. If omitted,
              all indexed files are checked. Output path keys are always
              project-relative POSIX paths (absolute argv is normalized).

Statuses:
  stale       The file exists but its content_hash differs from the index.
  missing     The file is in the index but has been deleted on disk.
  unindexed   The file exists on disk but is not present in the index (only
              when explicit paths are passed).
  rejected    The path could not be checked safely. reason is one of:
              path escapes project root | path escapes via symlink |
              path resolves outside project root.

Flags:
  --json      Emit a JSON array of {path, status[, reason]} objects (for agents).
              Exits 1 when any row is returned (including rejected).
  --help, -h  Show this help.

Examples:
  codemap validate                       # check every indexed file
  codemap validate src/parser.ts         # check just one file
  codemap validate --json src/a.ts src/b.ts
`);
}

/**
 * Parse `argv` after the bootstrap split: `rest[0]` must be `"validate"`.
 */
export function parseValidateRest(
  rest: string[],
):
  | { kind: "help" }
  | { kind: "error"; message: string }
  | { kind: "run"; paths: string[]; json: boolean } {
  if (rest[0] !== "validate") {
    throw new Error("parseValidateRest: expected validate");
  }
  let json = false;
  const paths: string[] = [];
  for (let i = 1; i < rest.length; i++) {
    const a = rest[i];
    if (a === "--help" || a === "-h") return { kind: "help" };
    if (a === "--json") {
      json = true;
      continue;
    }
    if (a.startsWith("-")) {
      return {
        kind: "error",
        message: `codemap: unknown option "${a}". Run codemap validate --help for usage.`,
      };
    }
    paths.push(a);
  }
  return { kind: "run", paths, json };
}

/**
 * Initialize Codemap for `opts.root`, then print the staleness report.
 * Sets **`process.exitCode`** to **1** if any rows are returned (mirrors
 * `git status` semantics so agents can branch on `$?`).
 */
export async function runValidateCmd(opts: ValidateOpts): Promise<void> {
  const json = opts.json === true;
  try {
    await bootstrapCodemap(opts);
    const db = openDb();
    let rows: ValidateRow[];
    try {
      rows = computeValidateRows(db, getProjectRoot(), opts.paths);
    } finally {
      closeDb(db, { readonly: true });
    }
    if (json) {
      console.log(JSON.stringify(rows));
    } else if (rows.length === 0) {
      console.log("(everything in sync)");
    } else {
      console.table(rows);
    }
    if (rows.length > 0) process.exitCode = 1;
  } catch (err) {
    const msg = err instanceof Error ? err.message : String(err);
    if (json) {
      console.log(JSON.stringify({ error: msg }));
    } else {
      console.error(msg);
    }
    process.exitCode = 1;
  }
}
