import jingrow
import json
import os
import shutil
import subprocess
import tempfile
import datetime
from pathlib import Path
from typing import Dict, List, Any, Optional
from jingrow.modules.utils import get_pagetype_module, get_module_app
from jingrow.modules.export_file import export_pg
from jingrow import _
from jingrow.permissions import get_valid_perms, get_roles, rights as PERMISSION_RIGHTS

@jingrow.whitelist()
def get_local_ai_agents_list(page: int = 1, page_size: int = 10, fields: List = None, order_by: str = "modified desc") -> Dict:
    """
    获取AI智能体列表 - 支持分页
    返回格式: {"items": [...], "total": int, "page": int, "page_size": int}
    """
    try:
        # 参数验证和默认值
        page = max(1, int(page)) if page else 1
        page_size = max(1, min(100, int(page_size))) if page_size else 10  # 限制在1-100之间
        
        if not fields:
            fields = [
                'name', 'agent_name', 'status', 'enabled', 'trigger_mode', 
                'progress', 'ai_repeat', 'creation', 'modified', 'modified_by', 'description'
            ]
        
        # 构建过滤条件
        filters = []
        
        # 计算分页
        limit_start = (page - 1) * page_size
        
        # 获取总数（用于分页计算）
        total_filters = filters.copy()
        total_count = jingrow.get_list(
            pagetype="Local Ai Agent",
            fields=["name"],
            filters=total_filters,
            limit_start=0,
            limit_page_length=0
        )
        total = len(total_count) if total_count else 0
        
        # 获取分页数据
        query_kwargs = {
            "fields": fields,
            "order_by": order_by,
            "limit_start": limit_start,
            "limit_page_length": page_size,
        }
        
        if filters:
            query_kwargs["filters"] = filters
        
        agents = jingrow.get_list("Local Ai Agent", **query_kwargs)
        
        return {
            "items": agents or [],
            "total": total,
            "page": page,
            "page_size": page_size
        }
        
    except Exception as e:
        jingrow.log_error("获取智能体列表失败", str(e))
        return {
            "items": [],
            "total": 0,
            "page": page,
            "page_size": page_size
        }

@jingrow.whitelist()
def get_local_ai_agent_detail(name: str) -> Optional[Dict]:
    """
    获取单个AI智能体详情
    """
    try:
        agents = jingrow.get_list(
            pagetype='Local Ai Agent',
            fields=['name', 'agent_name', 'status', 'enabled', 'trigger_mode', 
                   'progress', 'ai_repeat', 'creation', 'modified', 'modified_by',
                   'agent_flow', 'exclude_prompts'],
            filters=[['name', '=', name]],
            limit_start=0,
            limit_page_length=1
        )
        
        if not agents:
            return {
                'success': False,
                'message': 'Local Ai Agent不存在',
                'data': None
            }
            
        return {
            'success': True,
            'message': '获取成功',
            'data': agents[0]
        }
        
    except Exception as e:
        jingrow.log_error(f"获取AI智能体详情失败: {name}", str(e))
        return {
            'success': False,
            'message': f'获取失败: {str(e)}',
            'data': None
        }

@jingrow.whitelist()
def execute_local_ai_agent(name: str) -> Dict[str, Any]:
    """
    执行AI智能体
    """
    try:
        # 调用AI智能体执行方法
        from jingrow.ai.pagetype.ai_agent.ai_agent import execute_agent_task
        result = execute_agent_task(name, 'Local Ai Agent')
        
        return {
            'success': True,
            'message': '执行成功',
            'result': result
        }
        
    except Exception as e:
        jingrow.log_error(f"执行AI智能体失败: {name}", str(e))
        return {
            'success': False,
            'message': f'执行失败: {str(e)}',
            'result': None
        }

@jingrow.whitelist()
def get_available_node_types() -> List[str]:
    """
    获取所有可用的节点类型
    """
    try:
        # 扫描节点目录获取所有可用的节点类型
        base_path = jingrow.get_app_path("jingrow")
        nodes_path = os.path.join(base_path, "ai", "nodes")
        
        node_types = []
        if os.path.exists(nodes_path):
            for item in os.listdir(nodes_path):
                item_path = os.path.join(nodes_path, item)
                if os.path.isdir(item_path):
                    # 检查是否有对应的schema文件
                    schema_file = os.path.join(item_path, f"{item}.json")
                    if os.path.exists(schema_file):
                        node_types.append(item)
        
        # 如果没有找到节点，返回默认的节点类型
        if not node_types:
            node_types = [
                'ai_content_generation',
                'ai_image_generation', 
                'input_record',
                'update_record',
                'create_record',
                'condition_check',
                'image_upload'
            ]
        
        return node_types
        
    except Exception as e:
        jingrow.log_error("获取节点类型失败", str(e))
        return [
            'ai_content_generation',
            'ai_image_generation', 
            'input_record',
            'update_record',
            'create_record',
            'condition_check',
            'image_upload'
        ]

