"""调度器工具服务门面。

Agent 工具与 API 端点对运行时调度器的操作统一经本模块调用，
Scheduler 实现由 startup 组合根在导入期注册，避免 application 层
静态依赖顶层 scheduler 模块（scheduler 反向依赖 chain，会成环）。

依赖方向：

    agent.tools / api.endpoints -> application.scheduling <- startup（注册 Scheduler 类）
"""

import asyncio
from dataclasses import dataclass, field
from enum import StrEnum
from typing import Any, Awaitable, Callable, List, Optional, cast

# Agent 自主定时任务在运行时调度器中的任务 ID 前缀。
AGENT_TASK_JOB_PREFIX = "agent-task"

# Scheduler 类：由 startup/initializers/scheduler.py 在导入期注册。
_scheduler_class: Any = None


class JobOverlapPolicy(StrEnum):
    """描述同一 job 已运行时的新触发处理策略。"""

    SKIP = "skip"


class JobRecoveryPolicy(StrEnum):
    """描述进程重启后是否以及如何重建执行意图。"""

    NEXT_SCHEDULE = "next_schedule"
    DURABLE_QUEUE = "durable_queue"
    MANUAL_ONLY = "manual_only"


@dataclass(frozen=True, slots=True)
class JobSpec:
    """数据化声明一个业务 job 的执行和恢复合同。"""

    job_id: str
    name: str
    func: Callable[..., Any]
    owner: str
    overlap: JobOverlapPolicy = JobOverlapPolicy.SKIP
    timeout_seconds: int | None = None
    manual: bool = False
    recovery: JobRecoveryPolicy = JobRecoveryPolicy.NEXT_SCHEDULE
    kwargs: dict[str, Any] = field(default_factory=dict)

    def to_runtime_state(self) -> dict[str, Any]:
        """生成兼容 Scheduler Facade 的可变执行状态。"""
        return {
            "name": self.name,
            "func": self.func,
            "owner": self.owner,
            "overlap": self.overlap.value,
            "timeout_seconds": self.timeout_seconds,
            "manual": self.manual,
            "recovery": self.recovery.value,
            "kwargs": dict(self.kwargs),
            "running": False,
        }


class JobCatalog:
    """保存唯一 job ID 到声明的映射。"""

    def __init__(self, specs: list[JobSpec]) -> None:
        """拒绝重复 ID，并冻结供 Scheduler 初始化的声明集合。"""
        self._specs = {spec.job_id: spec for spec in specs}
        if len(self._specs) != len(specs):
            raise ValueError("JobSpec job_id 不得重复")

    def runtime_states(self) -> dict[str, dict[str, Any]]:
        """返回兼容旧 Scheduler `_jobs` 的全新状态字典。"""
        return {
            job_id: spec.to_runtime_state()
            for job_id, spec in self._specs.items()
        }


class JobExecutionState:
    """集中维护兼容 job 字典的 overlap 与终态字段。"""

    @staticmethod
    def begin(job: dict[str, Any], started_at: str) -> bool:
        """按 overlap policy 尝试进入 running，已运行时返回 False。"""
        if job.get("running") and job.get(
            "overlap", JobOverlapPolicy.SKIP.value
        ) == JobOverlapPolicy.SKIP.value:
            return False
        job.update(
            running=True,
            last_started_at=started_at,
            last_finished_at=None,
            last_error=None,
        )
        return True

    @staticmethod
    def finish(job: dict[str, Any], finished_at: str, error: str | None) -> None:
        """写入成功或失败终态并释放 running 标记。"""
        job.update(
            running=False,
            last_finished_at=finished_at,
            last_error=error,
        )

    @staticmethod
    async def await_result(
        awaitable: Awaitable[Any], timeout_seconds: int | None
    ) -> Any:
        """等待协程任务；声明了超时时由 asyncio 负责取消底层任务。"""
        if timeout_seconds is None:
            return await awaitable
        return await asyncio.wait_for(awaitable, timeout=timeout_seconds)


def register_scheduler_class(scheduler_class: Any) -> None:
    """注册 Scheduler 类（组合根在导入期调用）。"""
    global _scheduler_class
    _scheduler_class = scheduler_class


def get_scheduler() -> Any:
    """返回调度器实例。"""
    if _scheduler_class is None:
        raise RuntimeError(
            "调度器服务未初始化：请先通过 register_scheduler_class 注册 Scheduler 类"
        )
    return _scheduler_class()


class Scheduler:
    """应用层调度器兼容门面，不直接导入顶层 Scheduler 实现。"""

    def __new__(cls) -> Any:
        """返回组合根注册的调度器实例。"""
        return get_scheduler()


def list_scheduler_jobs() -> List[Any]:
    """列出运行时调度器的全部任务。"""
    return cast(List[Any], get_scheduler().list())


def start_scheduler_job(job_id: str, **kwargs: Any) -> None:
    """立即运行指定的运行时定时任务。"""
    get_scheduler().start(job_id, **kwargs)


def update_plugin_job(plugin_id: str) -> None:
    """更新插件的定时任务。"""
    get_scheduler().update_plugin_job(plugin_id)


def remove_plugin_job(plugin_id: str) -> None:
    """移除插件的定时任务。"""
    get_scheduler().remove_plugin_job(plugin_id)


def start_agent_task(task_id: int) -> bool:
    """立即执行 Agent 自主定时任务。"""
    return cast(bool, get_scheduler().start_agent_task(task_id))


def get_agent_task_next_run(task_id: int) -> Optional[Any]:
    """查询 Agent 自主定时任务的下一次运行时间。"""
    return get_scheduler().get_agent_task_next_run(task_id)


def update_agent_task_job(task_id: int) -> Optional[Any]:
    """更新 Agent 自主定时任务的注册信息，返回下一次运行时间。"""
    return get_scheduler().update_agent_task_job(task_id)


def remove_agent_task_job(task_id: int) -> None:
    """移除 Agent 自主定时任务的注册信息。"""
    get_scheduler().remove_agent_task_job(task_id)
