"""
定时任务调度器
支持定时执行自动化任务
"""
import asyncio
from typing import Optional, Callable, List, Dict, Any
from datetime import datetime, timedelta
from croniter import croniter
from loguru import logger
from dataclasses import dataclass, field
from enum import Enum


class TaskStatus(Enum):
    """任务状态"""
    PENDING = "pending"
    RUNNING = "running"
    COMPLETED = "completed"
    FAILED = "failed"
    CANCELLED = "cancelled"


@dataclass
class ScheduledTask:
    """定时任务"""
    id: str
    name: str
    cron: str  # cron表达式
    handler: Callable
    next_run: Optional[datetime] = None
    last_run: Optional[datetime] = None
    status: TaskStatus = TaskStatus.PENDING
    run_count: int = 0
    error: Optional[str] = None
    enabled: bool = True
    params: Dict[str, Any] = field(default_factory=dict)
    
    def calculate_next_run(self) -> datetime:
        """计算下次运行时间"""
        iter = croniter(self.cron, datetime.now())
        return iter.get_next(datetime)


class TaskScheduler:
    """
    任务调度器
    支持cron表达式的定时任务调度
    """
    
    def __init__(self):
        self._tasks: Dict[str, ScheduledTask] = {}
        self._running: bool = False
        self._task_id_counter: int = 0
    
    def add_task(
        self,
        name: str,
        cron: str,
        handler: Callable,
        params: Dict[str, Any] = None,
    ) -> str:
        """
        添加定时任务
        
        Args:
            name: 任务名称
            cron: cron表达式 (如 "0 9 * * *" 表示每天9点)
            handler: 任务处理函数
            params: 任务参数
            
        Returns:
            str: 任务ID
            
        Example:
            scheduler.add_task(
                "每天9点发简报",
                "0 9 * * *",
                send_briefing,
                {"recipient": "user@example.com"}
            )
        """
        self._task_id_counter += 1
        task_id = f"task_{self._task_id_counter}"
        
        task = ScheduledTask(
            id=task_id,
            name=name,
            cron=cron,
            handler=handler,
            params=params or {},
        )
        
        task.next_run = task.calculate_next_run()
        self._tasks[task_id] = task
        
        logger.info(f"添加任务: {name}, cron={cron}, 下次运行={task.next_run}")
        return task_id
    
    def remove_task(self, task_id: str):
        """移除任务"""
        if task_id in self._tasks:
            del self._tasks[task_id]
            logger.info(f"移除任务: {task_id}")
    
    def enable_task(self, task_id: str):
        """启用任务"""
        if task_id in self._tasks:
            self._tasks[task_id].enabled = True
            logger.info(f"启用任务: {task_id}")
    
    def disable_task(self, task_id: str):
        """禁用任务"""
        if task_id in self._tasks:
            self._tasks[task_id].enabled = False
            logger.info(f"禁用任务: {task_id}")
    
    def get_task(self, task_id: str) -> Optional[ScheduledTask]:
        """获取任务"""
        return self._tasks.get(task_id)
    
    def list_tasks(self) -> List[ScheduledTask]:
        """列出所有任务"""
        return list(self._tasks.values())
    
    async def run_task(self, task_id: str) -> bool:
        """
        手动运行任务
        
        Args:
            task_id: 任务ID
            
        Returns:
            bool: 是否成功
        """
        task = self._tasks.get(task_id)
        if not task:
            logger.error(f"任务不存在: {task_id}")
            return False
        
        return await self._execute_task(task)
    
    async def _execute_task(self, task: ScheduledTask) -> bool:
        """执行任务"""
        logger.info(f"执行任务: {task.name}")
        
        task.status = TaskStatus.RUNNING
        task.last_run = datetime.now()
        
        try:
            if asyncio.iscoroutinefunction(task.handler):
                await task.handler(**task.params)
            else:
                task.handler(**task.params)
            
            task.status = TaskStatus.COMPLETED
            task.run_count += 1
            task.error = None
            
            logger.info(f"任务完成: {task.name}")
            return True
            
        except Exception as e:
            task.status = TaskStatus.FAILED
            task.error = str(e)
            logger.error(f"任务失败: {task.name}, error={e}")
            return False
        
        finally:
            # 计算下次运行时间
            task.next_run = task.calculate_next_run()
    
    async def start(self):
        """启动调度器"""
        self._running = True
        logger.info("任务调度器已启动")
        
        while self._running:
            now = datetime.now()
            
            for task in self._tasks.values():
                if not task.enabled:
                    continue
                
                if task.next_run and task.next_run <= now:
                    # 执行任务
                    asyncio.create_task(self._execute_task(task))
            
            # 每秒检查一次
            await asyncio.sleep(1)
    
    def stop(self):
        """停止调度器"""
        self._running = False
        logger.info("任务调度器已停止")


class IntervalTask:
    """间隔任务"""
    
    def __init__(
        self,
        name: str,
        interval_seconds: float,
        handler: Callable,
        params: Dict[str, Any] = None,
    ):
        self.name = name
        self.interval_seconds = interval_seconds
        self.handler = handler
        self.params = params or {}
        self._running = False
        self._task: Optional[asyncio.Task] = None
    
    async def start(self):
        """启动间隔任务"""
        self._running = True
        self._task = asyncio.create_task(self._run_loop())
        logger.info(f"间隔任务已启动: {self.name}")
    
    async def stop(self):
        """停止间隔任务"""
        self._running = False
        if self._task:
            self._task.cancel()
        logger.info(f"间隔任务已停止: {self.name}")
    
    async def _run_loop(self):
        """运行循环"""
        while self._running:
            try:
                if asyncio.iscoroutinefunction(self.handler):
                    await self.handler(**self.params)
                else:
                    self.handler(**self.params)
            except Exception as e:
                logger.error(f"间隔任务执行失败: {self.name}, error={e}")
            
            await asyncio.sleep(self.interval_seconds)


class DelayedTask:
    """延迟任务"""
    
    @staticmethod
    async def run_after(
        delay_seconds: float,
        handler: Callable,
        params: Dict[str, Any] = None,
    ):
        """延迟执行"""
        await asyncio.sleep(delay_seconds)
        
        if asyncio.iscoroutinefunction(handler):
            await handler(**(params or {}))
        else:
            handler(**(params or {}))
    
    @staticmethod
    async def run_at(
        target_time: datetime,
        handler: Callable,
        params: Dict[str, Any] = None,
    ):
        """在指定时间执行"""
        now = datetime.now()
        delay = (target_time - now).total_seconds()
        
        if delay > 0:
            await DelayedTask.run_after(delay, handler, params)
        else:
            logger.warning(f"目标时间已过: {target_time}")


# 常用cron表达式
CRON_EXPRESSIONS = {
    "every_minute": "* * * * *",
    "every_hour": "0 * * * *",
    "every_day_9am": "0 9 * * *",
    "every_day_6pm": "0 18 * * *",
    "every_monday_9am": "0 9 * * 1",
    "every_friday_5pm": "0 17 * * 5",
    "first_day_of_month": "0 9 1 * *",
    "every_weekday_9am": "0 9 * * 1-5",
}