@jingrow.whitelist()
def update_local_ai_agent(name: str, data: Dict[str, Any]) -> Dict[str, Any]:

    try:
        agent = jingrow.get_pg('Local Ai Agent', name)       
        for field, value in data.items():
            if hasattr(agent, field):
                field_type = getattr(agent.meta, 'get_field', lambda x: None)(field)
                if field_type and field_type.fieldtype == 'JSON' and isinstance(value, dict):
                    # JSON 字段保持格式化
                    import json
                    setattr(agent, field, json.dumps(value, ensure_ascii=False, indent=2))
                else:
                    setattr(agent, field, value)
        
        # 保存更新
        agent.save()
        jingrow.db.commit()
        
        return {
            'success': True,
            'message': '更新成功'
        }
        
    except Exception as e:
        jingrow.log_error(f"更新AI智能体失败: {name}", str(e))
        return {
            'success': False,
            'message': f'更新失败: {str(e)}'
        }

@jingrow.whitelist()
def get_jflow_dashboard_stats() -> Dict[str, Any]:
    """
    获取Jflow仪表板统计数据
    """
    try:
        # 获取AI智能体统计
        agents_result = get_local_ai_agents_list(page=1, page_size=1000)
        agents = agents_result.get('items', [])
        
        stats = {
            'total_agents': len(agents),
            'enabled_agents': len([a for a in agents if a.get('enabled', False)]),
            'active_agents': len([a for a in agents if a.get('status') == '进行中']),
            'completed_agents': len([a for a in agents if a.get('status') == '已完成']),
            'total_nodes': len(get_available_node_types()),
            'total_executions': 0,  # 这里可以添加执行记录统计
            'active_flows': 0  # 这里可以添加流程统计
        }
        
        return stats
        
    except Exception as e:
        jingrow.log_error("获取仪表板统计失败", str(e))
        return {
            'total_agents': 0,
            'enabled_agents': 0,
            'active_agents': 0,
            'completed_agents': 0,
            'total_nodes': 0,
            'total_executions': 0,
            'active_flows': 0
        }

@jingrow.whitelist()
def check_node_type_exists(node_type: str) -> bool:
    """检查节点类型是否已存在，返回布尔值"""
    if not node_type:
        return False
    
    try:
        pg = jingrow.get_pg('Local Ai Node', {"node_type": node_type})
        return pg is not None
    except Exception:
        return False

@jingrow.whitelist()
def update_node(name: str, node_data: Dict) -> Dict:
    """更新节点的所有字段数据到数据库"""
    if not name:
        jingrow.throw("参数错误", "name不能为空")
    
    try:
        # 获取节点记录
        pg = jingrow.get_pg('Local Ai Node', name)
        if not pg:
            jingrow.throw("节点不存在", f"节点 {name} 不存在")
        
        # 更新所有字段
        for field, value in node_data.items():
            if hasattr(pg, field):
                field_type = getattr(pg.meta, 'get_field', lambda x: None)(field)
                if field_type and field_type.fieldtype == 'JSON' and isinstance(value, dict):
                    # JSON 字段保持格式化
                    setattr(pg, field, json.dumps(value, ensure_ascii=False, indent=2))
                else:
                    setattr(pg, field, value)
        
        pg.save()
        
        return {"success": True, "message": "节点更新成功"}
        
    except Exception as e:
        jingrow.log_error(f"更新节点失败: {name}", str(e))
        jingrow.throw("更新失败", f"节点 {name}: {str(e)}")

