from enum import Enum
from typing import Any, Optional

from pydantic import model_validator

from galtea.utils.case_insensitive_enum import CaseInsensitiveEnumMixin
from galtea.utils.from_camel_case_base_model import FromCamelCaseBaseModel
from galtea.utils.input_normalization import coerce_to_string, unwrap_context


class SessionStatus(CaseInsensitiveEnumMixin, str, Enum):
    PENDING = "PENDING"
    COMPLETED = "COMPLETED"
    FAILED = "FAILED"


class SessionBase(FromCamelCaseBaseModel):
    """Base model for creating a new session.

    The ``context`` field is stored as a JSON object in the API (e.g. ``{"value": "..."}``).
    The SDK auto-unwraps it to a string for backward compatibility. Use ``context_data``
    on ``Session`` to access the raw JSON object.
    """

    custom_id: Optional[str] = (
        None  # client-provided ID to associate galtea session with the user's application session
    )
    version_id: Optional[str] = None
    test_case_id: Optional[str] = None
    context: Optional[str] = None  # flexible string context for user-defined information
    metadata: Optional[dict[str, Any]] = None  # stores open fields from endpoint responses
    status: Optional[SessionStatus] = None

    @model_validator(mode="before")
    @classmethod
    def _unwrap_json_context(cls, data: Any) -> Any:
        """Auto-unwrap JSON context from the API into a backward-compatible string."""
        if not isinstance(data, dict):
            return data

        raw_context = data.get("context")
        if isinstance(raw_context, dict):
            data["context_data"] = raw_context
            data["context"] = coerce_to_string(unwrap_context(raw_context))

        return data


class Session(SessionBase):
    """Complete session model returned from the API."""

    id: str
    created_at: str
    deleted_at: Optional[str] = None
    stopping_reason: Optional[str] = None
    error: Optional[str] = None  # stores error message if session failed

    # Raw JSON object from the API (for structured context access)
    context_data: Optional[dict[str, Any]] = None

    def is_test_based(self) -> bool:
        """Check if this session is test-based (has a test_case_id)."""
        return self.test_case_id is not None

    def is_monitoring(self) -> bool:
        """Check if this session is for production monitoring."""
        return self.test_case_id is None


class SessionUpdate(FromCamelCaseBaseModel):
    """Model for updating sessions with all optional fields (PATCH operation).

    Fields are Optional (defaulting to None) so they can be excluded from serialization
    if not set (via exclude_unset=True in the service).
    """

    id: Optional[str] = None
    created_at: Optional[str] = None
    deleted_at: Optional[str] = None
    stopping_reason: Optional[str] = None
    error: Optional[str] = None
    custom_id: Optional[str] = None
    version_id: Optional[str] = None
    test_case_id: Optional[str] = None
    context: Optional[str] = None
    metadata: Optional[dict[str, Any]] = None
    status: Optional[SessionStatus] = None

    @model_validator(mode="before")
    @classmethod
    def _unwrap_json_context(cls, data: Any) -> Any:
        """Auto-unwrap JSON context if passed as a dict."""
        if not isinstance(data, dict):
            return data

        raw_context = data.get("context")
        if isinstance(raw_context, dict):
            data["context"] = coerce_to_string(unwrap_context(raw_context))

        return data
