"""
Data models for error capture with Pydantic ORM (v1 and v2 compatible).
"""

from datetime import datetime, timezone
from enum import Enum
from typing import Dict, Any, Optional, Set, ClassVar, TYPE_CHECKING
from uuid import UUID, uuid4
from time import time

# Use compatibility layer instead of direct pydantic imports
from .pydantic_compat import (
    PYDANTIC_V2,
    Field,
    field_validator,
    model_validator,
    get_model_dump,
)

# Import ConfigDict at module level for v2, but only use it conditionally
if PYDANTIC_V2:
    from pydantic import BaseModel, ConfigDict
else:
    from pydantic import BaseModel


class Severity(str, Enum):
    """How severe/critical the event is"""
    CRITICAL = "critical"
    HIGH = "high"
    MEDIUM = "medium"
    LOW = "low"
    WARNING = "warning"


class EventType(str, Enum):
    """The type/category of event"""
    ERROR = "error"
    WARNING = "warning"
    INFO = "info"
    SUCCESS = "success"


class PipelineComponent(str, Enum):
    """Pipeline component types - flexible for future use"""
    # Message Brokers
    KAFKA_PRODUCER = "kafka_producer"
    KAFKA_CONSUMER = "kafka_consumer"
    RABBITMQ = "rabbitmq"
    BEANSTALK = "beanstalk"

    # Databases & Storage
    ELASTICSEARCH = "elasticsearch"
    POSTGRES = "postgres"
    MONGODB = "mongodb"
    REDIS = "redis"
    S3 = "s3"
    MEMGRAPH = "memgraph"
    NEBULA = "nebula"
    QDRANT = "qdrant"
    DATABASE = "database"

    # Processing & Transformation
    TRANSFORMATION = "transformation"
    DBT = "dbt"
    PANDAS = "pandas"

    # Orchestration
    AIRFLOW = "airflow"

    # API & Services
    API = "api"
    WEBHOOK = "webhook"

    # Generic/Custom
    CUSTOM = "custom"
    UNKNOWN = "unknown"


# Create base class conditionally
if PYDANTIC_V2:
    class _ErrorEventBase(BaseModel):
        """Base class with v2 config"""
        model_config = ConfigDict(
            json_encoders={
                datetime: lambda v: v.isoformat(),
                UUID: lambda v: str(v),
            },
            use_enum_values=True,
        )
else:
    class _ErrorEventBase(BaseModel):
        """Base class with v1 config"""
        class Config:
            json_encoders = {
                datetime: lambda v: v.isoformat(),
                UUID: lambda v: str(v),
            }
            use_enum_values = True


class ErrorEvent(_ErrorEventBase):
    """Standardized error event model"""
    id: UUID = Field(default_factory=uuid4)
    event_type: EventType = EventType.ERROR

    # Pipeline & Engine Identification
    pipeline_name: Optional[str] = None
    engine_name: Optional[str] = None

    # Component & Severity
    component: Optional[PipelineComponent] = PipelineComponent.UNKNOWN
    component_name: Optional[str] = None
    severity: Severity = Severity.MEDIUM

    # Message & Datetime
    message: str
    timestamp: float = Field(default_factory=time)
    timestamp_iso: str = Field(
        default_factory=lambda: datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
    )

    # Error details
    error_type: str
    stack_trace: Optional[str] = None

    # Context
    function_name: Optional[str] = None
    file_path: Optional[str] = None
    line_number: Optional[int] = None
    metadata: Dict[str, Any] = Field(default_factory=dict)
    details: Optional[Dict[str, Any]] = Field(default_factory=dict)

    # Status & Resolution
    resolved: bool = False
    resolved_at: Optional[float] = None
    resolved_by: Optional[str] = None
    resolution_notes: Optional[str] = None

    # Use ClassVar to indicate this is a class variable
    _id_registry: ClassVar[Set[UUID]] = set()

    # Version-specific initialization
    if PYDANTIC_V2:
        def model_post_init(self, __context: Any) -> None:
            """Called after model initialization - register the ID (v2)"""
            super().model_post_init(__context)
            self.__class__._id_registry.add(self.id)
    else:
        def __init__(self, **data):
            """Override __init__ for v1 to register ID"""
            super().__init__(**data)
            self.__class__._id_registry.add(self.id)

    def mark_resolved(self, resolved_by: str, notes: Optional[str] = None) -> None:
        """Mark the error as resolved"""
        self.resolved = True
        self.resolved_at = time()
        self.resolved_by = resolved_by
        self.resolution_notes = notes

    @classmethod
    def clear_id_registry(cls) -> None:
        """Clear the ID registry (useful for testing or cleanup)"""
        cls._id_registry.clear()

    @classmethod
    def get_registered_ids(cls) -> Set[UUID]:
        """Get all registered IDs"""
        return cls._id_registry.copy()


# Create base class for CaptureConfig conditionally
if PYDANTIC_V2:
    class _CaptureConfigBase(BaseModel):
        """Base class with v2 config"""
        model_config = ConfigDict(use_enum_values=True)
else:
    class _CaptureConfigBase(BaseModel):
        """Base class with v1 config"""
        class Config:
            use_enum_values = True


class CaptureConfig(_CaptureConfigBase):
    """Configuration for error capture"""
    component: Optional[PipelineComponent] = None
    component_name: Optional[str] = None
    severity: Severity = Severity.HIGH
    event_type: EventType = EventType.ERROR

    # Pipeline context
    pipeline_name: Optional[str] = None
    engine_name: Optional[str] = None

    # Behavior
    send_to_transport: bool = True
    reraise: bool = True
    log_to_console: bool = True

    # Additional context
    metadata: Dict[str, Any] = Field(default_factory=dict)
    tags: list[str] = Field(default_factory=list)

    # Filtering
    ignore_error_types: list[str] = Field(default_factory=list)