@jingrow.whitelist()
def get_node_record(name: str = None) -> Dict:
    if not name:
        jingrow.throw("参数错误", "name不能为空")
    
    try:
        # 通过name主键获取记录，性能更好
        pg = jingrow.get_pg('Local Ai Node', name)
        if not pg:
            jingrow.throw("节点不存在", f"节点 {name} 不存在")
        
        # 返回完整的记录信息
        result = {
            "name": pg.name,
            "node_type": pg.node_type,
            "node_label": pg.node_label,
            "node_group": pg.node_group,
            "node_component": pg.node_component,
            "node_icon": pg.node_icon,
            "node_color": pg.node_color,
            "node_description": pg.node_description,
            "status": pg.status,
            "node_schema": pg.node_schema,
        }
        
        # 如果 node_schema 是字符串，解析为 JSON 对象
        if pg.node_schema:
            if isinstance(pg.node_schema, str):
                try:
                    result["node_schema"] = json.loads(pg.node_schema)
                except json.JSONDecodeError as e:
                    jingrow.throw("Schema格式错误", f"节点 {name} 的node_schema不是有效的JSON: {str(e)}")
        
        return result
        
    except Exception as e:
        jingrow.log_error(f"获取节点记录失败: {name}", str(e))
        jingrow.throw("节点加载失败", f"节点 {name}: {str(e)}")

@jingrow.whitelist()
def get_node_by_type(node_type: str = None) -> Dict:
    
    try:
        pg = jingrow.get_pg('Local Ai Node', {"node_type": node_type})
        if not pg:
            jingrow.throw("节点不存在", f"节点类型 {node_type} 不存在")
        
        # 返回完整的记录信息
        result = {
            "name": pg.name,
            "node_type": pg.node_type,
            "node_label": pg.node_label,
            "node_group": pg.node_group,
            "node_component": pg.node_component,
            "node_icon": pg.node_icon,
            "node_color": pg.node_color,
            "node_description": pg.node_description,
            "status": pg.status,
            "node_schema": pg.node_schema,
        }
        
        # 如果 node_schema 是字符串，解析为 JSON 对象
        if pg.node_schema:
            if isinstance(pg.node_schema, str):
                try:
                    result["node_schema"] = json.loads(pg.node_schema)
                except json.JSONDecodeError as e:
                    jingrow.throw("Schema格式错误", f"节点 {node_type} 的node_schema不是有效的JSON: {str(e)}")
        
        return result
        
    except Exception as e:
        jingrow.log_error(f"获取节点记录失败: {node_type}", str(e))
        jingrow.throw("节点加载失败", f"节点类型 {node_type}: {str(e)}")

@jingrow.whitelist()
def get_node_list(page: int = 1, page_size: int = 10) -> Dict:

    try:
        # 参数验证和默认值
        page = max(1, int(page)) if page else 1
        page_size = max(1, min(100, int(page_size))) if page_size else 10  # 限制在1-100之间
        
        # 构建过滤条件
        filters = []
        
        # 计算分页
        limit_start = (page - 1) * page_size
        
        # 获取总数（用于分页计算）
        total_filters = filters.copy()
        total_count = jingrow.get_list(
            pagetype="Local Ai Node",
            fields=["name"],
            filters=total_filters,
            limit_start=0,
            limit_page_length=0
        )
        total = len(total_count) if total_count else 0
        
        # 获取分页数据
        query_kwargs = {
            "fields": [
                "name",
                "node_type", 
                "node_label",
                "node_group",
                "node_component",
                "node_icon",
                "node_color",
                "node_description",
                "status",
            ],
            "order_by": "modified desc",
            "limit_start": limit_start,
            "limit_page_length": page_size,
        }
        
        if filters:
            query_kwargs["filters"] = filters
        
        nodes = jingrow.get_list("Local Ai Node", **query_kwargs)
        
        return {
            "items": nodes or [],
            "total": total,
            "page": page,
            "page_size": page_size
        }
        
    except Exception as e:
        jingrow.log_error("获取节点列表失败", str(e))

@jingrow.whitelist()
def get_meta(pagetype: str) -> Dict[str, Any]:
    """
    获取页面类型的元数据
    """
    try:
        meta = jingrow.get_meta(pagetype)
        
        # 只返回必要的字段信息
        fields = []
        if hasattr(meta, 'fields') and meta.fields:
            for field in meta.fields:
                fields.append({
                    'fieldname': field.fieldname,
                    'label': field.label,
                    'fieldtype': field.fieldtype
                })
        
        return {
            'success': True,
            'data': {
                'name': meta.name,
                'fields': fields
            }
        }
        
    except Exception as e:
        jingrow.log_error(f"获取页面类型元数据失败: {pagetype}", str(e))
        return {
            'success': False,
            'error': str(e)
        }

