import re
from typing import List, Dict, Any

# --- Audit Constants ---
AUDIT_ISSUE = "irac.issue"
AUDIT_RULE = "irac.rule"
AUDIT_APP = "irac.application"
AUDIT_CONCL = "irac.conclusion"

class InvalidElementsConfigError(ValueError):
    """Raised when required_elements is misconfigured."""

class InvalidPlanInputError(TypeError):
    """Raised when verify_autonomous_path receives a non-string plan."""

class SelfInitiatedCoTGuard:
    """
    Verifies Self-Initiated Chain-of-Thought (S-CoT) reasoning.

    Avoids 'cognitive interference' of rigid prompts by allowing advanced reasoning models
    (e.g., DeepSeek-R1, o1-mini) to structure their own logic paths, while deterministically
    verifying that the agent's generated path covers all necessary domain checkpoints before
    executing the actual analysis.
    """

    def __init__(self, required_elements: List[str]):
        """
        Args:
            required_elements: A list of milestones/nodes that MUST be present
                in the AI's autonomously generated reasoning plan.
        """
        if not required_elements:
            raise InvalidElementsConfigError("required_elements must be a non-empty list of strings.")
        if not all(isinstance(e, str) for e in required_elements):
            raise InvalidElementsConfigError("All required_elements must be strings.")
        if not all(len(e) > 0 for e in required_elements):
            raise InvalidElementsConfigError("All required_elements must be non-empty strings.")
        self.required_elements = required_elements
        self._compiled_patterns = [
            re.compile(
                rf"(?i)(?<!\w){re.escape(element)}(?!\w)"
                if element[0].isalnum() or element[0] == '_' else
                rf"(?i){re.escape(element)}"
            )
            for element in required_elements
        ]

    def verify_autonomous_path(self, generated_reasoning_plan: str) -> Dict[str, Any]:
        """
        Validates the structure of the agent's autonomously generated plan.

        Args:
            generated_reasoning_plan: The reasoning path/plan generated by the AI
                prior to deep analysis.

        Returns:
            IRAC-compliant verification result dict.
        """
        if not isinstance(generated_reasoning_plan, str):
            raise InvalidPlanInputError("generated_reasoning_plan must be a string")

        missing_elements = []
        for element, pattern in zip(self.required_elements, self._compiled_patterns, strict=True):
            if not pattern.search(generated_reasoning_plan):
                missing_elements.append(element)

        _rule = "The autonomous reasoning path must contain all required domain elements."

        if missing_elements:
            return {
                "verified": False,
                "risk": "INCOMPLETE_REASONING_FRAMEWORK",
                "missing_elements": missing_elements,
                "message": (
                    f"Agent's self-initiated plan missed {len(missing_elements)} "
                    f"critical element(s): {missing_elements}. Rejecting execution."
                ),
                AUDIT_ISSUE: "INCOMPLETE_REASONING_FRAMEWORK",
                AUDIT_RULE: _rule,
                AUDIT_APP: f"Self-initiated plan failed to include: {', '.join(missing_elements)}.",
                AUDIT_CONCL: "Blocked: reasoning path is incomplete.",
            }

        return {
            "verified": True,
            "message": "Autonomous reasoning path contains all required elements. Proceed with analysis.",
            AUDIT_ISSUE: "REASONING_FRAMEWORK_VERIFIED",
            AUDIT_RULE: _rule,
            AUDIT_APP: f"All {len(self.required_elements)} required element(s) were found in the plan.",
            AUDIT_CONCL: "Verified: reasoning path is complete.",
        }
