"""
Validation engine for LogicPwn Exploit Engine.
- Validates response content, status, headers, and session state
- Extensible and interoperable with core models
"""
from typing import Any, Dict
from logicpwn.core.utils.utils import check_indicators
from .models import ExploitStep, ValidationResult

try:
    from jsonpath_ng import parse as jsonpath_parse
except ImportError:
    jsonpath_parse = None

def eval_python_expr(expr: str, response: Any, session_state: dict) -> bool:
    try:
        return bool(eval(expr, {"response": response, "session": session_state}))
    except Exception:
        return False

def eval_jsonpath(expr: str, json_body: dict) -> bool:
    if not jsonpath_parse or not json_body:
        return False
    try:
        jsonpath_expr = jsonpath_parse(expr)
        matches = [match.value for match in jsonpath_expr.find(json_body)]
        return bool(matches)
    except Exception:
        return False

def validate_step_success(
    response: Any,
    step: ExploitStep,
    session_state: Dict[str, Any]
) -> ValidationResult:
    """
    Validate if exploit step succeeded based on response.
    Checks:
    - HTTP status codes
    - Response headers
    - Body content patterns
    - JSON field values
    - Session state changes
    """
    reasons = []
    matched, failed = [], []
    body = getattr(response, 'text', None)
    if hasattr(response, 'json'):
        try:
            json_body = response.json()
        except Exception:
            json_body = None
    else:
        json_body = None
    # Success indicators
    is_valid, matched = check_indicators(body or '', step.success_indicators, "success")
    # Failure indicators
    is_fail, failed = check_indicators(body or '', step.failure_indicators, "failure")
    # Status code check
    status_code = getattr(response, 'status_code', None)
    # Advanced validation
    for ind in step.success_indicators:
        if ind.startswith("status_code =="):
            try:
                code = int(ind.split("==")[1].strip())
                if status_code == code:
                    is_valid = True
                    matched.append(ind)
            except Exception:
                pass
        elif ind.startswith("status_code !="):
            try:
                code = int(ind.split("!=")[1].strip())
                if status_code != code:
                    is_valid = True
                    matched.append(ind)
            except Exception:
                pass
        elif ind.startswith("py:"):
            expr = ind[3:]
            if eval_python_expr(expr, response, session_state):
                is_valid = True
                matched.append(ind)
        elif ind.startswith("jsonpath:") and json_body:
            expr = ind[9:]
            if eval_jsonpath(expr, json_body):
                is_valid = True
                matched.append(ind)
        elif ind.startswith("session:"):
            expr = ind[8:]
            try:
                if eval(expr, {"session": session_state}):
                    is_valid = True
                    matched.append(ind)
            except Exception:
                pass
        elif ind.endswith("in response.json") and json_body:
            field = ind.split("in response.json")[0].strip()
            if field in json_body:
                is_valid = True
                matched.append(ind)
    # Failure indicators override
    if is_fail:
        is_valid = False
        reasons.extend([f"Failure indicator matched: {f}" for f in failed])
    if not is_valid:
        reasons.append("No success indicators matched.")
    return ValidationResult(
        is_valid=is_valid,
        reasons=reasons,
        matched_indicators=matched,
        failed_indicators=failed,
        extracted_data={}
    ) 