@jingrow.whitelist()
def get_field_mapping(pagetype: str) -> Dict[str, Any]:
    """
    获取页面类型的字段映射（label到fieldname的映射）
    """
    try:
        meta = jingrow.get_meta(pagetype)
        
        field_mapping = {}
        if hasattr(meta, 'fields') and meta.fields:
            for field in meta.fields:
                if hasattr(field, 'label') and hasattr(field, 'fieldname'):
                    label = str(field.label).strip()
                    fieldname = str(field.fieldname).strip()
                    field_mapping[label] = fieldname
                    
                    try:
                        user_lang = jingrow.get_pg("User", jingrow.session.user).language or jingrow.get_single("System Settings").language or "zh"
                        translated_label = jingrow._(label, lang=user_lang)
                        if translated_label != label and translated_label not in field_mapping:
                            field_mapping[translated_label] = fieldname
                    except:
                        pass
        
        return {
            'success': True,
            'field_mapping': field_mapping
        }
        
    except Exception as e:
        jingrow.log_error(f"获取字段映射失败: {pagetype}", str(e))
        return {
            'success': False,
            'error': str(e),
            'field_mapping': {}
        }

@jingrow.whitelist()
def get_field_value(pagetype: str, name: str, fieldname: str) -> Dict[str, Any]:
    """
    获取页面类型记录的字段值
    供jflow框架调用，用于检查字段当前值
    
    :param pagetype: 页面类型名称
    :param name: 记录名称
    :param fieldname: 字段名称
    :return: 字段值
    """
    try:
        pg = jingrow.get_pg(pagetype, name)
        field_value = pg.get(fieldname)
        
        return {
            'success': True,
            'pagetype': pagetype,
            'name': name,
            'fieldname': fieldname,
            'value': field_value
        }
        
    except Exception as e:
        jingrow.log_error(f"获取字段值失败: {pagetype}.{name}.{fieldname}", str(e))
        return {
            'success': False,
            'error': f'获取字段值失败: {str(e)}',
            'value': None
        }

@jingrow.whitelist()
def set_field_value(pagetype: str, name: str, fieldname: str, value: Any) -> Dict[str, Any]:
    """
    设置页面类型记录的字段值
    供jflow框架调用，用于更新字段值
    
    :param pagetype: 页面类型名称
    :param name: 记录名称
    :param fieldname: 字段名称
    :param value: 要设置的值
    :return: 操作结果
    """
    try:
        jingrow.db.set_value(pagetype, name, fieldname, value)
        jingrow.db.commit()
        
        return {
            'success': True,
            'message': f'字段 {fieldname} 更新成功',
            'pagetype': pagetype,
            'name': name,
            'fieldname': fieldname,
            'value': value
        }
        
    except Exception as e:
        jingrow.log_error(f"设置字段值失败: {pagetype}.{name}.{fieldname}", str(e))
        return {
            'success': False,
            'error': f'设置字段值失败: {str(e)}'
        }

@jingrow.whitelist()
def get_ai_settings() -> Dict[str, Any]:
    """
    获取Ai Settings配置
    供jflow框架调用，返回整个Ai Settings pagetype的所有字段
    """
    try:
        # 获取Ai Settings记录
        ai_settings = jingrow.get_single("Ai Settings")
        if not ai_settings:
            return {
                'success': False,
                'error': 'Ai Settings配置不存在'
            }
        
        # 返回整个Ai Settings记录的所有字段
        config = {}
        for field in ai_settings.meta.fields:
            fieldname = field.fieldname
            try:
                if field.fieldtype == 'Password':
                    # 密码字段使用get_password方法
                    config[fieldname] = ai_settings.get_password(fieldname)
                else:
                    # 其他字段直接获取
                    config[fieldname] = ai_settings.get(fieldname)
            except:
                config[fieldname] = None
        
        return {
            'success': True,
            'config': config
        }
        
    except Exception as e:
        jingrow.log_error(f"获取Ai Settings配置失败", str(e))
        return {
            'success': False,
            'error': f'获取Ai Settings配置失败: {str(e)}'
        }


@jingrow.whitelist()
def get_user_pagetype_permissions(user: Optional[str] = None) -> Dict[str, Any]:
    """获取指定用户在所有 PageType 上的角色权限汇总"""
    try:
        if not user:
            user = jingrow.session.user

        roles = get_roles(user)
        perms = get_valid_perms(user=user)

        pagetype_perms: Dict[str, Dict[str, int]] = {}

        for p in perms or []:
            pagetype = getattr(p, "parent", None)
            if not pagetype:
                continue

            if pagetype not in pagetype_perms:
                pagetype_perms[pagetype] = {ptype: 0 for ptype in PERMISSION_RIGHTS}

            for ptype in PERMISSION_RIGHTS:
                if getattr(p, ptype, 0):
                    pagetype_perms[pagetype][ptype] = 1

        return {
            "success": True,
            "user": user,
            "roles": roles,
            "permissions": pagetype_perms,
        }

    except Exception as e:
        jingrow.log_error("获取用户权限失败", str(e))
        return {
            "success": False,
            "error": str(e),
        }


