import logging
import os
import asyncio
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from jettask.webui.config import webui_config
from jettask.db.connector import get_pg_engine_and_factory
import uvicorn

logger = logging.getLogger(__name__)

CONFIG_SYNC_INTERVAL = int(os.environ.get('JETTASK_CONFIG_SYNC_INTERVAL', 300))


async def _config_sync_loop(app: FastAPI, interval: int):

    await asyncio.sleep(5)

    await _sync_all_configs(app)

    while True:
        try:
            await asyncio.sleep(interval)
            await _sync_all_configs(app)
        except asyncio.CancelledError:
            logger.info("配置同步任务被取消")
            break
        except Exception as e:
            logger.error(f"配置同步任务异常: {e}", exc_info=True)
            await asyncio.sleep(30)


async def _sync_all_configs(app: FastAPI):
    from jettask.webui.services.config_service import ConfigService
    from jettask.db.models.config import Config

    try:
        if not hasattr(app.state, 'meta_db_session_factory'):
            logger.warning("元数据库会话工厂未初始化，跳过配置同步")
            return

        if not hasattr(app.state, 'namespace_manager'):
            logger.warning("命名空间管理器未初始化，跳过配置同步")
            return

        meta_db_session_factory = app.state.meta_db_session_factory
        namespace_manager = app.state.namespace_manager

        from jettask.config import DynamicConfig

        namespaces = await namespace_manager.list_namespaces(enabled_only=True)

        for ns in namespaces:
            try:
                redis_client = await ns.get_redis_client()
                prefix = ns.redis_prefix

                async with meta_db_session_factory() as pg_session:
                    task_configs = await ConfigService.get_all_task_configs_from_pg(pg_session, ns.name)

                    for scope, configs in task_configs.items():
                        parsed = Config.parse_task_scope(scope)
                        if not parsed:
                            continue

                        task_name = parsed['task_name']
                        queue = parsed['queue']

                        pg_task_version = await ConfigService.get_task_version_from_pg(
                            pg_session, ns.name, queue, task_name
                        )

                        config_key = DynamicConfig.get_task_config_key(prefix, task_name)
                        redis_task_version = await redis_client.hget(config_key, '_version')
                        redis_task_version = int(redis_task_version) if redis_task_version else 0

                        logger.debug(
                            f"[CONFIG_SYNC] {ns.name}:{task_name}: "
                            f"PG版本={pg_task_version}, Redis版本={redis_task_version}"
                        )

                        if redis_task_version > 0 and redis_task_version >= pg_task_version:
                            continue
                        if redis_task_version == 0 and pg_task_version == 0:
                            logger.debug(f"[CONFIG_SYNC] {ns.name}:{task_name}: 两边版本号都为0，跳过")
                            continue

                        logger.info(
                            f"[CONFIG_SYNC] 恢复任务配置: {ns.name}:{task_name} "
                            f"(PG={pg_task_version}, Redis={redis_task_version})"
                        )
                        configs_with_version = {**configs, '_version': str(pg_task_version)}
                        await ConfigService._write_flat_config_to_redis(
                            redis_client, prefix, task_name, configs_with_version
                        )

                    system_configs = await ConfigService.get_all_system_configs_from_pg(pg_session)
                    system_config_key = DynamicConfig.get_system_config_key(prefix)

                    for key, value in system_configs.items():
                        exists = await redis_client.hexists(system_config_key, key)
                        if not exists:
                            logger.info(f"[CONFIG_SYNC] 恢复系统配置: {key}")
                            await DynamicConfig.set_system_config_to_redis(
                                redis_client, prefix, key, value,
                                publish_change=False
                            )

            except Exception as e:
                logger.error(f"同步配置到命名空间 {ns.name} 失败: {e}", exc_info=True)

    except Exception as e:
        logger.error(f"配置同步失败: {e}", exc_info=True)


