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

from typing import List, Dict, Optional, Any, Union
from enum import Enum
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) 