"""
Data models for the Exploit Automation Engine (LogicPwn)
- Reuses RequestConfig from logicpwn.models.request_config
- Designed for extensibility, maintainability, and interoperability
"""

from enum import Enum
from typing import Any, Optional

from pydantic import BaseModel, Field

from logicpwn.models.request_config import RequestConfig


class ExploitStatus(str, Enum):
    PENDING = "pending"
    RUNNING = "running"
    SUCCESS = "success"
    FAILED = "failed"
    ABORTED = "aborted"


class PayloadType(str, Enum):
    STATIC = "static"
    DYNAMIC = "dynamic"
    TEMPLATE = "template"
    RANDOM = "random"
    FUZZ = "fuzz"


class PayloadInjectionPoint(BaseModel):
    location: str  # "url", "headers", "body", "json_path", "form_data"
    target_field: str
    payload_type: PayloadType
    payload_value: Optional[str] = None  # Allow None for dynamic payloads
    encoding: Optional[str] = None  # "url", "base64", "html"

    def model_post_init(self, __context) -> None:
        """Validate payload configuration after initialization."""
        self._validate_payload_config()

    def _validate_payload_config(self) -> None:
        """Validate payload injection point configuration."""
        # Validate location
        valid_locations = ["url", "headers", "body", "json_path", "form_data"]
        if self.location not in valid_locations:
            raise ValueError(
                f"location must be one of {valid_locations}, got {self.location}"
            )

        # Validate target_field
        if not self.target_field or not self.target_field.strip():
            raise ValueError("target_field cannot be empty")

        # Validate payload_value for static payloads
        if self.payload_type == PayloadType.STATIC and self.payload_value is None:
            raise ValueError("payload_value is required for STATIC payload type")

        # Validate encoding
        if self.encoding is not None:
            valid_encodings = ["url", "base64", "html"]
            if self.encoding not in valid_encodings:
                raise ValueError(
                    f"encoding must be one of {valid_encodings}, got {self.encoding}"
                )

        # Validate JSON path syntax
        if self.location == "json_path" and self.target_field:
            if not self._is_valid_json_path(self.target_field):
                raise ValueError(f"Invalid JSON path syntax: {self.target_field}")

    def _is_valid_json_path(self, path: str) -> bool:
        """Validate JSON path syntax."""
        # Basic validation - no empty segments, valid characters
        if not path or ".." in path or path.startswith(".") or path.endswith("."):
            return False

        # Check for valid path segments
        segments = path.split(".")
        for segment in segments:
            if not segment or not segment.replace("_", "").replace("-", "").isalnum():
                return False

        return True


class ExploitStep(BaseModel):
    name: str
    description: str
    request_config: RequestConfig
    success_indicators: list[str]
    failure_indicators: list[str] = []
    required_session_state: Optional[dict[str, Any]] = None
    payload_injection_points: Optional[list[PayloadInjectionPoint]] = None
    delay_after_step: Optional[float] = 0.0
    retry_count: int = 1
    critical: bool = False


class ExploitChain(BaseModel):
    name: str
    description: str
    steps: list[ExploitStep]
    session_state: dict[str, Any] = Field(default_factory=dict)
    execution_log: list[Any] = Field(default_factory=list)


class ValidationResult(BaseModel):
    is_valid: bool
    reasons: list[str] = []
    matched_indicators: list[str] = []
    failed_indicators: list[str] = []
    extracted_data: dict[str, Any] = Field(default_factory=dict)


class ExploitResult(BaseModel):
    step_name: str
    status: ExploitStatus
    response: Optional[Any] = None  # Should be requests.Response or RequestResult
    validation_result: Optional[ValidationResult] = None
    error_message: Optional[str] = None
    execution_time: float = 0.0
    extracted_data: dict[str, Any] = Field(default_factory=dict)
