#!/usr/bin/env node
/**
 * Rule #4 — Require fresh verification artifact before git commit / push.
 *
 * PreToolUse on Bash. If command is git commit / push, check the
 * verify-pass-<session>.json sentinel:
 *   - missing → block
 *   - age > MAX_AGE_SEC → block ("stale")
 *   - result !== 'pass' → block ("last run failed")
 *
 * Override phrases: `срочно` / `быстрый коммит` / `ремонт инфраструктуры`.
 *
 * Spec: docs/superpowers/specs/2026-05-25-enforce-hard-rules-design.md
 */

import {
  readStdin,
  parseEventJson,
  readTranscript,
  lastUserPromptText,
  findOverride,
  findOverrideAttempt,
  logOverride,
  exitDecision,
  detectGitCommandKind,
  readSentinel,
  sentinelAgeSec,
  isDocsOnlyChange,
  listChangedFiles,
} from './enforce-hook-helpers.mjs';

const RULE_KEY_COMMIT = 'verify-before-commit';
const RULE_KEY_PUSH = 'verify-before-push';
const MAX_AGE_SEC = 30 * 60; // 30 min

export function decide({ toolName, command, sentinel, sentinelAge, override, overrideAttempt, changedPaths }) {
  if (toolName !== 'Bash' || typeof command !== 'string') return { block: false };
  const kind = detectGitCommandKind(command);
  if (kind !== 'commit' && kind !== 'push') return { block: false };
  if (override) return { block: false };

  // Docs-only short-circuit (2026-05-27): if EVERY staged/unpushed file is a
  // `.md` document, the regression gate adds no value — there's no executable
  // code in the change set, so a fresh test run can't tell us anything new.
  // Empty/missing changedPaths → unknown → fall through to normal checks.
  if (isDocsOnlyChange(changedPaths)) return { block: false };

  // Silent-reject bug fix (2026-05-26): when user typed an override phrase that
  // requires justification (e.g. "ремонт инфраструктуры") but forgot the
  // "<prefix> <reason>" line, emit an explicit diagnostic — not the generic
  // "no verification artifact" message that misled users into thinking the
  // override mechanism was broken.
  if (overrideAttempt && overrideAttempt.requires_justification) {
    return {
      block: true,
      message: [
        `[enforce-verify-before-push] Override phrase "${overrideAttempt.phrase}" found, but missing justification line.`,
        `Add a line "${overrideAttempt.requires_justification} <one-line причина>" in the SAME prompt.`,
        ``,
        `Example:`,
        `  ${overrideAttempt.phrase}`,
        `  ${overrideAttempt.requires_justification} observer refresh after brainstorm session`,
      ].join('\n'),
    };
  }

  if (!sentinel) {
    return {
      block: true,
      message: [
        `[enforce-verify-before-push] No verification artifact found.`,
        `Run a full test suite first (vitest run / composer test) before \`git ${kind}\`.`,
      ].join('\n'),
    };
  }
  if (sentinel.result !== 'pass') {
    return {
      block: true,
      message: [
        `[enforce-verify-before-push] Last verification FAILED (result=${sentinel.result}, exit=${sentinel.exit_code}).`,
        `Tests: ${sentinel.tests_passed}/${sentinel.tests_total} passed, ${sentinel.tests_failed} failed.`,
        `Re-run the suite and address failures before \`git ${kind}\`.`,
      ].join('\n'),
    };
  }
  if (sentinelAge !== null && sentinelAge > MAX_AGE_SEC) {
    return {
      block: true,
      message: [
        `[enforce-verify-before-push] Verification artifact is stale (age ${sentinelAge}s > ${MAX_AGE_SEC}s).`,
        `Re-run the full test suite before \`git ${kind}\`.`,
      ].join('\n'),
    };
  }
  return { block: false };
}

async function main() {
  try {
    const raw = await readStdin();
    const event = parseEventJson(raw);
    const toolName = event.tool_name || '';
    const command = (event.tool_input && event.tool_input.command) || '';

    const transcript = readTranscript(event.transcript_path);
    const userPrompt = lastUserPromptText(transcript);
    const kind = detectGitCommandKind(command);
    const ruleKey = kind === 'commit' ? RULE_KEY_COMMIT : RULE_KEY_PUSH;
    const override = findOverride(userPrompt, ruleKey);
    if (override) logOverride(ruleKey, override, event.session_id);
    const overrideAttempt = override ? null : findOverrideAttempt(userPrompt, ruleKey);

    const sentinel = readSentinel('verify-pass', event.session_id);
    const age = sentinelAgeSec('verify-pass', event.session_id);
    const changedPaths = (kind === 'commit' || kind === 'push') ? listChangedFiles(kind) : [];

    const result = decide({ toolName, command, sentinel, sentinelAge: age, override, overrideAttempt, changedPaths });
    exitDecision(result);
  } catch {
    exitDecision({ block: false });
  }
}

const isCli = process.argv[1] && process.argv[1].replace(/\\/g, '/').endsWith('/enforce-verify-before-push.mjs');
if (isCli) main();
