// Copyright (c) 2026 defconxt. All rights reserved.
// Licensed under AGPL-3.0 — see LICENSE file for details.
// NERF is a trademark of defconxt.

/**
 * Self-Check Module — Post-output hallucination and verification gate.
 *
 * Analyzes agent output for unverified claims, hedging language,
 * rationalization patterns, and hallucination red flags. Returns
 * structured findings with a trust score.
 *
 * Sources:
 * - SuperClaude SelfCheckProtocol: 4 mandatory questions + 7 hallucination red flags
 * - Existing confidence.js: detectHedging() + detectRationalizations()
 *
 * @module gates/self-check
 */

import { detectHedging, detectRationalizations } from './confidence.js';

// ---------------------------------------------------------------------------
// Hallucination red flags (from SuperClaude)
// ---------------------------------------------------------------------------

/**
 * 7 hallucination red flags — claims made without supporting evidence.
 * Each pattern fires when the text asserts a positive outcome
 * without nearby evidence phrases.
 */
const HALLUCINATION_RED_FLAGS = [
  {
    pattern: /\btests?\s+pass(?:es|ed|ing)?\b/i,
    category: 'claim-without-output',
    description: 'Claims tests pass without showing test output',
  },
  {
    pattern: /\beverything\s+works?\b/i,
    category: 'blanket-success',
    description: 'Blanket "everything works" without specific evidence',
  },
  {
    pattern: /\bimplementation\s+(?:is\s+)?complete\b/i,
    category: 'premature-completion',
    description: 'Claims implementation complete without verification',
  },
  {
    pattern: /\bshould\s+work\s+now\b/i,
    category: 'unverified-fix',
    description: 'Claims fix works without running verification',
  },
  {
    pattern: /\bI'm\s+confident\b/i,
    category: 'false-confidence',
    description: 'Expresses confidence without supporting evidence',
  },
  {
    pattern: /\bjust\s+fixed\s+it\b/i,
    category: 'unverified-fix',
    description: 'Claims fix applied without showing verification',
  },
  {
    pattern: /\bminor\s+issue\b/i,
    category: 'severity-downplay',
    description: 'Downplays issue severity without analysis',
  },
];

// ---------------------------------------------------------------------------
// Evidence phrases — presence near a claim reduces suspicion
// ---------------------------------------------------------------------------

const EVIDENCE_PHRASES = [
  /\b(?:output|result|log|trace)\s*(?:shows?|confirms?|indicates?)\b/i,
  /\bexit\s*code\s*(?:0|zero)\b/i,
  /\b\d+\s+(?:tests?\s+)?pass(?:ed|ing)?\b/i,  // "42 tests passing"
  /\bverified\s+(?:by|via|with|using)\b/i,
  /\bas\s+(?:shown|confirmed|demonstrated)\s+(?:by|above|below)\b/i,
  /\b(?:stdout|stderr|output):\s/i,
  /```[\s\S]{10,}```/,  // code blocks with substantial content
];

/**
 * Check if text contains evidence near a claim.
 * Looks in a 200-char window around the claim position.
 * @param {string} text
 * @param {number} claimIndex — position of the claim in text
 * @returns {boolean}
 */
function hasNearbyEvidence(text, claimIndex) {
  const windowStart = Math.max(0, claimIndex - 200);
  const windowEnd = Math.min(text.length, claimIndex + 200);
  const window = text.slice(windowStart, windowEnd);
  return EVIDENCE_PHRASES.some(p => p.test(window));
}

// ---------------------------------------------------------------------------
// Unverified completion detection
// ---------------------------------------------------------------------------

const COMPLETION_CLAIMS = [
  /\b(?:task|work|implementation|feature|fix)\s+(?:is\s+)?(?:done|complete|finished|ready)\b/i,
  /\bsuccessfully\s+(?:implemented|completed|fixed|resolved)\b/i,
  /\ball\s+(?:tests?\s+)?pass(?:es|ed|ing)?\b/i,
  /\bno\s+(?:errors?|failures?|issues?|problems?)\b/i,
];

/**
 * Detect completion claims without nearby evidence.
 * @param {string} text
 * @returns {Array<{text: string, category: string}>}
 */
function detectUnverifiedClaims(text) {
  const claims = [];
  for (const pattern of COMPLETION_CLAIMS) {
    const match = text.match(pattern);
    if (match && !hasNearbyEvidence(text, match.index)) {
      claims.push({ text: match[0], category: 'unverified-completion' });
    }
  }
  return claims;
}

// ---------------------------------------------------------------------------
// detectHallucinations (exported for direct use)
// ---------------------------------------------------------------------------

/**
 * Detect hallucination red flags in agent output.
 * @param {string} text
 * @returns {{ found: boolean, flags: Array<{text: string, category: string, description: string}> }}
 */
export function detectHallucinations(text) {
  const flags = [];
  for (const { pattern, category, description } of HALLUCINATION_RED_FLAGS) {
    const match = text.match(pattern);
    if (match && !hasNearbyEvidence(text, match.index)) {
      flags.push({ text: match[0], category, description });
    }
  }
  return { found: flags.length > 0, flags };
}

// ---------------------------------------------------------------------------
// SelfChecker
// ---------------------------------------------------------------------------

/**
 * @typedef {Object} SelfCheckFinding
 * @property {'hedging'|'hallucination'|'rationalization'|'unverified'} type
 * @property {string} text — The matched text
 * @property {'warning'|'error'} severity
 * @property {string} category — Specific category within the type
 */

/**
 * @typedef {Object} SelfCheckResult
 * @property {SelfCheckFinding[]} findings
 * @property {number} score — Trust score (0–1), 1.0 = no issues
 * @property {boolean} shouldProceed — false when score < 0.5
 */

export class SelfChecker {
  /**
   * Analyze agent output for quality issues.
   * @param {string} text — Agent output text
   * @returns {SelfCheckResult}
   */
  check(text) {
    if (!text || text.trim().length === 0) {
      return { findings: [], score: 1.0, shouldProceed: true };
    }

    /** @type {SelfCheckFinding[]} */
    const findings = [];

    // 1. Hedging detection (reuse from confidence.js)
    const hedging = detectHedging(text);
    for (const match of hedging.matches) {
      findings.push({
        type: 'hedging',
        text: match,
        severity: 'warning',
        category: 'hedging-language',
      });
    }

    // 2. Rationalization detection (reuse from confidence.js)
    const rationalizations = detectRationalizations(text);
    for (const r of rationalizations.rationalizations) {
      findings.push({
        type: 'rationalization',
        text: r.text,
        severity: 'error',
        category: r.category,
      });
    }

    // 3. Hallucination red flags
    const hallucinations = detectHallucinations(text);
    for (const flag of hallucinations.flags) {
      findings.push({
        type: 'hallucination',
        text: flag.text,
        severity: 'error',
        category: flag.category,
      });
    }

    // 4. Unverified completion claims
    const unverified = detectUnverifiedClaims(text);
    for (const claim of unverified) {
      findings.push({
        type: 'unverified',
        text: claim.text,
        severity: 'warning',
        category: claim.category,
      });
    }

    // Compute trust score
    const score = this._computeScore(findings);

    return {
      findings,
      score,
      shouldProceed: score >= 0.5,
    };
  }

  /**
   * Compute trust score from findings.
   * Errors weigh more than warnings.
   * @private
   */
  _computeScore(findings) {
    if (findings.length === 0) return 1.0;

    let penalty = 0;
    for (const f of findings) {
      penalty += f.severity === 'error' ? 0.15 : 0.08;
    }

    return Math.max(0, +(1 - penalty).toFixed(3));
  }
}