@asynccontextmanager
async def lifespan(app: FastAPI):

    try:
        log_format = os.environ.get('JETTASK_LOG_FORMAT', 'text').lower()
        log_level = logging.INFO

        from jettask.utils.task_logger import JSONFormatter, ExtendedTextFormatter, TaskContextFilter
        import sys

        jettask_logger_names = [name for name in logging.Logger.manager.loggerDict
                                if name.startswith('jettask')]

        if not jettask_logger_names:
            jettask_logger_names = ['jettask']

        for logger_name in jettask_logger_names:
            jettask_logger = logging.getLogger(logger_name)
            jettask_logger.setLevel(log_level)

            jettask_logger.handlers.clear()

            handler = logging.StreamHandler(sys.stderr)
            handler.addFilter(TaskContextFilter())

            if log_format == 'json':
                handler.setFormatter(JSONFormatter())
            else:
                handler.setFormatter(ExtendedTextFormatter(
                    '%(asctime)s - %(levelname)s - [%(task_id)s] - %(name)s - %(message)s'
                ))

            jettask_logger.addHandler(handler)
            jettask_logger.propagate = False  

        if log_format == 'json':
            logger.info("日志格式已配置为 JSON (仅jettask相关日志)")
        else:
            logger.info("日志格式已配置为 TEXT (仅jettask相关日志)")

        db_info = webui_config.get_database_info()

        logger.info("=" * 60)
        logger.info("任务中心配置:")
        logger.info(f"  配置模式: {'Nacos' if webui_config.use_nacos else '环境变量'}")

        if db_info['host']:
            logger.info(f"  元数据库: {db_info['host']}:{db_info['port']}/{db_info['database']}")
        else:
            logger.info(f"  元数据库: {webui_config.pg_url}")

        logger.info(f"  Redis: {webui_config._mask_url(webui_config.redis_url)}")
        logger.info(f"  Redis Prefix: {webui_config.redis_prefix}")
        logger.info(f"  API服务: {webui_config.api_host}:{webui_config.api_port}")
        logger.info(f"  基础URL: {webui_config.base_url}")
        logger.info("=" * 60)

        meta_db_engine, meta_db_session_factory = get_pg_engine_and_factory(
            webui_config.meta_database_url
        )
        app.state.meta_db_engine = meta_db_engine
        app.state.meta_db_session_factory = meta_db_session_factory
        logger.info("元数据库会话工厂已初始化")

        from jettask.core.namespace import NamespaceManagerDB
        namespace_manager = NamespaceManagerDB(auto_refresh=True, refresh_interval=60)
        app.state.namespace_manager = namespace_manager
        logger.info("命名空间管理器已初始化")


        sync_task = asyncio.create_task(
            _config_sync_loop(app, CONFIG_SYNC_INTERVAL)
        )
        app.state.config_sync_task = sync_task
        logger.info(f"配置同步后台任务已启动（间隔 {CONFIG_SYNC_INTERVAL} 秒）")

        logger.info("JetTask WebUI 启动成功")
    except Exception as e:
        logger.error(f"启动失败: {e}")
        import traceback
        traceback.print_exc()
        raise

    yield

    if hasattr(app.state, 'config_sync_task') and app.state.config_sync_task:
        app.state.config_sync_task.cancel()
        try:
            await app.state.config_sync_task
        except asyncio.CancelledError:
            pass
        logger.info("配置同步后台任务已停止")

    if hasattr(app.state, 'namespace_manager') and app.state.namespace_manager:
        await app.state.namespace_manager.close()
        logger.info("命名空间管理器已关闭")

    if hasattr(app.state, 'meta_db_engine') and app.state.meta_db_engine:
        await app.state.meta_db_engine.dispose()
        logger.info("元数据库连接已关闭")


app = FastAPI(title="Jettask Monitor", lifespan=lifespan)

from jettask.webui.middleware import UnifiedAuthMiddleware
from jettask.webui.utils.jwt_utils import JWTManager

api_key = os.environ.get('JETTASK_API_KEY')
remote_token_verify_url = webui_config.remote_token_verify_url

jwt_manager = None
if remote_token_verify_url:
    jwt_manager = JWTManager(
        secret_key=webui_config.jwt_secret_key,
        algorithm=webui_config.jwt_algorithm,
        access_token_expire_minutes=webui_config.jwt_access_token_expire_minutes,
        refresh_token_expire_days=webui_config.jwt_refresh_token_expire_days
    )
    logger.info(f"JWT Manager 已初始化（用于前端用户认证）")

app.add_middleware(
    UnifiedAuthMiddleware,
    api_key=api_key,
    jwt_manager=jwt_manager
)

auth_methods = []
if api_key:
    auth_methods.append("API Key（后端服务）")
if jwt_manager:
    auth_methods.append("JWT Token（前端用户）")

if auth_methods:
    logger.info(f"UnifiedAuthMiddleware 已注册 - 支持鉴权方式: {', '.join(auth_methods)}")
else:
    logger.warning("UnifiedAuthMiddleware 已注册 - 未配置任何鉴权方式，所有请求无需认证")

from jettask.webui.middleware import NamespaceMiddleware
app.add_middleware(NamespaceMiddleware)
logger.info("NamespaceMiddleware 已注册 - 所有包含 {namespace} 的路由将自动注入命名空间上下文")

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  
    allow_credentials=True,
    allow_methods=["*"],  
    allow_headers=["*"],  
    max_age=3600,  
)
logger.info("CORSMiddleware 已注册（最先执行，确保所有响应都带 CORS headers）")

from jettask.webui.api import api_router
app.include_router(api_router)


if __name__ == "__main__":
    logging.basicConfig(
        level=logging.INFO,
        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
    )
    
    uvicorn.run(app, host="0.0.0.0", port=8000)