"""
仪表盘发布节点

将仪表盘发布到菜单系统
"""
import logging
from typing import Any, Dict

from ..base import BaseNode, NodeContext, NodeResult
from ..registry import NodeRegistry

logger = logging.getLogger(__name__)


@NodeRegistry.register
class DashboardPublishNode(BaseNode):
    """
    仪表盘发布节点

    将仪表盘发布到菜单系统，创建菜单项
    """

    node_type = 'dashboard_publish'
    node_name = '仪表盘发布'
    node_category = 'dashboard'
    node_icon = 'upload'
    node_description = '将仪表盘发布到菜单系统'

    inputs = [
        {
            'name': 'dashboard_id',
            'type': 'string',
            'description': '仪表盘ID（来自创建节点）',
            'required': True,
        },
        {
            'name': 'menu_name',
            'type': 'string',
            'description': '菜单名称',
            'required': True,
        },
        {
            'name': 'menu_parent_id',
            'type': 'string',
            'description': '父菜单ID（可选）',
            'required': False,
        },
        {
            'name': 'menu_icon',
            'type': 'string',
            'description': '菜单图标',
            'required': False,
        },
        {
            'name': 'menu_order',
            'type': 'number',
            'description': '菜单排序',
            'required': False,
        },
        {
            'name': 'application_id',
            'type': 'string',
            'description': '所属应用ID',
            'required': False,
        },
    ]

    outputs = [
        {
            'name': 'menu_id',
            'type': 'string',
            'description': '创建的菜单ID',
        },
        {
            'name': 'route_path',
            'type': 'string',
            'description': '访问路径',
        },
        {
            'name': 'publish_result',
            'type': 'object',
            'description': '发布结果',
        },
    ]

    def execute(self, context: NodeContext) -> NodeResult:
        """执行节点（同步方法，通过运行异步方法实现）"""
        import asyncio

        loop = asyncio.get_event_loop()
        if loop.is_running():
            import concurrent.futures
            with concurrent.futures.ThreadPoolExecutor() as executor:
                future = executor.submit(asyncio.run, self.execute_async(context))
                return future.result()
        else:
            return loop.run_until_complete(self.execute_async(context))

    async def execute_async(self, context: NodeContext) -> NodeResult:
        """异步执行节点"""
        try:
            # 检查是否需要确认（支持布尔值、字符串模式和变量引用）
            require_confirmation = self.resolve_require_confirmation(context, default=True)

            # 检查是否有用户输入（确认后的数据）
            user_input = context.user_input
            logger.info(f'DashboardPublishNode - user_input 类型: {type(user_input)}, 值: {user_input}')
            logger.info(f'DashboardPublishNode - require_confirmation: {require_confirmation}')

            # 解析 user_input（可能是字符串或字典）
            user_input_dict = None
            if user_input:
                if isinstance(user_input, str):
                    try:
                        import json
                        user_input_dict = json.loads(user_input)
                        logger.info(f'DashboardPublishNode - 解析 JSON 字符串为字典: {user_input_dict}')
                    except json.JSONDecodeError as e:
                        logger.warning(f'DashboardPublishNode - JSON 解析失败: {e}')
                elif isinstance(user_input, dict):
                    user_input_dict = user_input

            # 检查 user_input 是否包含发布节点需要的字段（dashboard_id 和 menu_name）
            # 如果包含，说明是用户确认后的数据；否则是从上一个节点传递过来的数据
            if user_input_dict and 'dashboard_id' in user_input_dict and 'menu_name' in user_input_dict:
                # 用户已确认，使用确认后的数据
                logger.info(f'DashboardPublishNode - 使用用户确认的发布配置')
                return await self._execute_publish(context, user_input_dict)

            # 获取输入参数
            dashboard_id = context.resolve_template(self.config.get('dashboard_id', ''))
            menu_name = context.resolve_template(self.config.get('menu_name', ''))
            menu_parent_id = context.resolve_template(self.config.get('menu_parent_id', ''))
            menu_icon = context.resolve_template(self.config.get('menu_icon', 'lucide:layout-dashboard'))
            application_id = context.resolve_template(self.config.get('application_id', ''))

            # 处理menu_order
            menu_order_value = self.config.get('menu_order', 0)
            menu_order_str = context.resolve_template(str(menu_order_value))
            try:
                menu_order = int(menu_order_str) if menu_order_str else 0
            except (ValueError, TypeError):
                menu_order = 0

            logger.info(f'DashboardPublishNode - 开始发布仪表盘: {dashboard_id}')

            # 验证必要参数
            if not dashboard_id:
                return NodeResult(
                    success=False,
                    error='仪表盘ID不能为空',
                )

            if not menu_name:
                return NodeResult(
                    success=False,
                    error='菜单名称不能为空',
                )

            # 获取仪表盘的 code（用于显示路由路径）
            dashboard_code = ''
            try:
                from app.database import AsyncSessionLocal
                from online_dev.page_manager.model import PageMeta
                from sqlalchemy import select

                async with AsyncSessionLocal() as db:
                    stmt = select(PageMeta.code).where(PageMeta.id == dashboard_id, PageMeta.is_deleted == False)
                    result = await db.execute(stmt)
                    dashboard_code = result.scalar_one_or_none() or ''
            except Exception as e:
                logger.warning(f'获取仪表盘 code 失败: {e}')

            # 构建发布数据
            publish_data = {
                'dashboard_id': dashboard_id,
                'dashboard_code': dashboard_code,
                'menu_name': menu_name,
                'menu_parent_id': menu_parent_id,
                'menu_icon': menu_icon,
                'menu_order': menu_order,
                'application_id': application_id,
            }

            # 如果需要确认，返回等待用户输入
            if require_confirmation:
                return NodeResult(
                    success=True,
                    output=publish_data,
                    output_variables=publish_data,
                    waiting_for_input=True,
                    waiting_config={
                        'type': 'design_preview',
                        'preview_type': 'dashboard_publish',
                        'title': '仪表盘发布确认',
                        'data': publish_data,
                        'editable': True,
                    },
                )

            # 不需要确认，直接发布
            return await self._execute_publish(context, publish_data)

        except Exception as e:
            logger.error(f'DashboardPublishNode - 执行失败: {str(e)}')
            return NodeResult(
                success=False,
                error=f'发布仪表盘失败: {str(e)}',
            )

    async def _execute_publish(self, context: NodeContext, publish_data: Dict[str, Any]) -> NodeResult:
        """执行发布操作"""
        try:
            dashboard_id = publish_data.get('dashboard_id', '')
            menu_name = publish_data.get('menu_name', '')
            menu_parent_id = publish_data.get('menu_parent_id', '')
            menu_icon = publish_data.get('menu_icon', 'lucide:layout-dashboard')
            menu_order = publish_data.get('menu_order', 0)
            application_id = publish_data.get('application_id', '')

            if not dashboard_id or not menu_name:
                return NodeResult(
                    success=False,
                    error='仪表盘ID和菜单名称不能为空',
                )

            # 发布到数据库
            from app.database import AsyncSessionLocal
            from online_dev.page_manager.model import PageMeta
            from core.menu.model import Menu
            from sqlalchemy import select

            async with AsyncSessionLocal() as db:
                # 获取仪表盘信息
                stmt = select(PageMeta).where(PageMeta.id == dashboard_id, PageMeta.is_deleted == False)
                result = await db.execute(stmt)
                page = result.scalar_one_or_none()

                if not page:
                    return NodeResult(
                        success=False,
                        error=f'仪表盘不存在: {dashboard_id}',
                    )

                # 生成路由路径
                route_path = f'/page-render/{page.code}'

                # 检查菜单是否已存在
                menu_stmt = select(Menu).where(
                    Menu.path == route_path,
                    Menu.is_deleted == False
                )
                menu_result = await db.execute(menu_stmt)
                existing_menu = menu_result.scalar_one_or_none()

                if existing_menu:
                    # 更新现有菜单
                    existing_menu.title = menu_name
                    existing_menu.icon = menu_icon
                    existing_menu.order = menu_order
                    if menu_parent_id:
                        existing_menu.parent_id = menu_parent_id

                    menu_id = existing_menu.id
                    logger.info(f'DashboardPublishNode - 更新菜单: {menu_id}')
                else:
                    # 创建新菜单
                    new_menu = Menu(
                        name=menu_name,
                        title=menu_name,
                        path=route_path,
                        component='_core/page-render/index',
                        icon=menu_icon,
                        order=menu_order,
                        type='online_page',
                        parent_id=menu_parent_id if menu_parent_id else None,
                        application_id=application_id if application_id else page.application_id,
                    )

                    db.add(new_menu)
                    await db.flush()
                    menu_id = new_menu.id
                    logger.info(f'DashboardPublishNode - 创建菜单: {menu_id}')

                # 更新页面状态为已发布
                page.status = 'published'

                await db.commit()

                output_data = {
                    'menu_id': menu_id,
                    'route_path': route_path,
                    'dashboard_id': dashboard_id,
                    'dashboard_code': page.code,
                    'dashboard_name': page.name,
                    'menu_name': menu_name,
                    'status': 'published',
                }

                return NodeResult(
                    success=True,
                    output=output_data,
                    output_variables=output_data,
                )

        except Exception as e:
            logger.error(f'DashboardPublishNode - 执行失败: {str(e)}')
            return NodeResult(
                success=False,
                error=f'发布仪表盘失败: {str(e)}',
            )