@jingrow.whitelist()
def get_single_pagetype(pagetype: str) -> Dict[str, Any]:
    """获取single类型pagetype记录"""
    try:
        record = jingrow.get_single(pagetype)
        if not record:
            return {
                'success': False,
                'error': f'{pagetype}配置不存在'
            }
        
        # 获取所有字段值
        config = {}
        for field in record.meta.fields:
            fieldname = field.fieldname
            if field.fieldtype == 'Password':
                config[fieldname] = record.get_password(fieldname)
            else:
                config[fieldname] = record.get(fieldname)
        
        return {'success': True, 'config': config}
        
    except Exception as e:
        jingrow.log_error(f"获取single类型pagetype失败: {pagetype}", str(e))
        return {
            'success': False,
            'error': f'获取{pagetype}配置失败: {str(e)}'
        }

@jingrow.whitelist()
def get_pagetype_module_app(pagetype: str) -> Dict[str, Any]:
    """
    获取指定PageType的 module 与 app（高效，使用内置缓存）
    支持 custom pagetype（custom=1），直接从 Module Def 获取 app_name
    """

    try:
        module = get_pagetype_module(pagetype)
        module_scrub = jingrow.scrub(module)
        
        # 先查缓存
        app = jingrow.local.module_app.get(module_scrub)
        
        # 未命中则一次性读取 Module Def，custom 优先 package，否则取 app_name，最后兜底
        if app is None:
            module_def = jingrow.get_pg("Module Def", {"module_name": module})
            if module_def.custom:
                app = module_def.package or module_def.app_name
            else:
                app = get_module_app(module)

            jingrow.local.module_app[module_scrub] = app
        
        return {
            'success': True,
            'pagetype': pagetype,
            'module': module,
            'app': app,
        }
    except Exception as e:
        jingrow.log_error(f"获取pagetype模块信息失败: {pagetype}", str(e))
        return {
            'success': False,
            'error': str(e),
        }

@jingrow.whitelist()
def sync_app_files(app_name: str, app_path: str, force: bool = True) -> Dict[str, Any]:
    """同步应用文件到数据库（导入 Package 和 PageTypes）"""
    try:
        if not os.path.exists(app_path):
            return {
                "success": False,
                "error": f"应用目录不存在: {app_path}"
            }
        
        files = []
        
        # 使用 jingrow 的 get_pg_files 来收集所有 pagetype 文件
        from jingrow.model.sync import get_pg_files
        from jingrow.modules.import_file import import_file_by_path, import_pg
        import json
        
        # 查找 package.json
        package_file = os.path.join(app_path, f"{app_name}.json")
        
        # 先导入 Package（如果存在），设置上下文
        package_doc = None
        if os.path.exists(package_file):
            try:
                with open(package_file, 'r', encoding='utf-8') as f:
                    pg_dict = json.load(f)
                    if pg_dict.get('pagetype') == 'Package':
                        # 检查 Package 是否已存在于数据库
                        package_name = pg_dict.get('name') or app_name
                        if jingrow.db.exists('Package', package_name):
                            # 使用已有的 Package
                            package_doc = jingrow.get_pg('Package', package_name)
                        else:
                            # 导入新的 Package
                            package_doc = import_pg(pg_dict, ignore_version=True)
                        jingrow.flags.package = package_doc
            except Exception:
                pass
        
        for item in os.listdir(app_path):
            item_path = os.path.join(app_path, item)
            if os.path.isdir(item_path) and item not in ['__pycache__', 'public', 'config']:
                files = get_pg_files(files, item_path)
        
        imported_files = []
        for file_path in files:
            try:
                import_file_by_path(file_path, force=force, ignore_version=True)
                jingrow.db.commit()
                imported_files.append(file_path)
            except Exception:
                pass
        
        return {
            "success": True,
            "message": f"成功导入 {len(imported_files)} 个文件",
            "app_name": app_name,
            "file_count": len(imported_files),
            "imported_files": imported_files
        } 
        
    except Exception as e:
        return {
            "success": False,
            "error": str(e)
        }

