"""
AI Agent Safety Constraints — Verification for Autonomous Agent Actions
Prevents hallucination-driven actions, validates tool calls, gates external effects.
"""

from pramana.core.engine import Pramana, VerificationResult
from pramana.core.nyaya import Evidence, PramanaType
from pramana.core.arthashastra import StrategicContext
from pramana.core.vedic import SutraResult, Sutra


# Action risk tiers
ACTION_TIERS = {
    "read": {"risk": 0.05, "requires_evidence": False, "reversible": True},
    "search": {"risk": 0.05, "requires_evidence": False, "reversible": True},
    "generate": {"risk": 0.3, "requires_evidence": True, "reversible": True},
    "write_file": {"risk": 0.4, "requires_evidence": True, "reversible": True},
    "api_call": {"risk": 0.5, "requires_evidence": True, "reversible": False},
    "send_message": {"risk": 0.6, "requires_evidence": True, "reversible": False},
    "publish": {"risk": 0.7, "requires_evidence": True, "reversible": False},
    "delete": {"risk": 0.8, "requires_evidence": True, "reversible": False},
    "payment": {"risk": 0.9, "requires_evidence": True, "reversible": False},
    "deploy": {"risk": 0.8, "requires_evidence": True, "reversible": False},
}


def verify_agent_action(action_type: str,
                        action_params: dict,
                        agent_context: dict = None,
                        evidence: list = None) -> VerificationResult:
    """
    Verify an AI agent's proposed action before execution.

    Args:
        action_type: Type of action (read, write, api_call, publish, etc.)
        action_params: Parameters the agent wants to pass
        agent_context: Current agent state (task, history, permissions)
        evidence: Evidence supporting why this action should happen

    Returns:
        VerificationResult — VERIFIED/FLAGGED/HALTED

    Usage:
        result = verify_agent_action(
            action_type="send_message",
            action_params={"to": "client@email.com", "body": "..."},
            agent_context={"task": "follow-up", "approved": True},
            evidence=[Evidence(value="client requested callback",
                              pramana_type=PramanaType.SHABDA,
                              source="crm_notes")]
        )
        if result.verdict == "VERIFIED":
            execute_action()
    """
    engine = Pramana()
    agent_context = agent_context or {}

    tier = ACTION_TIERS.get(action_type, ACTION_TIERS.get("api_call"))

    # Build evidence list
    all_evidence = list(evidence) if evidence else []

    # If action requires evidence but none provided, flag it
    if tier["requires_evidence"] and not all_evidence:
        all_evidence.append(Evidence(
            value=None,
            pramana_type=PramanaType.UPAMANA,
            source="no_evidence_provided",
            reliability=0.1
        ))

    # Build strategic context
    context = StrategicContext(
        task_type=action_type,
        description=str(action_params),
        risk_level=_risk_to_level(tier["risk"]),
        reversible=tier["reversible"],
        cost_usd=action_params.get("cost_usd", 0),
    )

    # Build constraint data
    data = {**action_params}
    constraints = {}

    # Check required params per action type
    required_params = _get_required_params(action_type)
    if required_params:
        constraints["required_fields"] = required_params

    # Run full pipeline
    result = engine.verify(
        claim=f"Agent action: {action_type}",
        evidence=all_evidence,
        constraints=constraints,
        data=data,
        context=context,
        layers=["nyaya", "vedic", "strategy"]
    )

    return result


