#!/usr/bin/env node

/**
 * audit-governance.mjs — GOVERNANCE.md Execution Governance Audit
 *
 * Checks:
 *  1. All task states are legal (7-state machine)
 *  2. State transitions are legal (no skipping)
 *  3. Stale task detection rules exist
 *  4. Blocker management (blocker.md template + schema)
 *  5. Execution audit exists (execution-audit.md template)
 *  6. Project Health Score formula defined
 *  7. Auto-Stop Rule defined
 *  8. Governance Quality Gate defined
 *
 * Usage:
 *   node scripts/audit-governance.mjs          ← standalone
 *   import { runGovernanceAudit } from './audit-governance.mjs'  ← via orchestrator
 */

import { fileURLToPath } from 'url';
import path from 'path';
import fs from 'fs';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const WORKSPACE = path.resolve(__dirname, '..');
const SELF = process.argv[1].replace(/\\/g, '/');

/**
 * @typedef {Object} CheckResult
 * @property {string} id
 * @property {string} name
 * @property {'PASS'|'WARN'|'FAIL'} status
 * @property {string} evidence
 * @property {string} [suggestion]
 */

/** @returns {CheckResult[]} */
export function runGovernanceAudit(workspaceDir) {
  const root = workspaceDir || WORKSPACE;
  const checks = [];

  // ---- Helpers ----
  function readFile(rel) {
    const p = path.join(root, rel);
    if (!fs.existsSync(p)) return null;
    return fs.readFileSync(p, 'utf8');
  }

  function check(prefix, id, name, ok, passEv, failEv, suggestion) {
    checks.push({
      id: `${prefix}-${id}`,
      name,
      status: ok ? 'PASS' : 'FAIL',
      evidence: ok ? passEv : failEv,
      ...(ok ? {} : { suggestion: suggestion || `Implement ${id} in GOVERNANCE.md` }),
    });
  }

  function warn(prefix, id, name, evidence, suggestion) {
    checks.push({ id: `${prefix}-${id}`, name, status: 'WARN', evidence, suggestion });
  }

  // ---- Read protocol file ----
  const gov = readFile('GOVERNANCE.md');
  if (!gov) {
    checks.push({
      id: 'GV-001',
      name: 'GOVERNANCE.md exists',
      status: 'FAIL',
      evidence: 'GOVERNANCE.md not found in workspace root',
      suggestion: 'Create GOVERNANCE.md with the Execution Governance Protocol',
    });
    return checks;
  }
  checks.push({
    id: 'GV-001',
    name: 'GOVERNANCE.md exists',
    status: 'PASS',
    evidence: `GOVERNANCE.md found (${gov.length} bytes)`,
  });

  // ---- §1 State Machine ----
  const hasStateMachine = /§1 State Machine/.test(gov);
  check('GV', '1', '§1 State Machine defined', hasStateMachine,
    'Found "§1 State Machine" section',
    'Add §1 State Machine section');

  // 7 states
  const states = ['NOT_STARTED', 'IN_PROGRESS', 'BLOCKED', 'WAITING_DEPENDENCY', 'READY_FOR_REVIEW', 'VERIFIED', 'FAILED', 'ARCHIVED'];
  const foundStates = states.filter(s => gov.includes(s));
  check('GV', '1a', `§1a ${states.length} states defined (found ${foundStates.length})`, foundStates.length >= states.length,
    `All ${states.length} states present: ${foundStates.join(', ')}`,
    `Missing states: ${states.filter(s => !gov.includes(s)).join(', ')}`,
    'Add missing states to §1.1 Allowed States');

  // Mermaid diagram
  const hasMermaid = /stateDiagram-v2/.test(gov);
  check('GV', '1b', '§1b State transition diagram (mermaid)', hasMermaid,
    'Found mermaid stateDiagram-v2 with all legal transitions',
    'Add a mermaid stateDiagram-v2 for the state machine');

  // Transition table
  const hasTransitionTable = /Transition Table/.test(gov) || /§1\.3/.test(gov);
  check('GV', '1c', '§1c Transition table (From/To/Condition)', hasTransitionTable,
    'Found Transition Table with From/To/Condition columns',
    'Add Transition Table (From | To | Condition)');

  // Prohibited transitions
  const hasProhibited = /Prohibited Transitions/.test(gov) || /§1\.4/.test(gov);
  check('GV', '1d', '§1d Prohibited transitions defined', hasProhibited,
    'Found "Prohibited Transitions" with ❌ markers for illegal jumps',
    'Add "Prohibited Transitions" with explicit ❌ transition violations');

  // ---- §2 Heartbeat Rule ----
  const hasHeartbeat = /§2 Heartbeat Rule/.test(gov);
  check('GV', '2', '§2 Heartbeat Rule defined', hasHeartbeat,
    'Found "§2 Heartbeat Rule" section',
    'Add §2 Heartbeat Rule section');

  // Heartbeat format defined
  const hasHeartbeatFormat = /"ts".*"status".*"completed".*"remaining".*"next_action"/.test(gov);
  check('GV', '2a', '§2a Heartbeat JSON format defined (ts/status/completed/remaining/next_action)',
    hasHeartbeatFormat,
    'Heartbeat JSON format found with ts, status, completed, remaining, next_action fields',
    'Define heartbeat format as JSON with ts/status/completed/remaining/next_action');

  // Stale thresholds
  const hasStaleThresholds = /Small[\s\S]*3[0０][\s\S]*Medium[\s\S]*2[\s\S]*Large[\s\S]*8/.test(gov);
  check('GV', '2b', '§2b Stale thresholds defined (Small=30m, Medium=2h, Large=8h)',
    hasStaleThresholds,
    'Found stale thresholds: Small=30min/30分钟, Medium=2h/2小时, Large=8h/8小时',
    'Add Stale thresholds: Small=30min, Medium=2h, Large=8h');

  // progress.log template exists
  const progressTmpl = readFile('scripts/templates/progress.log.template');
  check('GV', '2c', 'progress.log.template exists', progressTmpl !== null,
    'Found scripts/templates/progress.log.template',
    'Create scripts/templates/progress.log.template');

  // ---- §3 Blocker Management ----
  const hasBlockerMgmt = /§3 Blocker Management/.test(gov);
  check('GV', '3', '§3 Blocker Management defined', hasBlockerMgmt,
    'Found "§3 Blocker Management" section',
    'Add §3 Blocker Management section');

  // Blocker schema
  const hasBlockerSchema = /blocker[\s\S]*root_cause[\s\S]*owner[\s\S]*mitigation[\s\S]*dependency[\s\S]*created_at/.test(gov) || /protocol-reference\/templates\.md[\s\S]*blocker/.test(gov);
  check('GV', '3a', '§3a Blocker schema (blocker/root_cause/owner/mitigation/dependency/created_at)',
    hasBlockerSchema,
    'Blocker schema found in GOVERNANCE.md or protocol-reference/templates.md',
    'Define blocker md schema in protocol-reference/templates.md');

  // Blocker escalation
  const hasBlockerEsc = /Blocker Escalation/.test(gov) || /escalated/.test(gov);
  check('GV', '3b', '§3b Blocker escalation defined', hasBlockerEsc,
    'Found Blocker Escalation mechanism with escalated: YES/NO',
    'Add Blocker Escalation: if escalated=YES, notify user and stop AP');

  // blocker.md template exists
  const blockerTmpl = readFile('scripts/templates/blocker.md.template');
  check('GV', '3c', 'blocker.md.template exists', blockerTmpl !== null,
    'Found scripts/templates/blocker.md.template',
    'Create scripts/templates/blocker.md.template');

  // ---- §4 Replanning Trigger ----
  const hasReplan = /§4 Replanning Trigger/.test(gov);
  check('GV', '4', '§4 Replanning Trigger defined', hasReplan,
    'Found "§4 Replanning Trigger" with R1-R8 conditions',
    'Add §4 Replanning Trigger with R1-R8 conditions');

  // 8 trigger conditions
  const triggerCount = (gov.match(/R\d /g) || []).length;
  warn('GV', '4a', `§4a Replan triggers: ${triggerCount} (target: ≥8)`,
    `Found ${triggerCount} replan trigger conditions (R1-R? )`,
    'Ensure all 8 replan triggers (R1-R8) are defined');

  // Replan log entry format
  const hasReplanLog = /Replan.*#N/.test(gov) || /replan.*evidence/.test(gov);
  check('GV', '4b', '§4b Replan log entry format defined', hasReplanLog,
    'Found Replan log entry format with trigger/cause/before/after/impact',
    'Define Replan log entry format (trigger, cause, before, after, impact)');

  // ---- §5 Scope Explosion ----
  const hasScope = /§5 Scope Explosion/.test(gov);
  check('GV', '5', '§5 Scope Explosion Detector defined', hasScope,
    'Found "§5 Scope Explosion Detector" with S1-S4 detection rules',
    'Add §5 Scope Explosion Detector with S1-S4 detection rules');

  // ---- §6 Execution Audit ----
  const hasAudit = /§6 Execution Audit/.test(gov);
  check('GV', '6', '§6 Execution Audit defined', hasAudit,
    'Found "§6 Execution Audit" section',
    'Add §6 Execution Audit section');

  // execution-audit.md template exists
  const auditTmpl = readFile('scripts/templates/execution-audit.md.template');
  check('GV', '6a', 'execution-audit.md.template exists', auditTmpl !== null,
    'Found scripts/templates/execution-audit.md.template',
    'Create scripts/templates/execution-audit.md.template');

  // ---- §7 Health Score ----
  const hasHealth = /§7 Project Health Score/.test(gov);
  check('GV', '7', '§7 Project Health Score defined', hasHealth,
    'Found "§7 Project Health Score" with 5 dimensions (Delivery/Quality/Parallelism/Dependency/Learning)',
    'Add §7 Project Health Score with 5 dimensions');

  // All 5 sub-scores
  const hasDelivery = /Delivery Score/.test(gov);
  const hasQuality = /Quality Score/.test(gov);
  const hasParallelism = /Parallelism Score/.test(gov);
  const hasDependency = /Dependency Score/.test(gov);
  const hasLearning = /Learning Score/.test(gov);
  const all5 = hasDelivery && hasQuality && hasParallelism && hasDependency && hasLearning;
  check('GV', '7a', '§7a All 5 Health Score dimensions defined (Delivery/Quality/Parallelism/Dependency/Learning)',
    all5,
    'All 5 dimensions found with formulas',
    'Ensure all 5 dimensions: Delivery, Quality, Parallelism, Dependency, Learning');

  // Weighted formula
  const hasWeighted = /Health Score\s?=\s?Delivery\s*[×x]\s*0\.\d+/.test(gov);
  check('GV', '7b', '§7b Weighted final formula defined', hasWeighted,
    'Found weighted formula: Health Score = Delivery×0.30 + Quality×0.25 + Parallelism×0.15 + Dependency×0.15 + Learning×0.15',
    'Define the weighted sum formula for final Health Score');

  // Rating
  const hasRating = /EXCELLENT[\s\S]*GOOD[\s\S]*ACCEPTABLE[\s\S]*AT RISK[\s\S]*CRITICAL/.test(gov) || /protocol-reference\/metrics\.md/.test(gov);
  check('GV', '7c', '§7c Health Score rating defined (EXCELLENT/GOOD/ACCEPTABLE/AT RISK/CRITICAL)',
    hasRating,
    'Found 5-level rating in GOVERNANCE.md or protocol-reference/metrics.md',
    'Add 5-level rating to protocol-reference/metrics.md');

  // ---- §8 Auto-Stop ----
  const hasAutoStop = /§8 Auto-Stop/.test(gov);
  check('GV', '8', '§8 Auto-Stop Rule defined', hasAutoStop,
    'Found "§8 Auto-Stop Rule" with A1-A3 conditions and RECOVERY MODE',
    'Add §8 Auto-Stop Rule with A1(>3 failures), A2(Health<60), A3(Critical dep failed)');

  // Recovery mode
  const hasRecoveryMode = /RECOVERY MODE/.test(gov);
  check('GV', '8a', '§8a RECOVERY MODE defined', hasRecoveryMode,
    'Found RECOVERY MODE with recovery steps and user approval requirement',
    'Add RECOVERY MODE procedure (stop, analyze, plan, user approval)');

  // ---- §9 Quality Gate ----
  const hasGovGate = /§9 Governance Quality Gate/.test(gov);
  check('GV', '9', '§9 Governance Quality Gate defined', hasGovGate,
    'Found "§9 Governance Quality Gate" with 8-item checklist',
    'Add §9 Governance Quality Gate with 8 pre-output checks');

  // ---- §10 State Initialization ----
  const hasInit = /§10 Governance State Initialization/.test(gov);
  check('GV', '10', '§10 Governance State Initialization defined', hasInit,
    'Found "§10 Governance State Initialization" with project start/handoff/override',
    'Add §10 Governance State Initialization section');

  // ---- §11 Integration ----
  const hasIntegration = /§11 Integration Summary/.test(gov);
  check('GV', '11', '§11 Integration Summary defined', hasIntegration,
    'Found "§11 Integration Summary" with raw diagram and lifecycle',
    'Add §11 Integration Summary');

  // ---- §11 Integration Summary (re-numbered — §12 was deleted) ----
  const hasInt11 = /§11 Integration Summary/.test(gov);
  check('GV', '11b', '§11 Integration Summary includes Portfolio ref', hasInt11,
    'Found §11 Integration Summary (with governance→portfolio data flow)',
    'Portfolio Integration reference is covered by §11 Integration Summary');

  // ---- Runtime artifacts scan ----
  // Scan for actual progress.log files
  const progressLogs = findFiles(root, 'progress.log');
  const blockerFiles = findFiles(root, 'blocker.md');
  const execAudits = findFiles(root, 'execution-audit.md');

  warn('GV', 'RA-progress', `Progress logs found: ${progressLogs.length}`,
    progressLogs.length > 0
      ? `Found ${progressLogs.length} progress.log file(s): ${progressLogs.join(', ')}`
      : 'No progress.log files found (expected during active project execution)',
    progressLogs.length === 0 ? 'Active projects should have progress.log heartbeat entries' : undefined);

  warn('GV', 'RA-blocker', `Blocker files found: ${blockerFiles.length}`,
    blockerFiles.length > 0
      ? `Found ${blockerFiles.length} blocker.md file(s): ${blockerFiles.join(', ')}`
      : 'No blocker.md files found',
    blockerFiles.length === 0 ? '(no active blockers — good)' : undefined);

  warn('GV', 'RA-audit', `Execution audits found: ${execAudits.length}`,
    execAudits.length > 0
      ? `Found ${execAudits.length} execution-audit.md file(s): ${execAudits.join(', ')}`
      : 'No execution-audit.md found (expected after first project execution)',
    execAudits.length === 0 ? 'Run first project and generate execution-audit.md' : undefined);

  // ---- Health Score calculation (from available data) ----
  // Without actual project data, report N/A
  checks.push({
    id: 'GV-HS',
    name: 'Project Health Score calculated',
    status: 'WARN',
    evidence: 'No active project data available for health score calculation. Formula is defined in GOVERNANCE.md §7.',
    suggestion: 'Health Score will be calculated during/after project execution',
  });

  // ---- Summary ----
  const passCount = checks.filter(c => c.status === 'PASS').length;
  const warnCount = checks.filter(c => c.status === 'WARN').length;
  const failCount = checks.filter(c => c.status === 'FAIL').length;
  checks.push({
    id: 'GV-SUMMARY',
    name: `Summary — ${passCount} PASS / ${warnCount} WARN / ${failCount} FAIL`,
    status: failCount > 0 ? 'FAIL' : warnCount > 0 ? 'WARN' : 'PASS',
    evidence: `${passCount} passed, ${warnCount} warnings, ${failCount} failures`,
  });

  return checks;
}

/** Recursively find files by name (limited depth) */
function findFiles(dir, filename, depth = 3) {
  const results = [];
  if (depth <= 0) return results;
  try {
    const entries = fs.readdirSync(dir, { withFileTypes: true });
    for (const entry of entries) {
      if (entry.name === filename) {
        results.push(path.relative(WORKSPACE, path.join(dir, entry.name)));
      }
      if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {
        results.push(...findFiles(path.join(dir, entry.name), filename, depth - 1));
      }
    }
  } catch { /* skip */ }
  return results;
}

// ---- Standalone runner ----
if (SELF.endsWith('/audit-governance.mjs') || SELF.endsWith('\\audit-governance.mjs')) {
  const results = runGovernanceAudit(WORKSPACE);
  console.log(`\n=== GOVERNANCE.md Audit ===\n`);
  for (const r of results) {
    const icon = r.status === 'PASS' ? '✅' : r.status === 'WARN' ? '⚠️' : '❌';
    console.log(`  ${icon} [${r.id}] ${r.name}`);
    console.log(`     ${r.evidence}`);
    if (r.suggestion) console.log(`     → ${r.suggestion}`);
    console.log();
  }
}
