"""
Pydantic Models for CMDB - ips/sensor

Runtime validation models for ips/sensor configuration.
Generated from FortiOS schema version unknown.
"""

from __future__ import annotations

from pydantic import BaseModel, Field, field_validator
from typing import Any, Literal, Optional
from enum import Enum

# ============================================================================
# Enum Definitions for Child Table Fields (for fields with 4+ allowed values)
# ============================================================================

class SensorEntriesActionEnum(str, Enum):
    """Allowed values for action field in entries."""
    PASS = "pass"
    BLOCK = "block"
    RESET = "reset"
    DEFAULT = "default"

class SensorEntriesRateTrackEnum(str, Enum):
    """Allowed values for rate_track field in entries."""
    NONE = "none"
    SRC_IP = "src-ip"
    DEST_IP = "dest-ip"
    DHCP_CLIENT_MAC = "dhcp-client-mac"
    DNS_DOMAIN = "dns-domain"

# ============================================================================
# Child Table Models (sorted deepest-first so nested models are defined before their parents)
# ============================================================================

class SensorEntriesVulnType(BaseModel):
    """
    Child table model for entries.vuln-type.
    
    List of signature vulnerability types to filter by.
    """
    
    class Config:
        """Pydantic model configuration."""
        extra = "allow"  # Allow additional fields from API
        str_strip_whitespace = True
        use_enum_values = True  # Use enum values instead of names
    
    id_: int = Field(ge=0, le=4294967295, default=0, serialization_alias="id", description="Vulnerability type ID.")
class SensorEntriesRule(BaseModel):
    """
    Child table model for entries.rule.
    
    Identifies the predefined or custom IPS signatures to add to the sensor.
    """
    
    class Config:
        """Pydantic model configuration."""
        extra = "allow"  # Allow additional fields from API
        str_strip_whitespace = True
        use_enum_values = True  # Use enum values instead of names
    
    id_: int = Field(ge=0, le=4294967295, default=0, serialization_alias="id", description="Rule IPS.")
class SensorEntriesExemptIp(BaseModel):
    """
    Child table model for entries.exempt-ip.
    
    Traffic from selected source or destination IP addresses is exempt from this signature.
    """
    
    class Config:
        """Pydantic model configuration."""
        extra = "allow"  # Allow additional fields from API
        str_strip_whitespace = True
        use_enum_values = True  # Use enum values instead of names
    
    id_: int | None = Field(ge=0, le=4294967295, default=0, serialization_alias="id", description="Exempt IP ID.")    
    src_ip: str | None = Field(default="0.0.0.0 0.0.0.0", description="Source IP address and netmask (applies to packet matching the signature).")    
    dst_ip: str | None = Field(default="0.0.0.0 0.0.0.0", description="Destination IP address and netmask (applies to packet matching the signature).")
class SensorEntriesCve(BaseModel):
    """
    Child table model for entries.cve.
    
    List of CVE IDs of the signatures to add to the sensor.
    """
    
    class Config:
        """Pydantic model configuration."""
        extra = "allow"  # Allow additional fields from API
        str_strip_whitespace = True
        use_enum_values = True  # Use enum values instead of names
    
    cve_entry: str = Field(max_length=19, description="CVE IDs or CVE wildcards.")