def verify_llm_output(output: str,
                      expected_format: str = None,
                      max_length: int = None,
                      required_fields: list = None,
                      banned_patterns: list = None) -> VerificationResult:
    """
    Verify an LLM's output before using it downstream.

    Checks: format compliance, length, completeness, banned content.

    Usage:
        result = verify_llm_output(
            output=llm_response,
            expected_format="json",
            max_length=1000,
            required_fields=["name", "email", "score"],
            banned_patterns=["password", "ssn", "credit_card"]
        )
    """
    import json as json_lib

    results = []

    # Length check
    if max_length:
        passed = len(output) <= max_length
        results.append(SutraResult(
            sutra=Sutra.YAVADUNAM.value,
            passed=passed,
            score=min(1.0, max_length / max(len(output), 1)),
            detail=f"Length: {len(output)} (max: {max_length})",
            violations=[] if passed else [
                f"Output length {len(output)} exceeds max {max_length}"
            ]
        ))

    # Format check
    if expected_format == "json":
        try:
            parsed = json_lib.loads(output)
            results.append(SutraResult(
                sutra=Sutra.PURANAPURANA.value,
                passed=True, score=1.0,
                detail="Valid JSON format"
            ))

            # Check required fields in parsed JSON
            if required_fields:
              if not isinstance(parsed, dict):
                results.append(SutraResult(
                    sutra=Sutra.PURANAPURANA.value,
                    passed=False, score=0.0,
                    detail="JSON is not an object — cannot check required fields",
                    violations=["Expected JSON object, got array or primitive"]
                ))
              else:
                missing = [f for f in required_fields if f not in parsed]
                results.append(SutraResult(
                    sutra=Sutra.PURANAPURANA.value,
                    passed=len(missing) == 0,
                    score=1.0 - len(missing) / max(len(required_fields), 1),
                    detail=f"Required fields: {len(required_fields) - len(missing)}"
                           f"/{len(required_fields)}",
                    violations=[f"Missing field: {f}" for f in missing]
                ))
        except (json_lib.JSONDecodeError, ValueError):
            results.append(SutraResult(
                sutra=Sutra.PURANAPURANA.value,
                passed=False, score=0.0,
                detail="Expected JSON format, got invalid JSON",
                violations=["Output is not valid JSON"]
            ))

    # Banned patterns check
    if banned_patterns:
        found = [p for p in banned_patterns if p.lower() in output.lower()]
        results.append(SutraResult(
            sutra=Sutra.NIKHILAM.value,
            passed=len(found) == 0,
            score=1.0 - len(found) / max(len(banned_patterns), 1),
            detail=f"Banned pattern check: {len(banned_patterns)} patterns",
            violations=[f"Banned pattern found: '{p}'" for p in found]
        ))

    # Empty/nonsense check
    stripped = output.strip()
    if not stripped:
        results.append(SutraResult(
            sutra=Sutra.PURANAPURANA.value,
            passed=False, score=0.0,
            detail="Output is empty",
            violations=["LLM returned empty output"]
        ))

    # Compute aggregate
    if results:
        avg_score = sum(r.score for r in results) / len(results)
        all_passed = all(r.passed for r in results)
        all_violations = []
        for r in results:
            all_violations.extend(r.violations)

        if all_passed:
            verdict = "VERIFIED"
        elif any(r.score < 0.3 for r in results):
            verdict = "HALTED"
        else:
            verdict = "FLAGGED"

        return VerificationResult(
            verdict=verdict,
            confidence=round(avg_score, 4),
            reasoning=[f"LLM output check: {len(results)} tests, "
                       f"{sum(1 for r in results if r.passed)} passed, "
                       f"{len(all_violations)} violations"],
            metadata={"violations": all_violations, "tests": len(results)}
        )

    return VerificationResult(
        verdict="FLAGGED",
        confidence=0.5,
        reasoning=["No verification tests applied"]
    )


def _risk_to_level(risk: float) -> str:
    if risk < 0.2:
        return "low"
    elif risk < 0.5:
        return "medium"
    elif risk < 0.8:
        return "high"
    return "critical"


def _get_required_params(action_type: str) -> list:
    required = {
        "send_message": ["to", "body"],
        "api_call": ["url", "method"],
        "publish": ["platform", "content"],
        "payment": ["amount", "recipient", "currency"],
        "delete": ["target", "confirmation"],
        "deploy": ["service", "version"],
    }
    return required.get(action_type, [])
