"""
统一的调度器管理器
支持单/多命名空间模式
"""
import asyncio
import logging
from typing import Optional, Dict
from jettask.core.unified_manager_base import UnifiedManagerBase
from jettask.scheduler.scheduler import TaskScheduler

logger = logging.getLogger(__name__)


class UnifiedSchedulerManager(UnifiedManagerBase):
    """
    统一的调度器管理器
    继承自 UnifiedManagerBase，实现调度器特定的逻辑
    """

    def __init__(self,
                 task_center_url: str,
                 scan_interval: float = 1.0,
                 batch_size: int = 100,
                 lookahead_seconds: int = 3600,
                 check_interval: int = 30,
                 debug: bool = False):
        """
        初始化调度器管理器

        Args:
            task_center_url: 任务中心URL
            scan_interval: 调度器扫描间隔（秒）
            batch_size: 每批处理的最大任务数
            lookahead_seconds: 提前查看时间（秒）
            check_interval: 命名空间检测间隔（秒）
            debug: 是否启用调试模式
        """
        super().__init__(task_center_url, check_interval, debug)

        self.scan_interval = scan_interval
        self.batch_size = batch_size
        self.lookahead_seconds = lookahead_seconds

        # 调度器实例（单命名空间模式）
        self.scheduler_instance: Optional[TaskScheduler] = None

        # 多命名空间模式下的调度器实例
        self.scheduler_tasks: Dict[str, asyncio.Task] = {}

    async def run_single_namespace(self, namespace_name: str):
        """
        运行单命名空间模式（实现基类抽象方法）

        Args:
            namespace_name: 命名空间名称
        """
        # 调用统一的内部实现
        await self._run_unified()

    async def run_multi_namespace(self, namespace_names: Optional[set]):
        """
        运行多命名空间模式（实现基类抽象方法）

        Args:
            namespace_names: 目标命名空间集合，None表示所有命名空间
        """
        # 调用统一的内部实现
        await self._run_unified()

    async def _run_unified(self):
        """
        统一的运行逻辑（内部实现）

        核心思想：单命名空间只是多命名空间的子集
        - 单命名空间模式：只管理一个固定的命名空间
        - 多命名空间模式：动态检测和管理多个命名空间
        """
        if self.is_single_namespace:
            logger.info(f"启动单命名空间调度器: {self.namespace_name}")
        else:
            logger.info("启动多命名空间调度器管理器")
            logger.info(f"命名空间检测间隔: {self.check_interval}秒")

        logger.info(f"扫描间隔: {self.scan_interval}秒")
        logger.info(f"批次大小: {self.batch_size}")
        logger.info(f"提前查看: {self.lookahead_seconds}秒")

        try:
            while self.running:
                # 获取目标命名空间列表
                if self.is_single_namespace:
                    # 单命名空间模式：获取指定命名空间的配置
                    namespaces = await self._get_single_namespace_config(self.namespace_name)
                else:
                    # 多命名空间模式：获取所有命名空间
                    namespaces = await self.fetch_namespaces()

                if not namespaces:
                    if self.is_single_namespace:
                        logger.error(f"无法获取命名空间 '{self.namespace_name}' 的配置")
                        raise ValueError(f"命名空间 '{self.namespace_name}' 不存在或配置不可用")
                    else:
                        logger.warning("未检测到任何命名空间，等待下次检测...")
                        await asyncio.sleep(self.check_interval)
                        continue

                # 检查新增的命名空间
                current_namespaces = set(ns['name'] for ns in namespaces)
                new_namespaces = current_namespaces - set(self.scheduler_tasks.keys())

                # 启动新命名空间的调度器
                for ns_name in new_namespaces:
                    ns_config = next(ns for ns in namespaces if ns['name'] == ns_name)
                    await self._start_scheduler_for_namespace(ns_name, ns_config)

                # 检查已删除的命名空间
                removed_namespaces = set(self.scheduler_tasks.keys()) - current_namespaces
                for ns_name in removed_namespaces:
                    await self._stop_scheduler_for_namespace(ns_name)

                # 单命名空间模式：启动后就阻塞等待，不再循环检测
                if self.is_single_namespace:
                    # 等待调度器任务完成（通常是被中断）
                    if self.namespace_name in self.scheduler_tasks:
                        try:
                            await self.scheduler_tasks[self.namespace_name]
                        except asyncio.CancelledError:
                            pass
                    break

                # 多命名空间模式：等待下次检测
                await asyncio.sleep(self.check_interval)

        except KeyboardInterrupt:
            logger.info("收到中断信号，停止所有调度器...")
            await self._stop_all_schedulers()

    async def _get_single_namespace_config(self, namespace_name: str):
        """
        获取单个命名空间的配置

        Args:
            namespace_name: 命名空间名称

        Returns:
            命名空间配置列表（只包含一个元素）
        """
        try:
            # 使用基类提供的方法获取命名空间配置
            namespaces = await self.fetch_namespaces_info({namespace_name})

            if not namespaces:
                logger.error(f"无法获取命名空间配置: {namespace_name}")
                return []

            # 转换为调度器需要的格式
            result = []
            for ns in namespaces:
                redis_url = ns.get('redis_config', {}).get('url')
                pg_url = ns.get('pg_config', {}).get('url')
                redis_prefix = ns.get('redis_prefix', 'jettask')

                if not redis_url or not pg_url:
                    logger.error(f"命名空间配置不完整: redis_url={redis_url}, pg_url={pg_url}")
                    continue

                logger.info(f"命名空间配置:")
                logger.info(f"  Redis URL: {redis_url}")
                logger.info(f"  Redis Prefix: {redis_prefix}")
                logger.info(f"  PostgreSQL URL: {pg_url.split('@')[1] if '@' in pg_url else pg_url}")

                result.append({
                    'name': ns['name'],
                    'redis_url': redis_url,
                    'redis_prefix': redis_prefix,
                    'pg_url': pg_url
                })

            return result

        except Exception as e:
            logger.error(f"获取命名空间配置失败: {e}", exc_info=True)
            return []

    async def _start_scheduler_for_namespace(self, namespace_name: str, config: dict):
        """为指定命名空间启动调度器"""
        logger.info(f"为命名空间 '{namespace_name}' 启动调度器")

        try:
            redis_url = config.get('redis_url')
            redis_prefix = config.get('redis_prefix', 'jettask')
            pg_url = config.get('pg_url')

            if not redis_url or not pg_url:
                logger.error(f"命名空间 '{namespace_name}' 配置不完整")
                return

            # 创建 Jettask 应用实例
            from jettask import Jettask
            app = Jettask(
                redis_url=redis_url,
                redis_prefix=redis_prefix,
                pg_url=pg_url
            )

            # 创建调度器
            scheduler = TaskScheduler(
                app=app,
                db_url=pg_url,
                scan_interval=self.scan_interval,
                batch_size=self.batch_size,
                lookahead_seconds=self.lookahead_seconds
            )

            # 启动调度器任务
            task = asyncio.create_task(scheduler.start(wait=True))
            self.scheduler_tasks[namespace_name] = task

            logger.info(f"✓ 命名空间 '{namespace_name}' 的调度器已启动")

        except Exception as e:
            logger.error(f"启动命名空间 '{namespace_name}' 的调度器失败: {e}", exc_info=True)

    async def _stop_scheduler_for_namespace(self, namespace_name: str):
        """停止指定命名空间的调度器"""
        logger.info(f"停止命名空间 '{namespace_name}' 的调度器")

        task = self.scheduler_tasks.get(namespace_name)
        if task:
            task.cancel()
            try:
                await task
            except asyncio.CancelledError:
                pass

            del self.scheduler_tasks[namespace_name]
            logger.info(f"✓ 命名空间 '{namespace_name}' 的调度器已停止")

    async def _stop_all_schedulers(self):
        """停止所有调度器"""
        for namespace_name in list(self.scheduler_tasks.keys()):
            await self._stop_scheduler_for_namespace(namespace_name)

    async def fetch_namespaces(self):
        """
        从任务中心获取所有命名空间

        Returns:
            命名空间列表
        """
        import aiohttp

        base_url = self.get_base_url()
        url = f"{base_url}/api/v1/namespaces"

        try:
            async with aiohttp.ClientSession() as session:
                async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as response:
                    if response.status == 200:
                        data = await response.json()
                        return data.get('namespaces', [])
                    else:
                        logger.error(f"获取命名空间列表失败: HTTP {response.status}")
                        return []
        except Exception as e:
            logger.error(f"获取命名空间列表失败: {e}")
            return []


__all__ = ['UnifiedSchedulerManager']