class SensorEntries(BaseModel):
    """
    Child table model for entries.
    
    IPS sensor filter.
    """
    
    class Config:
        """Pydantic model configuration."""
        extra = "allow"  # Allow additional fields from API
        str_strip_whitespace = True
        use_enum_values = True  # Use enum values instead of names
    
    id_: int | None = Field(ge=0, le=4294967295, default=0, serialization_alias="id", description="Rule ID in IPS database (0 - 4294967295).")    
    rule: list[SensorEntriesRule] = Field(default_factory=list, description="Identifies the predefined or custom IPS signatures to add to the sensor.")    
    location: list[str] = Field(default_factory=list, description="Protect client or server traffic.")    
    severity: list[str] = Field(default_factory=list, description="Relative severity of the signature, from info to critical. Log messages generated by the signature include the severity.")    
    protocol: list[str] = Field(default_factory=list, description="Protocols to be examined. Use all for every protocol and other for unlisted protocols.")    
    os: list[str] = Field(default_factory=list, description="Operating systems to be protected. Use all for every operating system and other for unlisted operating systems.")    
    application: list[str] = Field(default_factory=list, description="Operating systems to be protected. Use all for every application and other for unlisted application.")    
    default_action: Literal["all", "pass", "block"] | None = Field(default="all", description="Signature default action filter.")    
    default_status: Literal["all", "enable", "disable"] | None = Field(default="all", description="Signature default status filter.")    
    cve: list[SensorEntriesCve] = Field(default_factory=list, description="List of CVE IDs of the signatures to add to the sensor.")    
    vuln_type: list[SensorEntriesVulnType] = Field(default_factory=list, description="List of signature vulnerability types to filter by.")    
    last_modified: str | None = Field(default=None, description="Filter by signature last modified date. Formats: before <date>, after <date>, between <start-date> <end-date>.")    
    status: Literal["disable", "enable", "default"] | None = Field(default="default", description="Status of the signatures included in filter. Only those filters with a status to enable are used.")    
    log: Literal["disable", "enable"] | None = Field(default="enable", description="Enable/disable logging of signatures included in filter.")    
    log_packet: Literal["disable", "enable"] | None = Field(default="disable", description="Enable/disable packet logging. Enable to save the packet that triggers the filter. You can download the packets in pcap format for diagnostic use.")    
    log_attack_context: Literal["disable", "enable"] | None = Field(default="disable", description="Enable/disable logging of attack context: URL buffer, header buffer, body buffer, packet buffer.")    
    action: SensorEntriesActionEnum | None = Field(default=SensorEntriesActionEnum.DEFAULT, description="Action taken with traffic in which signatures are detected.")    
    rate_count: int | None = Field(ge=0, le=65535, default=0, description="Count of the rate.")    
    rate_duration: int | None = Field(ge=1, le=65535, default=60, description="Duration (sec) of the rate.")    
    rate_mode: Literal["periodical", "continuous"] | None = Field(default="continuous", description="Rate limit mode.")    
    rate_track: SensorEntriesRateTrackEnum | None = Field(default=SensorEntriesRateTrackEnum.NONE, description="Track the packet protocol field.")    
    exempt_ip: list[SensorEntriesExemptIp] = Field(default_factory=list, description="Traffic from selected source or destination IP addresses is exempt from this signature.")    
    quarantine: Literal["none", "attacker"] | None = Field(default="none", description="Quarantine method.")    
    quarantine_expiry: str | None = Field(default="5m", description="Duration of quarantine. (Format ###d##h##m, minimum 1m, maximum 364d23h59m, default = 5m). Requires quarantine set to attacker.")    
    quarantine_log: Literal["disable", "enable"] | None = Field(default="enable", description="Enable/disable quarantine logging.")
# ============================================================================
# Enum Definitions (for fields with 4+ allowed values)
# ============================================================================


# ============================================================================
# Main Model
# ============================================================================

