#!/usr/bin/env node
/**
 * Rule #7 — Branch-switch detection before commit / push.
 *
 * PreToolUse on Bash. Detects `git commit`, `git push`, `git cherry-pick`,
 * `git reset --hard`, `git rebase`, `git branch -f/-d`. Reads expected branch
 * from sentinel; if missing, defaults to "main". Compares to actual current
 * branch via `git branch --show-current`. Mismatch → block unless explicit
 * confirmation marker in last assistant text OR override phrase.
 *
 * Confirmation markers in assistant response (case-sensitive substring):
 *   - BRANCH-SWITCH-CONFIRMED
 *   - RECOVERY-INTENT:
 * Override phrases: "recovery" (suppresses branch-switch + git-recovery rule keys)
 *
 * Spec: docs/superpowers/specs/2026-05-25-enforce-hard-rules-design.md
 */

import {
  readStdin,
  parseEventJson,
  readTranscript,
  lastUserPromptText,
  lastAssistantText,
  findOverride,
  logOverride,
  exitDecision,
  detectGitCommandKind,
  readGitBranch,
  getExpectedBranch,
} from './enforce-hook-helpers.mjs';

const RULE_KEY = 'branch-switch';

const CONFIRMATION_MARKERS = [
  'BRANCH-SWITCH-CONFIRMED',
  'RECOVERY-INTENT:',
];

export function decide({
  toolName,
  command,
  expectedBranch,
  actualBranch,
  assistantText,
  override,
}) {
  if (toolName !== 'Bash' || typeof command !== 'string') return { block: false };
  const kind = detectGitCommandKind(command);
  if (!kind) return { block: false };
  if (override) return { block: false };

  const exp = (expectedBranch || 'main').trim();
  const act = (actualBranch || '').trim();
  if (!act || act === exp) return { block: false };

  for (const marker of CONFIRMATION_MARKERS) {
    if (assistantText && assistantText.includes(marker)) return { block: false };
  }

  return {
    block: true,
    message: [
      `[enforce-branch-switch] About to run \`git ${kind}\` on branch "${act}" but expected "${exp}".`,
      `Likely cause: parallel session switched HEAD silently (see Pravila §15.1).`,
      ``,
      `If intentional — write one of these in your next response BEFORE running the command:`,
      `  BRANCH-SWITCH-CONFIRMED  (you intend to commit on ${act})`,
      `  RECOVERY-INTENT: <one-line reason>  (recovery operation, e.g., cherry-pick to main)`,
      ``,
      `Or include the override phrase "recovery" in the user's next prompt.`,
    ].join('\n'),
  };
}

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 override = findOverride(userPrompt, RULE_KEY);
    if (override) logOverride(RULE_KEY, override, event.session_id);

    const expected = getExpectedBranch(event.session_id) || 'main';
    const actual = readGitBranch();
    const assistantText = lastAssistantText(transcript);

    const result = decide({
      toolName, command,
      expectedBranch: expected,
      actualBranch: actual,
      assistantText,
      override,
    });
    exitDecision(result);
  } catch {
    exitDecision({ block: false });
  }
}

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