@jingrow.whitelist()
def install_package(package_file_url: str) -> Dict[str, Any]:
	"""一键安装包到数据库"""
	try:

		filename = package_file_url.lstrip('/files/').lstrip('files/')
		
		site_path = jingrow.get_site_path()
		file_path = os.path.join(site_path, "public", "files", filename)
		file_path = os.path.abspath(file_path)
		
		if not os.path.exists(file_path):
			return {
				'success': False,
				'error': f'文件不存在: {file_path}'
			}
		
		temp_dir = tempfile.mkdtemp(prefix="jingrow_package_install_")
		
		try:
			result = subprocess.run(
				["tar", "xzf", file_path, "-C", temp_dir],
				capture_output=True,
				text=True,
				check=False
			)
			
			if result.returncode != 0:
				return {'success': False, 'error': f"解压失败: {result.stderr}"}
			
			root_items = os.listdir(temp_dir)
			if not root_items:
				return {'success': False, 'error': '压缩包为空'}
			
			root_dir = os.path.join(temp_dir, root_items[0]) if len(root_items) == 1 and os.path.isdir(os.path.join(temp_dir, root_items[0])) else temp_dir
			package_name = os.path.basename(root_dir)
			package_json_path = os.path.join(root_dir, f"{package_name}.json")
			if not os.path.exists(package_json_path):
				# 搜索所有 json 文件
				for root, dirs, files in os.walk(root_dir):
					for file in files:
						if file.endswith('.json'):
							try:
								with open(os.path.join(root, file), 'r', encoding='utf-8') as f:
									data = json.load(f)
									if data.get('pagetype') == 'Package':
										package_json_path = os.path.join(root, file)
										break
							except:
								pass
						if os.path.exists(package_json_path):
							break
					if os.path.exists(package_json_path):
						break
			
			if not os.path.exists(package_json_path):
				return {'success': False, 'error': '找不到 Package.json 文件'}
			
			from jingrow.modules.import_file import import_pg, import_file_by_path
			from jingrow.model.sync import get_pg_files
			
			with open(package_json_path, 'r', encoding='utf-8') as f:
				pg_dict = json.load(f)
			
			package_doc = import_pg(pg_dict, ignore_version=True)
			jingrow.flags.package = package_doc
			files = []
			for module in os.listdir(root_dir):
				module_path = os.path.join(root_dir, module)
				if os.path.isdir(module_path):
					files = get_pg_files(files, module_path)
			
			imported_files = []
			for file_path_item in files:
				try:
					import_file_by_path(file_path_item, force=True, ignore_version=True)
					imported_files.append(file_path_item)
				except Exception:
					pass
			
			shutil.rmtree(temp_dir)
			
			return {
				'success': True,
				'message': f'扩展包 {package_name} 安装成功',
				'package_name': package_name,
				'imported_files': imported_files,
				'file_count': len(imported_files)
			}
			
		except Exception as e:
			return {'success': False, 'error': str(e)}
		
	except Exception as e:
		return {'success': False, 'error': str(e)}

@jingrow.whitelist()
def get_all_node_metadata():
    """获取所有节点元数据（从 jingrow/ai/nodes 目录扫描）"""
    try:
        base_path = jingrow.get_app_path("jingrow")
        nodes_path = os.path.join(base_path, "ai", "nodes")
        
        if not os.path.exists(nodes_path):
            return {"success": True, "data": {}}
        
        metadata_map = {}
        
        # 遍历节点目录
        for item in os.listdir(nodes_path):
            item_path = os.path.join(nodes_path, item)
            if not os.path.isdir(item_path):
                continue
            
            # 检查是否有对应的JSON配置文件
            json_file = os.path.join(item_path, f"{item}.json")
            if not os.path.exists(json_file):
                continue
            
            try:
                with open(json_file, 'r', encoding='utf-8') as f:
                    node_config = json.load(f)
                
                # 提取 metadata
                metadata = node_config.get('metadata', {})
                if metadata:
                    metadata_map[item] = metadata
                    
            except Exception as e:
                jingrow.log_error(f"读取节点配置失败: {item}", str(e))
                continue
        
        return {"success": True, "data": metadata_map}
        
    except Exception as e:
        jingrow.log_error("获取节点元数据失败", str(e))
        return {"success": False, "error": str(e), "data": {}}