class SensorModel(BaseModel):
    """
    Pydantic model for ips/sensor configuration.
    
    Configure IPS sensor.
    
    Validation Rules:        - name: max_length=47 pattern=        - comment: max_length=255 pattern=        - replacemsg_group: max_length=35 pattern=        - block_malicious_url: pattern=        - scan_botnet_connections: pattern=        - extended_log: pattern=        - entries: pattern=    """
    
    class Config:
        """Pydantic model configuration."""
        extra = "allow"  # Allow additional fields from API
        str_strip_whitespace = True
        validate_assignment = True  # Validate on attribute assignment
        use_enum_values = True  # Use enum values instead of names
    
    # ========================================================================
    # Model Fields
    # ========================================================================
    
    name: str = Field(max_length=47, description="Sensor name.")    
    comment: str | None = Field(max_length=255, default=None, description="Comment.")    
    replacemsg_group: str | None = Field(max_length=35, default=None, description="Replacement message group.")  # datasource: ['system.replacemsg-group.name']    
    block_malicious_url: Literal["disable", "enable"] | None = Field(default="disable", description="Enable/disable malicious URL blocking.")    
    scan_botnet_connections: Literal["disable", "block", "monitor"] | None = Field(default="disable", description="Block or monitor connections to Botnet servers, or disable Botnet scanning.")    
    extended_log: Literal["enable", "disable"] | None = Field(default="disable", description="Enable/disable extended logging.")    
    entries: list[SensorEntries] = Field(default_factory=list, description="IPS sensor filter.")    
    # ========================================================================
    # Custom Validators
    # ========================================================================
    
    @field_validator('replacemsg_group')
    @classmethod
    def validate_replacemsg_group(cls, v: Any) -> Any:
        """
        Validate replacemsg_group field.
        
        Datasource: ['system.replacemsg-group.name']
        
        Note:
            This validator only checks basic constraints.
            To validate that referenced object exists, query the API.
        """
        # Basic validation passed via Field() constraints
        # Additional datasource validation could be added here
        return v    
    # ========================================================================
    # Helper Methods
    # ========================================================================
    
    def to_fortios_dict(self) -> dict[str, Any]:
        """
        Convert model to FortiOS API payload format.
        
        Returns:
            Dict suitable for POST/PUT operations
        """
        # Export with exclude_none to avoid sending null values
        return self.model_dump(exclude_none=True, by_alias=True)
    
    @classmethod
    def from_fortios_response(cls, data: dict[str, Any]) -> "SensorModel":
        """
        Create model instance from FortiOS API response.
        
        Args:
            data: Response data from API
            
        Returns:
            Validated model instance
        """
        return cls(**data)
    # ========================================================================
    # Datasource Validation Methods
    # ========================================================================    
    async def validate_replacemsg_group_references(self, client: Any) -> list[str]:
        """
        Validate replacemsg_group references exist in FortiGate.
        
        This method checks if referenced objects exist by calling exists() on
        the appropriate API endpoints. This is an OPTIONAL validation step that
        can be called before posting to the API to catch reference errors early.
        
        Datasource endpoints checked:
        - system/replacemsg-group        
        Args:
            client: FortiOS client instance (from fgt._client)
            
        Returns:
            List of validation error messages (empty if all valid)
            
        Example:
            >>> from hfortix_fortios import FortiOS
            >>> 
            >>> fgt = FortiOS(host="192.168.1.1", token="your-token")
            >>> policy = SensorModel(
            ...     replacemsg_group="invalid-name",
            ... )
            >>> 
            >>> # Validate before posting
            >>> errors = await policy.validate_replacemsg_group_references(fgt._client)
            >>> if errors:
            ...     print("Validation failed:", errors)
            ... else:
            ...     result = await fgt.api.cmdb.ips.sensor.post(policy.to_fortios_dict())
        """
        errors: list[str] = []
        
        # Validate scalar field
        value = getattr(self, "replacemsg_group", None)
        if not value:
            return errors
        
        # Check all datasource endpoints
        found = False
        if await client.api.cmdb.system.replacemsg_group.exists(value):
            found = True
        
        if not found:
            errors.append(
                f"Replacemsg-Group '{value}' not found in "
                "system/replacemsg-group"
            )        
        return errors    
    async def validate_all_references(self, client: Any) -> list[str]:
        """
        Validate ALL datasource references in this model.
        
        Convenience method that runs all validate_*_references() methods
        and aggregates the results.
        
        Args:
            client: FortiOS client instance (from fgt._client)
            
        Returns:
            List of all validation errors found
            
        Example:
            >>> errors = await policy.validate_all_references(fgt._client)
            >>> if errors:
            ...     for error in errors:
            ...         print(f"  - {error}")
        """
        all_errors = []
        
        errors = await self.validate_replacemsg_group_references(client)
        all_errors.extend(errors)        
        return all_errors

# ============================================================================
# Type Aliases for Convenience
# ============================================================================

Dict = dict[str, Any]  # For backward compatibility

# ============================================================================
# Module Exports
# ============================================================================

__all__ = [
    "SensorModel",    "SensorEntries",    "SensorEntries.Rule",    "SensorEntries.Cve",    "SensorEntries.VulnType",    "SensorEntries.ExemptIp",]


# ============================================================================
# Generated by hfortix generator v0.6.0
# Schema: 1.7.2
# Generated: 2026-01-21T16:23:59.837624Z
# ============================================================================