# [DEF:TaskManagerModels:Module]
# @TIER: STANDARD
# @SEMANTICS: task, models, pydantic, enum, state
# @PURPOSE: Defines the data models and enumerations used by the Task Manager.
# @LAYER: Core
# @RELATION: Used by TaskManager and API routes.
# @INVARIANT: Task IDs are immutable once created.
# @CONSTRAINT: Must use Pydantic for data validation.

# [SECTION: IMPORTS]
import uuid
from datetime import datetime
from enum import Enum
from typing import Dict, Any, List, Optional

from pydantic import BaseModel, Field
# [/SECTION]

# [DEF:TaskStatus:Enum]
# @TIER: TRIVIAL
# @SEMANTICS: task, status, state, enum
# @PURPOSE: Defines the possible states a task can be in during its lifecycle.
class TaskStatus(str, Enum):
    PENDING = "PENDING"
    RUNNING = "RUNNING"
    SUCCESS = "SUCCESS"
    FAILED = "FAILED"
    AWAITING_MAPPING = "AWAITING_MAPPING"
    AWAITING_INPUT = "AWAITING_INPUT"
# [/DEF:TaskStatus:Enum]

# [DEF:LogLevel:Enum]
# @SEMANTICS: log, level, severity, enum
# @PURPOSE: Defines the possible log levels for task logging.
# @TIER: STANDARD
class LogLevel(str, Enum):
    DEBUG = "DEBUG"
    INFO = "INFO"
    WARNING = "WARNING"
    ERROR = "ERROR"
# [/DEF:LogLevel:Enum]

# [DEF:LogEntry:Class]
# @SEMANTICS: log, entry, record, pydantic
# @PURPOSE: A Pydantic model representing a single, structured log entry associated with a task.
# @TIER: CRITICAL
# @INVARIANT: Each log entry has a unique timestamp and source.
#
# @TEST_CONTRACT: LogEntryModel ->
# {
#   required_fields: {message: str},
#   optional_fields: {timestamp: datetime, level: str, source: str, context: dict, metadata: dict}
# }
# @TEST_FIXTURE: valid_log_entry -> {"message": "Plugin initialized"}
# @TEST_EDGE: empty_message -> {"message": ""}
class LogEntry(BaseModel):
    timestamp: datetime = Field(default_factory=datetime.utcnow)
    level: str = Field(default="INFO")
    message: str
    source: str = Field(default="system")  # Component attribution: plugin, superset_api, git, etc.
    context: Optional[Dict[str, Any]] = None  # Legacy field, kept for backward compatibility
    metadata: Optional[Dict[str, Any]] = None  # Structured metadata (e.g., dashboard_id, progress)
# [/DEF:LogEntry:Class]

# [DEF:TaskLog:Class]
# @SEMANTICS: task, log, persistent, pydantic
# @PURPOSE: A Pydantic model representing a persisted log entry from the database.
# @TIER: STANDARD
# @RELATION: MAPS_TO -> TaskLogRecord
class TaskLog(BaseModel):
    id: int
    task_id: str
    timestamp: datetime
    level: str
    source: str
    message: str
    metadata: Optional[Dict[str, Any]] = None

    class Config:
        from_attributes = True
# [/DEF:TaskLog:Class]

# [DEF:LogFilter:Class]
# @SEMANTICS: log, filter, query, pydantic
# @PURPOSE: Filter parameters for querying task logs.
# @TIER: STANDARD
class LogFilter(BaseModel):
    level: Optional[str] = None  # Filter by log level
    source: Optional[str] = None  # Filter by source component
    search: Optional[str] = None  # Text search in message
    offset: int = Field(default=0, ge=0)
    limit: int = Field(default=100, ge=1, le=1000)
# [/DEF:LogFilter:Class]

# [DEF:LogStats:Class]
# @SEMANTICS: log, stats, aggregation, pydantic
# @PURPOSE: Statistics about log entries for a task.
# @TIER: STANDARD
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}
# [/DEF:LogStats:Class]

# [DEF:Task:Class]
# @TIER: STANDARD
# @SEMANTICS: task, job, execution, state, pydantic
# @PURPOSE: A Pydantic model representing a single execution instance of a plugin, including its status, parameters, and logs.
class Task(BaseModel):
    id: str = Field(default_factory=lambda: str(uuid.uuid4()))
    plugin_id: str
    status: TaskStatus = TaskStatus.PENDING
    started_at: Optional[datetime] = None
    finished_at: Optional[datetime] = None
    user_id: Optional[str] = None
    logs: List[LogEntry] = Field(default_factory=list)
    params: Dict[str, Any] = Field(default_factory=dict)
    input_required: bool = False
    input_request: Optional[Dict[str, Any]] = None
    # Result payload can be dict/list/scalar depending on plugin and legacy records.
    result: Optional[Any] = None

    # [DEF:__init__: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")
    # [/DEF:__init__:Function]
# [/DEF:Task:Class]

# [/DEF:TaskManagerModels:Module]