@jingrow.whitelist()
def get_node_schema(node_type):
	"""获取指定节点的 Schema 配置"""
	if not node_type:
		return {"success": False, "error": "node_type不能为空", "data": {}}
	
	try:
		base_path = jingrow.get_app_path("jingrow")
		json_file = os.path.join(base_path, "ai", "nodes", node_type, f"{node_type}.json")
		
		if not os.path.exists(json_file):
			return {"success": False, "error": f"节点 {node_type} 不存在", "data": {}}
		
		with open(json_file, 'r', encoding='utf-8') as f:
			node_config = json.load(f)
		
		return {"success": True, "data": node_config}
		
	except Exception as e:
		jingrow.log_error(f"获取节点Schema失败: {node_type}", str(e))
		return {"success": False, "error": str(e), "data": {}}


@jingrow.whitelist()
def get_node_schema_fields(node_type: str) -> Dict[str, Any]:
	"""获取指定节点类型的Schema字段列表"""
	if not node_type:
		return {"success": True, "fields": []}
	
	try:
		base_path = jingrow.get_app_path("jingrow")
		json_file = os.path.join(base_path, "ai", "nodes", node_type, f"{node_type}.json")
		
		if not os.path.exists(json_file):
			return {"success": True, "fields": []}
		
		with open(json_file, 'r', encoding='utf-8') as f:
			data = json.load(f)
		
		# 移除metadata获取schema
		schema = dict(data)
		schema.pop('metadata', None)
		
		# 提取字段列表
		fields = []
		if schema.get('properties'):
			for name, config in schema['properties'].items():
				fields.append({
					'name': name,
					'label': config.get('title', name),
					'type': config.get('type', 'string'),
					'description': config.get('description', '')
				})
		
		return {"success": True, "fields": fields}
		
	except FileNotFoundError:
		return {"success": True, "fields": []}
	except Exception as e:
		jingrow.log_error(f"获取节点字段失败: {node_type}", str(e))
		return {"success": True, "fields": []}


@jingrow.whitelist()
def import_local_nodes() -> Dict[str, Any]:
	"""一键导入本地节点到Ai Node"""
	try:
		# 扫描本地节点目录
		base_path = jingrow.get_app_path("jingrow")
		nodes_path = os.path.join(base_path, "ai", "nodes")
		
		if not os.path.exists(nodes_path):
			return {
				"success": True,
				"matched": 0,
				"imported": 0,
				"skipped_existing": 0,
				"errors": []
			}
		
		matched = 0
		imported = 0
		skipped_existing = 0
		errors = []
		
		# 遍历节点目录
		for item in os.listdir(nodes_path):
			item_path = os.path.join(nodes_path, item)
			if not os.path.isdir(item_path):
				continue
			
			# 检查是否有对应的JSON配置文件
			json_file = os.path.join(item_path, f"{item}.json")
			if not os.path.exists(json_file):
				continue
			
			matched += 1
			
			try:
				# 读取节点配置
				with open(json_file, 'r', encoding='utf-8') as f:
					node_config = json.load(f)
				
				# 提取元数据
				metadata = node_config.get('metadata', {})
				node_type = item
				node_label = metadata.get('label', item)
				node_group = metadata.get('group', '')
				node_component = metadata.get('component', 'GenericNode')
				node_icon = metadata.get('icon', 'fa-cube')
				node_color = metadata.get('color', '#6b7280')
				node_description = metadata.get('description', '')
				
				# 检查节点是否已存在
				existing = jingrow.db.exists('Ai Node', {'node_type': node_type})
				
				if existing:
					skipped_existing += 1
					continue
				
				# 创建新节点
				node_doc = jingrow.get_pg({
					'pagetype': 'Ai Node',
					'node_type': node_type,
					'node_label': node_label,
					'node_group': node_group,
					'node_component': node_component,
					'node_icon': node_icon,
					'node_color': node_color,
					'node_description': node_description,
					'node_schema': node_config,
					'status': 'Published'
				})
				node_doc.insert()
				imported += 1
				
			except Exception as e:
				error_msg = f"导入节点 {item} 失败: {str(e)}"
				jingrow.log_error("导入本地节点失败", error_msg)
				errors.append(error_msg)
		
		# 提交事务
		jingrow.db.commit()
		
		return {
			"success": True,
			"matched": matched,
			"imported": imported,
			"skipped_existing": skipped_existing,
			"errors": errors
		}
		
	except Exception as e:
		jingrow.db.rollback()
		jingrow.log_error("导入本地节点失败", str(e))
		return {
			"success": False,
			"error": str(e),
			"matched": 0,
			"imported": 0,
			"skipped_existing": 0
		}


