from __future__ import annotations

import uuid
from datetime import datetime
from enum import Enum
from typing import Any, Dict, List, Optional

from pydantic import BaseModel, Field


class ParameterType(str, Enum):
    STRING = "string"
    INTEGER = "integer"
    FLOAT = "float"
    BOOLEAN = "boolean"
    ENUM = "enum"
    PATH = "path"


class ParameterDef(BaseModel):
    name: str = Field(..., description="参数名，用于模板渲染")
    type: ParameterType = Field(..., description="参数类型")
    description: str = Field(default="", description="参数说明")
    default: Any = Field(default=None, description="默认值")
    required: bool = Field(default=False, description="是否必填")
    options: Optional[List[str]] = Field(
        default=None, description="enum 类型的可选值列表"
    )
    constraints: Dict[str, Any] = Field(
        default_factory=dict, description="额外约束，如 min, max, regex"
    )
    source_dir: Optional[str] = Field(
        default=None, description="path 类型参数的文件来源目录，服务端据此列出候选文件"
    )


class TaskDefinition(BaseModel):
    version: str = Field(
        default="1", description="任务定义规范版本号"
    )
    id: str = Field(..., description="任务定义唯一标识")
    name: str = Field(..., description="显示名称")
    description: str = Field(default="", description="任务描述")
    command_template: str = Field(
        ..., description="Jinja2 命令模板，如 'python map.py --in {{input}}'"
    )
    parameters: List[ParameterDef] = Field(default_factory=list)
    env: Dict[str, str] = Field(default_factory=dict, description="额外环境变量")
    working_dir: Optional[str] = Field(default=None, description="工作目录")
    auto_restart: bool = Field(default=False, description="异常退出后自动重启")
    progress_protocol: str = Field(
        default="stdout", description="进度上报协议: stdout / http / both"
    )
    setup_script: Optional[str] = Field(
        default=None, description="运行前 source 的环境脚本（如 /opt/ros/noetic/setup.bash）"
    )
    name_i18n: Dict[str, str] = Field(
        default_factory=dict, description="多语言名称 {en: ..., ru: ...}"
    )
    description_i18n: Dict[str, str] = Field(
        default_factory=dict, description="多语言描述 {en: ..., ru: ...}"
    )


class TaskStatus(str, Enum):
    PENDING = "pending"
    RUNNING = "running"
    STOPPING = "stopping"
    STOPPED = "stopped"
    FAILED = "failed"
    COMPLETED = "completed"


class ProgressInfo(BaseModel):
    percent: int = Field(default=0, ge=0, le=100)
    message: str = Field(default="")
    metadata: Dict[str, Any] = Field(default_factory=dict)
    timestamp: datetime = Field(default_factory=datetime.utcnow)


class TaskInstance(BaseModel):
    id: str = Field(default_factory=lambda: str(uuid.uuid4())[:8])
    definition_id: str
    status: TaskStatus = TaskStatus.PENDING
    pid: Optional[int] = None
    command: Optional[str] = None  # 渲染后的最终命令行
    params: Dict[str, Any] = Field(default_factory=dict)
    progress: ProgressInfo = Field(default_factory=ProgressInfo)
    returncode: Optional[int] = None
    created_at: datetime = Field(default_factory=datetime.utcnow)
    started_at: Optional[datetime] = None
    finished_at: Optional[datetime] = None
    log_buffer: List[str] = Field(default_factory=list, exclude=True)

    def model_post_init(self, __context: Any) -> None:
        if isinstance(self.log_buffer, list) and len(self.log_buffer) > 5000:
            self.log_buffer = self.log_buffer[-5000:]


class ConflictPolicy(str, Enum):
    REJECT = "reject"
    RESTART = "restart"


class StartTaskRequest(BaseModel):
    definition_id: str
    params: Dict[str, Any] = Field(default_factory=dict)
    conflict_policy: ConflictPolicy = ConflictPolicy.REJECT
    force_ignore_lock: bool = Field(
        default=False, description="强制忽略 /tmp/arm.lock 锁文件（需用户两次确认）"
    )


class TaskInstanceOut(BaseModel):
    id: str
    definition_id: str
    status: TaskStatus
    pid: Optional[int]
    command: Optional[str]
    params: Dict[str, Any]
    progress: ProgressInfo
    returncode: Optional[int]
    created_at: datetime
    started_at: Optional[datetime]
    finished_at: Optional[datetime]

    @staticmethod
    def from_instance(instance: TaskInstance) -> TaskInstanceOut:
        return TaskInstanceOut(
            id=instance.id,
            definition_id=instance.definition_id,
            status=instance.status,
            pid=instance.pid,
            command=instance.command,
            params=instance.params,
            progress=instance.progress,
            returncode=instance.returncode,
            created_at=instance.created_at,
            started_at=instance.started_at,
            finished_at=instance.finished_at,
        )


class AppPackage(BaseModel):
    """已安装的 App 包元信息"""

    app_id: str = Field(..., description="应用标识符")
    app_version: str = Field(..., description="应用版本号")
    name: str = Field(..., description="显示名称")
    description: str = Field(default="", description="任务描述")
    app_dir: str = Field(..., description="安装绝对路径")
    installed_at: datetime = Field(default_factory=datetime.utcnow)
    definition: TaskDefinition = Field(..., description="生成的任务定义（id=app_id@app_version）")
    hidden: bool = Field(default=False, description="是否隐藏于 App 市场")

    @property
    def full_id(self) -> str:
        return f"{self.app_id}@{self.app_version}"
