"""
配置服务层
处理配置的 PG 持久化和 Redis 同步
"""
from typing import Optional, Dict, Any, List
from sqlalchemy import select, delete, and_
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.dialects.postgresql import insert as pg_insert
import logging
import json

from jettask.db.models.config import Config

logger = logging.getLogger(__name__)


class ConfigService:
    """配置服务类 - 处理 PG 持久化"""


    @staticmethod
    def _flatten_task_config(configs: Dict[str, Any]) -> Dict[str, Any]:
        flat_config = {}

        if 'rate_limit' in configs:
            rate_limit = configs['rate_limit']
            rate_type = rate_limit.get('type', 'qps')
            flat_config['rate_limit_type'] = rate_type

            if rate_type == 'qps':
                flat_config['rate_limit_qps'] = rate_limit.get('qps', 10)
                flat_config['rate_limit_window_size'] = rate_limit.get('window_size', 1.0)
            elif rate_type == 'concurrency':
                flat_config['rate_limit_max_concurrency'] = rate_limit.get('max_concurrency', 10)

        if 'max_retries' in configs:
            flat_config['max_retries'] = configs['max_retries']

        if 'retry_delay' in configs:
            flat_config['retry_delay'] = configs['retry_delay']

        if 'retry_backoff' in configs:
            backoff = configs['retry_backoff']
            if isinstance(backoff, dict):
                flat_config['retry_backoff'] = backoff.get('type') == 'exponential'
                if 'max' in backoff:
                    flat_config['retry_backoff_max'] = backoff['max']
            elif isinstance(backoff, bool):
                flat_config['retry_backoff'] = backoff

        if 'retry_on_exceptions' in configs:
            flat_config['retry_on_exceptions'] = configs['retry_on_exceptions']

        return flat_config

    @staticmethod
    async def save_task_config_to_pg(
        pg_session: AsyncSession,
        namespace: str,
        queue: str,
        task_name: str,
        configs: Dict[str, Any]
    ) -> bool:
        try:
            scope = Config.make_task_scope(namespace, queue, task_name)

            flat_config = ConfigService._flatten_task_config(configs)

            for key, value in flat_config.items():
                stmt = pg_insert(Config).values(
                    config_type='task',
                    scope=scope,
                    key=key,
                    value=value
                ).on_conflict_do_update(
                    index_elements=['config_type', 'scope', 'key'],
                    set_={
                        'value': value
                    }
                )
                await pg_session.execute(stmt)

            await pg_session.commit()
            logger.info(f"任务配置已保存到 PG: {scope}, keys={list(flat_config.keys())}")
            return True

        except Exception as e:
            await pg_session.rollback()
            logger.error(f"保存任务配置到 PG 失败: {e}", exc_info=True)
            return False

    @staticmethod
    async def delete_task_config_from_pg(
        pg_session: AsyncSession,
        namespace: str,
        queue: str,
        task_name: str,
        keys: Optional[List[str]] = None
    ) -> bool:
        try:
            scope = Config.make_task_scope(namespace, queue, task_name)

            if keys:
                stmt = delete(Config).where(
                    and_(
                        Config.config_type == 'task',
                        Config.scope == scope,
                        Config.key.in_(keys)
                    )
                )
            else:
                stmt = delete(Config).where(
                    and_(
                        Config.config_type == 'task',
                        Config.scope == scope
                    )
                )

            await pg_session.execute(stmt)
            await pg_session.commit()
            logger.info(f"任务配置已从 PG 删除: {scope}, keys={keys}")
            return True

        except Exception as e:
            await pg_session.rollback()
            logger.error(f"从 PG 删除任务配置失败: {e}", exc_info=True)
            return False

    @staticmethod
    async def get_task_config_from_pg(
        pg_session: AsyncSession,
        namespace: str,
        queue: str,
        task_name: str
    ) -> Dict[str, Any]:
        try:
            scope = Config.make_task_scope(namespace, queue, task_name)

            stmt = select(Config).where(
                and_(
                    Config.config_type == 'task',
                    Config.scope == scope
                )
            )

            result = await pg_session.execute(stmt)
            rows = result.scalars().all()

            configs = {}
            for row in rows:
                configs[row.key] = row.value

            return configs

        except Exception as e:
            logger.error(f"从 PG 获取任务配置失败: {e}", exc_info=True)
            return {}

    @staticmethod
    async def get_all_task_configs_from_pg(
        pg_session: AsyncSession,
        namespace: Optional[str] = None
    ) -> Dict[str, Dict[str, Any]]:
        try:
            if namespace:
                stmt = select(Config).where(
                    and_(
                        Config.config_type == 'task',
                        Config.scope.like(f"{namespace}:%")
                    )
                )
            else:
                stmt = select(Config).where(Config.config_type == 'task')

            result = await pg_session.execute(stmt)
            rows = result.scalars().all()

            configs_by_scope: Dict[str, Dict[str, Any]] = {}
            for row in rows:
                if row.scope not in configs_by_scope:
                    configs_by_scope[row.scope] = {}
                configs_by_scope[row.scope][row.key] = row.value

            return configs_by_scope

        except Exception as e:
            logger.error(f"从 PG 获取所有任务配置失败: {e}", exc_info=True)
            return {}


    @staticmethod
    async def save_system_config_to_pg(
        pg_session: AsyncSession,
        key: str,
        value: Any,
        description: Optional[str] = None
    ) -> bool:
        try:
            stmt = pg_insert(Config).values(
                config_type='system',
                scope=None,
                key=key,
                value=value,
                description=description
            ).on_conflict_do_update(
                index_elements=['config_type', 'scope', 'key'],
                set_={
                    'value': value,
                    'description': description
                }
            )
            await pg_session.execute(stmt)
            await pg_session.commit()
            logger.info(f"系统配置已保存到 PG: {key}={value}")
            return True

        except Exception as e:
            await pg_session.rollback()
            logger.error(f"保存系统配置到 PG 失败: {e}", exc_info=True)
            return False

    @staticmethod
    async def delete_system_config_from_pg(
        pg_session: AsyncSession,
        key: str
    ) -> bool:
        try:
            stmt = delete(Config).where(
                and_(
                    Config.config_type == 'system',
                    Config.scope.is_(None),
                    Config.key == key
                )
            )
            await pg_session.execute(stmt)
            await pg_session.commit()
            logger.info(f"系统配置已从 PG 删除: {key}")
            return True

        except Exception as e:
            await pg_session.rollback()
            logger.error(f"从 PG 删除系统配置失败: {e}", exc_info=True)
            return False

    @staticmethod
    async def get_all_system_configs_from_pg(
        pg_session: AsyncSession
    ) -> Dict[str, Any]:
        try:
            stmt = select(Config).where(
                and_(
                    Config.config_type == 'system',
                    Config.scope.is_(None)
                )
            )

            result = await pg_session.execute(stmt)
            rows = result.scalars().all()

            configs = {}
            for row in rows:
                configs[row.key] = row.value

            return configs

        except Exception as e:
            logger.error(f"从 PG 获取系统配置失败: {e}", exc_info=True)
            return {}


    @staticmethod
    async def sync_task_configs_to_redis(
        pg_session: AsyncSession,
        namespace: str,
        redis_client,
        redis_prefix: str
    ) -> int:
        from jettask.config import DynamicConfig

        try:
            configs_by_scope = await ConfigService.get_all_task_configs_from_pg(
                pg_session, namespace
            )

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

                task_name = parsed['task_name']

                await ConfigService._write_flat_config_to_redis(
                    redis_client, redis_prefix, task_name, configs
                )
                sync_count += 1

            logger.info(f"已同步 {sync_count} 个任务配置到 Redis: namespace={namespace}")
            return sync_count

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

    @staticmethod
    async def _write_flat_config_to_redis(
        redis_client,
        prefix: str,
        task_name: str,
        configs: Dict[str, Any]
    ):
        from jettask.config import DynamicConfig

        config_key = DynamicConfig.get_task_config_key(prefix, task_name)
        index_key = DynamicConfig.get_task_index_key(prefix)
        version_key = DynamicConfig.get_version_key(prefix)

        flat_config = {}
        for k, v in configs.items():
            if isinstance(v, (dict, list)):
                flat_config[k] = json.dumps(v)
            elif isinstance(v, bool):
                flat_config[k] = 'true' if v else 'false'
            else:
                flat_config[k] = str(v)

        if flat_config:
            await redis_client.hset(config_key, mapping=flat_config)
            await redis_client.sadd(index_key, task_name)
            await redis_client.incr(version_key)

    @staticmethod
    async def sync_system_configs_to_redis(
        pg_session: AsyncSession,
        redis_client,
        redis_prefix: str
    ) -> int:
        from jettask.config import DynamicConfig

        try:
            configs = await ConfigService.get_all_system_configs_from_pg(pg_session)

            sync_count = 0
            for key, value in configs.items():
                await DynamicConfig.set_system_config_to_redis(
                    redis_client, redis_prefix, key, value,
                    publish_change=False
                )
                sync_count += 1

            logger.info(f"已同步 {sync_count} 个系统配置到 Redis")
            return sync_count

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



    VERSION_FIELD = '_version'

    @staticmethod
    async def set_task_version_in_pg(
        pg_session: AsyncSession,
        namespace: str,
        queue: str,
        task_name: str,
        version: int
    ) -> bool:
        try:
            scope = Config.make_task_scope(namespace, queue, task_name)

            stmt = pg_insert(Config).values(
                config_type='task',
                scope=scope,
                key=ConfigService.VERSION_FIELD,
                value=version
            ).on_conflict_do_update(
                index_elements=['config_type', 'scope', 'key'],
                set_={'value': version}
            )
            await pg_session.execute(stmt)
            await pg_session.commit()

            logger.debug(f"PG 任务版本号已更新: {scope} -> {version}")
            return True

        except Exception as e:
            await pg_session.rollback()
            logger.error(f"更新 PG 任务版本号失败: {e}", exc_info=True)
            return False

    @staticmethod
    async def get_task_version_from_pg(
        pg_session: AsyncSession,
        namespace: str,
        queue: str,
        task_name: str
    ) -> int:
        try:
            scope = Config.make_task_scope(namespace, queue, task_name)

            stmt = select(Config).where(
                and_(
                    Config.config_type == 'task',
                    Config.scope == scope,
                    Config.key == ConfigService.VERSION_FIELD
                )
            )
            result = await pg_session.execute(stmt)
            row = result.scalar_one_or_none()

            if row and row.value is not None:
                return int(row.value)
            return 0

        except Exception as e:
            logger.error(f"从 PG 获取任务版本号失败: {e}", exc_info=True)
            return 0


__all__ = ['ConfigService']