@jingrow.whitelist()
def package_node(node_type: str) -> Dict[str, Any]:
	"""打包节点为zip文件"""
	if not node_type:
		jingrow.throw("参数错误", "node_type不能为空")
	
	try:
		import base64
		from datetime import datetime
		
		base_path = jingrow.get_app_path("jingrow")
		node_dir = os.path.join(base_path, "ai", "nodes", node_type)
		
		if not os.path.exists(node_dir):
			jingrow.throw("节点不存在", f"节点目录不存在: {node_type}")
		
		# 创建临时目录
		temp_dir = tempfile.mkdtemp()
		timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
		temp_package_dir = os.path.join(temp_dir, node_type)
		
		try:
			# 复制节点目录内容（排除不必要的文件）
			os.makedirs(temp_package_dir, exist_ok=True)
			for item in os.listdir(node_dir):
				if item in ['__pycache__', '.git', '.DS_Store', '.pytest_cache']:
					continue
				src = os.path.join(node_dir, item)
				dst = os.path.join(temp_package_dir, item)
				if os.path.isdir(src):
					shutil.copytree(src, dst)
				else:
					shutil.copy2(src, dst)
			
			# 打包为ZIP
			zip_filename = f"{node_type}-{timestamp}"
			zip_path = shutil.make_archive(
				os.path.join(temp_dir, zip_filename),
				'zip',
				root_dir=temp_dir,
				base_dir=node_type
			)
			
			# 读取文件内容
			with open(zip_path, 'rb') as f:
				zip_content = f.read()
			
			# 返回文件内容（base64编码）
			return {
				"success": True,
				"filename": f"{zip_filename}.zip",
				"content": base64.b64encode(zip_content).decode('utf-8')
			}
			
		finally:
			# 清理临时目录
			if os.path.exists(temp_dir):
				shutil.rmtree(temp_dir, ignore_errors=True)
			
	except Exception as e:
		jingrow.log_error(f"打包节点失败: {node_type}", str(e))
		jingrow.throw("打包失败", str(e))


@jingrow.whitelist()
def export_app_metadata(app_name: str) -> Dict[str, Any]:
	"""导出应用扩展包元数据 - 直接返回Package、Module、PageType的JSON数据结构"""
	try:
		# 1. 根据 app_name 查找对应的 Package
		package = None
		packages = jingrow.get_all("Package", filters={"name": app_name})
		
		if packages:
			package = jingrow.get_pg("Package", packages[0].name)
		else:
			# 如果没有找到，尝试使用 app_name 作为 package_name 查找
			packages = jingrow.get_all("Package", filters={"package_name": app_name})
			if packages:
				package = jingrow.get_pg("Package", packages[0].name)
		
		if not package:
			return {
				"success": False,
				"error": f"未找到应用 '{app_name}' 对应的 Package"
			}
		
		# 2. 收集Package元数据
		metadata = {
			"package": package.as_dict(no_nulls=True),
			"package_name": package.package_name,
			"readme": str(package.readme or ""),
			"license": str(package.license or "") if package.license else None,
			"modules": {}
		}
		
		# 3. 收集所有Module及其文档的元数据
		modules = jingrow.get_all("Module Def", filters={"package": package.name})
		
		for m in modules:
			try:
				module = jingrow.get_pg("Module Def", m.name)
				
				if not module or not hasattr(module, 'meta'):
					continue
				
				# 收集该模块下的所有文档
				module_pgs = {}
				for l in module.meta.links:
					if l.link_pagetype == "Module Def":
						continue
					
					try:
						# 获取该类型的所有文档
						pgs = jingrow.get_all(l.link_pagetype, filters={"module": m.name})
						for d in pgs:
							try:
								pg = jingrow.get_pg(l.link_pagetype, d.name)
								# 导出文档数据
								doc_export = pg.as_dict(no_nulls=True)
								
								# 添加到模块文档
								pagetype_name = l.link_pagetype
								if pagetype_name not in module_pgs:
									module_pgs[pagetype_name] = {}
								module_pgs[pagetype_name][pg.name] = doc_export
							except Exception:
								continue
					except Exception:
						continue
				
				metadata["modules"][m.name] = {
					"module_info": m.as_dict(no_nulls=True),
					"pgs": module_pgs
				}
			except Exception:
				continue
		
		# 4. 返回元数据
		result = {
			"success": True,
			"metadata": metadata
		}
		
		return result
		
	except Exception as e:
		import traceback
		jingrow.log_error("Export app metadata error", str(e))
		return {
			"success": False,
			"error": f"导出扩展包元数据失败: {str(e)}"
		}
