# #region TaskManagerModels [C:5] [TYPE Module] [SEMANTICS pydantic, task, model, validate, task-status]
# @BRIEF Defines the data models and enumerations used by the Task Manager.
# @LAYER Domain
# @RELATION CALLED_BY -> [TaskManager]
# @RELATION CALLED_BY -> [TaskManagerPackage]
# @INVARIANT  Task IDs are immutable once created.
# @CONSTRAINT: Must use Pydantic for data validation.
# @PRE  Task manager initialized
# @POST  Task models exported with immutable IDs
# @SIDE_EFFECT  Defines task data schema
# @DATA_CONTRACT  TaskInput -> TaskModel
from datetime import UTC, datetime
from enum import Enum
from typing import Any
import uuid

from pydantic import BaseModel, ConfigDict, Field


# #region TaskStatus [TYPE Enum]
class TaskStatus(str, Enum):
    PENDING = "PENDING"
    RUNNING = "RUNNING"
    SUCCESS = "SUCCESS"
    FAILED = "FAILED"
    AWAITING_MAPPING = "AWAITING_MAPPING"
    AWAITING_INPUT = "AWAITING_INPUT"
# #endregion TaskStatus
# #region LogLevel [TYPE Enum]
class LogLevel(str, Enum):
    DEBUG = "DEBUG"
    INFO = "INFO"
    WARNING = "WARNING"
    ERROR = "ERROR"
# #endregion LogLevel
# #region LogEntry [C:2] [TYPE Class]
# @BRIEF A Pydantic model representing a single, structured log entry associated with a task.
#
# {
#   required_fields: {message: str},
#   optional_fields: {timestamp: datetime, level: str, source: str, context: dict, metadata: dict}
# }
class LogEntry(BaseModel):
    timestamp: datetime = Field(default_factory=lambda: datetime.now(UTC))
    level: str = Field(default="INFO")
    message: str
    source: str = Field(
        default="system"
    )  # Component attribution: plugin, superset_api, git, etc.
    context: dict[str, Any] | None = (
        None  # Legacy field, kept for backward compatibility
    )
    metadata: dict[str, Any] | None = (
        None  # Structured metadata (e.g., dashboard_id, progress)
    )
# #endregion LogEntry
# #region TaskLog [C:3] [TYPE Class] [SEMANTICS task, log, persistent, pydantic]
# @BRIEF A Pydantic model representing a persisted log entry from the database.
# @RELATION DEPENDS_ON -> [TaskLogRecord]
class TaskLog(BaseModel):
    id: int
    task_id: str
    timestamp: datetime
    level: str
    source: str
    message: str
    metadata: dict[str, Any] | None = None
    model_config = ConfigDict(from_attributes=True)
# #endregion TaskLog
# #region LogFilter [TYPE Class]
class LogFilter(BaseModel):
    level: str | None = None  # Filter by log level
    source: str | None = None  # Filter by source component
    search: str | None = None  # Text search in message
    offset: int = Field(default=0, ge=0)
    limit: int = Field(default=100, ge=1, le=1000)
# #endregion LogFilter
# #region LogStats [C:1] [TYPE Class] [SEMANTICS log, stats, aggregation, pydantic]
# @BRIEF Statistics about log entries for a task.
# @RELATION DEPENDS_ON -> [TaskLog]
class LogStats(BaseModel):
    total_count: int
    by_level: dict[str, int]  # {"INFO": 10, "ERROR": 2}
    by_source: dict[str, int]  # {"plugin": 5, "superset_api": 7}
# #endregion LogStats
# #region Task [C:3] [TYPE Class] [SEMANTICS task, job, execution, state, pydantic]
# @BRIEF A Pydantic model representing a single execution instance of a plugin, including its status, parameters, and logs.
# @RELATION DEPENDS_ON -> [TaskStatus]
# @RELATION DEPENDS_ON -> [LogEntry]
# @RELATION DEPENDS_ON -> [TaskManager]
class Task(BaseModel):
    id: str = Field(default_factory=lambda: str(uuid.uuid4()))
    plugin_id: str
    status: TaskStatus = TaskStatus.PENDING
    started_at: datetime | None = None
    finished_at: datetime | None = None
    user_id: str | None = None
    logs: list[LogEntry] = Field(default_factory=list)
    params: dict[str, Any] = Field(default_factory=dict)
    input_required: bool = False
    input_request: dict[str, Any] | None = None
    # Result payload can be dict/list/scalar depending on plugin and legacy records.
    result: Any | None = None
    # #region __init__ [TYPE Function]
    # @PURPOSE: Initializes the Task model and validates input_request for AWAITING_INPUT status.
    # @PRE      If status is AWAITING_INPUT, input_request must be provided.
    # @POST     Task instance is created or ValueError is raised.
    # @PARAM    **data - Keyword arguments for model initialization.
    def __init__(self, **data):
        super().__init__(**data)
        if self.status == TaskStatus.AWAITING_INPUT and not self.input_request:
            raise ValueError("input_request is required when status is AWAITING_INPUT")
    # #endregion __init__
# #endregion Task
# #endregion TaskManagerModels
