from fastapi import APIRouter, HTTPException, Form, Response
from typing import Dict, Any, List, Optional
from pathlib import Path
import json
import os
import uuid
import shutil
import subprocess
import logging
import traceback
import requests
from jingrow.utils.fs import atomic_write_json
from jingrow.utils.jingrow_api import get_record_id, create_record, update_record, get_record_list
from jingrow.utils.auth import get_jingrow_cloud_url, get_jingrow_cloud_api_headers
from jingrow.config import Config
from jingrow.utils.path import get_root_path, get_jingrow_root
from jingrow.utils.node_dependencies import install_node_dependencies


logger = logging.getLogger(__name__)
router = APIRouter()


@router.post("/jingrow/node/export")
async def export_node_definition(payload: Dict[str, Any]):
	"""
	导出节点定义（metadata + schema）为 JSON 文件
	"""
	try:
		metadata = payload.get("metadata") or {}
		schema = payload.get("schema") or {}
		node_type = metadata.get("type")
		if not node_type:
			raise ValueError("metadata.type is required")

		export_data = {"metadata": metadata, **(schema or {})}

		jingrow_root = get_jingrow_root()
		new_root = jingrow_root / "ai" / "nodes"
		target = new_root / node_type / f"{node_type}.json"
		atomic_write_json(target, export_data)
		return {"success": True, "path": str(target)}
	except Exception as e:
		raise HTTPException(status_code=400, detail=str(e))


@router.post("/jingrow/node/import-local")
async def import_local_node_definitions():
	"""
	扫描本地节点定义目录，按 metadata 去重后导入到 Local Ai Node。
	"""
	try:
		jingrow_root = get_jingrow_root()
		nodes_root = jingrow_root / "ai" / "nodes"
		if not nodes_root.exists():
			return {"success": True, "matched": 0, "imported": 0, "skipped_existing": 0}

		matched: int = 0
		imported: int = 0
		skipped: int = 0
		errors: List[str] = []

		for node_dir in nodes_root.iterdir():
			if not node_dir.is_dir():
				continue
			json_file = node_dir / f"{node_dir.name}.json"
			if not json_file.exists():
				continue
			matched += 1
			try:
				with open(json_file, "r", encoding="utf-8") as f:
					data = json.load(f)
				if not isinstance(data, dict):
					continue
				metadata = data.get("metadata") or {}
				node_type = metadata.get("type")
				if not node_type:
					continue

				# 去重：按 node_type 查询是否已存在
				exists_res = get_record_id(
					pagetype="Local Ai Node",
					field="node_type",
					value=node_type,
				)
				if exists_res.get("success"):
					skipped += 1
					continue

				# 生成 schema（移除 metadata 的剩余部分）
				schema = dict(data)
				schema.pop("metadata", None)

				payload = {
					"node_type": node_type,
					"node_label": metadata.get("label") or node_type,
					"node_icon": metadata.get("icon") or "fa-cube",
					"node_color": metadata.get("color") or "#6b7280",
					"node_group": metadata.get("group") or "",
					"node_component": metadata.get("component_type") or "GenericNode",
					"node_description": metadata.get("description") or "",
					"status": "Published",
					"node_schema": schema,
				}

				res = create_record("Local Ai Node", payload)
				if res.get("success"):
					imported += 1
				else:
					errors.append(f"{node_type}: {res.get('error')}")
			except Exception as e:
				errors.append(f"{json_file.name}: {str(e)}")

		return {
			"success": True,
			"matched": matched,
			"imported": imported,
			"skipped_existing": skipped,
			"errors": errors,
		}
	except Exception as e:
		raise HTTPException(status_code=500, detail=str(e))


@router.get("/jingrow/node/metadata")
async def get_all_node_metadata():
	"""
	获取所有节点的元数据，用于流程编排界面
	"""
	try:
		jingrow_root = get_jingrow_root()
		nodes_root = jingrow_root / "ai" / "nodes"
		
		if not nodes_root.exists():
			return {"success": True, "data": {}}
		
		metadata_map = {}
		
		for node_dir in nodes_root.iterdir():
			if not node_dir.is_dir():
				continue
			json_file = node_dir / f"{node_dir.name}.json"
			if not json_file.exists():
				continue
			
			try:
				with open(json_file, "r", encoding="utf-8") as f:
					data = json.load(f)
				
				metadata = data.get("metadata") or {}
				node_type = metadata.get("type")
				if not node_type:
					continue
				
				metadata_map[node_type] = {
					"type": node_type,
					"label": metadata.get("label") or node_type,
					"icon": metadata.get("icon") or "fa-cube",
					"color": metadata.get("color") or "#6b7280",
					"description": metadata.get("description") or "",
					"group": metadata.get("group") or "其他",
					"component_type": metadata.get("component_type") or "GenericNode"
				}
			except Exception:
				continue
		
		return {"success": True, "data": metadata_map}
		
	except Exception as e:
		raise HTTPException(status_code=500, detail=str(e))


@router.get("/jingrow/node/schema/{node_type}")
async def get_node_schema(node_type: str):
	"""
	获取指定节点类型的Schema配置
	"""
	try:
		jingrow_root = get_jingrow_root()
		nodes_root = jingrow_root / "ai" / "nodes"
		json_file = nodes_root / node_type / f"{node_type}.json"
		
		if not json_file.exists():
			raise HTTPException(status_code=404, detail=f"节点类型 {node_type} 不存在")
		
		with open(json_file, "r", encoding="utf-8") as f:
			data = json.load(f)
		
		schema = dict(data)
		schema.pop("metadata", None)
		
		return {"success": True, "data": schema}
		
	except FileNotFoundError:
		raise HTTPException(status_code=404, detail=f"节点类型 {node_type} 不存在")
	except Exception as e:
		raise HTTPException(status_code=500, detail=str(e))


@router.get("/jingrow/node/schema-fields/{node_type}")
async def get_node_schema_fields(node_type: str):
	"""
	获取指定节点类型的Schema字段列表
	返回格式：[{"name": "field_name", "label": "显示名称", "type": "字段类型", "description": "描述"}]
	"""
	try:
		jingrow_root = get_jingrow_root()
		nodes_root = jingrow_root / "ai" / "nodes"
		json_file = nodes_root / node_type / f"{node_type}.json"
		
		if not json_file.exists():
			return {"success": True, "fields": []}
		
		with open(json_file, "r", encoding="utf-8") as f:
			data = json.load(f)
		
		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:
		logger.error(f"获取节点字段失败: {str(e)}")
		return {"success": True, "fields": []}


# ==================== 节点市场 API ====================

@router.get("/jingrow/node-marketplace")
async def get_node_marketplace(
    search: Optional[str] = None,
    page: int = 1,
    page_size: int = 20,
    sort_by: Optional[str] = None
):
    """获取节点市场数据，支持搜索、分页和排序"""
    try:
        url = f"{get_jingrow_cloud_url()}/api/action/jcloud.api.jlocal.get_local_node_list"
        
        # 构建过滤条件
        filters = {"public": 1}
        if search:
            filters["title"] = ["like", f"%{search}%"]
        
        # 1. 先获取总数（不分页）
        total_params = {
            'filters': json.dumps(filters, ensure_ascii=False),
            'limit_start': 0,
            'limit_page_length': 0  # 不限制数量，获取所有数据来计算总数
        }
        
        headers = get_jingrow_cloud_api_headers()
        total_response = requests.get(url, params=total_params, headers=headers, timeout=20)
        
        total_count = 0
        if total_response.status_code == 200:
            total_data = total_response.json()
            total_count = len(total_data.get('message', []))
        
        # 2. 获取分页数据
        params = {
            'filters': json.dumps(filters, ensure_ascii=False)
        }
        
        # 排序参数
        if sort_by:
            params['order_by'] = sort_by
        
        # 分页参数
        limit_start = (page - 1) * page_size
        params['limit_start'] = limit_start
        params['limit_page_length'] = page_size
        
        response = requests.get(url, params=params, headers=headers, timeout=20)
        
        if response.status_code == 200:
            data = response.json()
            nodes = data.get('message', [])
            
            # 返回分页格式的数据
            return {
                "items": nodes,
                "total": total_count,
                "page": page,
                "page_size": page_size
            }
        else:
            raise HTTPException(status_code=response.status_code, detail="获取节点市场数据失败")
            
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"获取节点市场数据失败: {str(e)}")


@router.get("/jingrow/node-marketplace/{name}")
async def get_node_detail(name: str):
    """获取节点详情"""
    try:
        url = f"{get_jingrow_cloud_url()}/api/action/jcloud.api.jlocal.get_local_node"
        params = {"name": name}
        
        headers = get_jingrow_cloud_api_headers()
        response = requests.get(url, params=params, headers=headers, timeout=20)
        
        if response.status_code == 200:
            data = response.json()
            return data.get('message')
        else:
            raise HTTPException(status_code=404, detail="节点不存在")
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"获取节点详情失败: {str(e)}")


@router.get("/jingrow/check-node/{node_type}")
async def check_node_exists(node_type: str):
    """检查节点是否已安装"""
    try:
        result = get_record_id(
            pagetype="Local Ai Node",
            field="node_type",
            value=node_type,
        )
        
        return {"exists": result.get("success", False)}
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"检查节点失败: {str(e)}")


@router.get("/jingrow/installed-node-types")
async def get_installed_node_types():
	"""
	获取已安装的节点类型列表
	通过扫描节点目录获取，这是最高效的方式（只需要列出目录名，不需要读取文件）
	"""
	try:
		# 确定节点目录路径：apps/jingrow/jingrow/ai/nodes
		jingrow_root = get_jingrow_root()
		nodes_root = jingrow_root / "ai" / "nodes"
		
		node_types = []
		
		# 如果目录不存在，返回空列表
		if not nodes_root.exists():
			return {"success": True, "node_types": []}
		
		# 扫描目录，只获取目录名（最高效的方式）
		for item in nodes_root.iterdir():
			if item.is_dir() and not item.name.startswith('.') and item.name != '__pycache__':
				# 验证是否包含节点定义文件（可选，但更可靠）
				json_file = item / f"{item.name}.json"
				if json_file.exists():
					node_types.append(item.name)
		
		return {"success": True, "node_types": sorted(node_types)}
	except Exception as e:
		logger.error(f"获取已安装节点类型失败: {str(e)}")
		logger.error(f"Traceback: {traceback.format_exc()}")
		return {"success": True, "node_types": []}


@router.post("/jingrow/install-node-from-url")
async def install_node_from_url(url: str = Form(...)):
	"""从URL安装节点"""
	try:
		# 下载文件
		root = get_root_path()
		tmp_dir = root / "tmp"
		tmp_dir.mkdir(parents=True, exist_ok=True)
		
		# 创建临时文件
		temp_filename = f"node_download_{uuid.uuid4().hex[:8]}{Path(url).suffix}"
		temp_file_path = tmp_dir / temp_filename
		
		# 下载文件
		response = requests.get(url, stream=True, timeout=300)
		response.raise_for_status()
		
		with open(temp_file_path, 'wb') as f:
			for chunk in response.iter_content(chunk_size=8192):
				f.write(chunk)
		
		# 安装节点
		result = _install_node_from_file(str(temp_file_path))
		
		# 清理临时文件
		if temp_file_path.exists():
			os.remove(temp_file_path)
		
		return result
		
	except Exception as e:
		logger.error(f"从URL安装节点失败: {str(e)}")
		logger.error(f"Traceback: {traceback.format_exc()}")
		raise HTTPException(status_code=500, detail=f"安装节点失败: {str(e)}")


@router.post("/jingrow/install-node-from-git")
async def install_node_from_git(repo_url: str = Form(...)):
	"""从git仓库克隆并安装节点"""
	try:
		root = get_root_path()
		tmp_dir = root / "tmp"
		tmp_dir.mkdir(parents=True, exist_ok=True)
		
		# 创建临时目录用于克隆
		clone_dir = tmp_dir / f"node_git_clone_{uuid.uuid4().hex[:8]}"
		
		try:
			# 使用 git clone 克隆仓库
			result = subprocess.run(
				['git', 'clone', repo_url, str(clone_dir)],
				capture_output=True,
				text=True,
				timeout=300
			)
			
			if result.returncode != 0:
				raise HTTPException(status_code=400, detail=f"Git 克隆失败: {result.stderr}")
			
			# 查找节点目录（节点包应该包含一个或多个节点目录）
			node_dirs = []
			for item in clone_dir.iterdir():
				if item.is_dir() and not item.name.startswith('.') and item.name != '__pycache__':
					json_file = item / f"{item.name}.json"
					if json_file.exists():
						node_dirs.append(item)
			
			if not node_dirs:
				raise HTTPException(status_code=400, detail="仓库中没有找到节点定义文件")
			
			# 安装所有找到的节点
			installed_nodes = []
			errors = []
			
			for node_dir in node_dirs:
				try:
					result = _install_single_node_directory(str(node_dir))
					if result.get('success'):
						installed_nodes.append(node_dir.name)
					else:
						errors.append(f"{node_dir.name}: {result.get('error')}")
				except Exception as e:
					errors.append(f"{node_dir.name}: {str(e)}")
			
			# 清理临时目录
			shutil.rmtree(clone_dir, ignore_errors=True)
			
			if errors:
				return {
					'success': len(installed_nodes) > 0,
					'installed': installed_nodes,
					'errors': errors,
					'message': f"成功安装 {len(installed_nodes)} 个节点，失败 {len(errors)} 个"
				}
			else:
				return {
					'success': True,
					'installed': installed_nodes,
					'message': f"成功安装 {len(installed_nodes)} 个节点"
				}
				
		finally:
			# 确保清理临时目录
			if clone_dir.exists():
				shutil.rmtree(clone_dir, ignore_errors=True)
				
	except HTTPException:
		raise
	except Exception as e:
		logger.error(f"从Git安装节点失败: {str(e)}")
		logger.error(f"Traceback: {traceback.format_exc()}")
		raise HTTPException(status_code=500, detail=f"安装节点失败: {str(e)}")


def _install_node_from_file(file_path: str) -> Dict[str, Any]:
    """从文件安装节点（支持ZIP和TAR.GZ）"""
    try:
        from jingrow.utils.app_installer import extract_package, cleanup_temp_dir
        
        # 解压文件
        extract_result = extract_package(file_path)
        if not extract_result.get('success'):
            return extract_result
        
        temp_dir = extract_result['temp_dir']
        
        try:
            # 查找节点目录
            node_dirs = []
            for item in Path(temp_dir).iterdir():
                if item.is_dir() and not item.name.startswith('.') and item.name != '__pycache__':
                    json_file = item / f"{item.name}.json"
                    if json_file.exists():
                        node_dirs.append(item)
            
            if not node_dirs:
                return {'success': False, 'error': '压缩包中没有找到节点定义文件'}
            
            # 安装所有找到的节点
            installed_nodes = []
            errors = []
            
            for node_dir in node_dirs:
                try:
                    result = _install_single_node_directory(str(node_dir))
                    if result.get('success'):
                        installed_nodes.append(node_dir.name)
                    else:
                        errors.append(f"{node_dir.name}: {result.get('error')}")
                except Exception as e:
                    errors.append(f"{node_dir.name}: {str(e)}")
            
            if errors:
                return {
                    'success': len(installed_nodes) > 0,
                    'installed': installed_nodes,
                    'errors': errors,
                    'message': f"成功安装 {len(installed_nodes)} 个节点，失败 {len(errors)} 个"
                }
            else:
                return {
                    'success': True,
                    'installed': installed_nodes,
                    'message': f"成功安装 {len(installed_nodes)} 个节点"
                }
                
        finally:
            cleanup_temp_dir(temp_dir)
            
    except Exception as e:
        logger.error(f"从文件安装节点失败: {str(e)}")
        return {'success': False, 'error': str(e)}


def _install_single_node_directory(node_dir: str) -> Dict[str, Any]:
	"""安装单个节点目录到 ai/nodes 并导入数据库"""
	try:
		node_dir_path = Path(node_dir)
		node_name = node_dir_path.name
		
		# 读取节点定义文件
		json_file = node_dir_path / f"{node_name}.json"
		if not json_file.exists():
			return {'success': False, 'error': f'找不到节点定义文件: {json_file.name}'}
		
		with open(json_file, 'r', encoding='utf-8') as f:
			node_data = json.load(f)
		
		if not isinstance(node_data, dict):
			return {'success': False, 'error': '节点定义文件格式错误'}
		
		metadata = node_data.get("metadata") or {}
		node_type = metadata.get("type")
		if not node_type:
			return {'success': False, 'error': '节点定义中缺少 metadata.type'}
		
		# 确定目标目录：apps/jingrow/jingrow/ai/nodes
		jingrow_root = get_jingrow_root()
		nodes_root = jingrow_root / "ai" / "nodes"
		nodes_root.mkdir(parents=True, exist_ok=True)
		
		target_node_dir = nodes_root / node_type
		
		# 如果目标目录已存在，先删除
		if target_node_dir.exists():
			shutil.rmtree(target_node_dir)
		
		# 复制整个节点目录
		shutil.copytree(node_dir_path, target_node_dir)
		
		# 安装节点依赖（如果节点有独立的 pyproject.toml）
		dep_result = install_node_dependencies(node_type)
		if not dep_result.get("success"):
			# 依赖安装失败，记录警告但继续安装节点（可能依赖已存在或稍后安装）
			logger.warning(f"节点 {node_type} 依赖安装失败: {dep_result.get('error', dep_result.get('message'))}")
		
		# 导入到数据库
		# 检查是否已存在
		exists_res = get_record_id(
			pagetype="Local Ai Node",
			field="node_type",
			value=node_type,
		)
		
		# 生成 schema（移除 metadata）
		schema = dict(node_data)
		schema.pop("metadata", None)
		
		payload = {
			"node_type": node_type,
			"node_label": metadata.get("label") or node_type,
			"node_icon": metadata.get("icon") or "fa-cube",
			"node_color": metadata.get("color") or "#6b7280",
			"node_group": metadata.get("group") or "",
			"node_component": metadata.get("component_type") or "GenericNode",
			"node_description": metadata.get("description") or "",
			"status": "Published",
			"node_schema": schema,
		}
		
		if exists_res.get("success"):
			# 更新现有记录，使用 get_record_id 返回的 name（根据 node_type 查找得到的）
			record_name = exists_res.get('name')
			if not record_name:
				return {'success': False, 'error': '获取记录名称失败'}
			res = update_record("Local Ai Node", record_name, payload)
		else:
			# 创建新记录
			res = create_record("Local Ai Node", payload)
		
		if res.get("success"):
			return {'success': True, 'node_type': node_type, 'message': f'节点 {node_type} 安装成功'}
		else:
			return {'success': False, 'error': res.get('error', '导入数据库失败')}
			
	except Exception as e:
		logger.error(f"安装节点目录失败: {str(e)}")
		return {'success': False, 'error': str(e)}


@router.post("/jingrow/node/package/{node_type}")
async def package_node(node_type: str):
	"""
	打包节点文件夹为zip文件，返回文件路径用于后续上传
	"""
	try:
		from datetime import datetime
		
		jingrow_root = get_jingrow_root()
		nodes_root = jingrow_root / "ai" / "nodes"
		node_dir = nodes_root / node_type
		
		if not node_dir.exists():
			raise HTTPException(status_code=404, detail=f"节点目录不存在: {node_type}")
		
		# 创建临时打包目录
		root = get_root_path()
		tmp_dir = root / "tmp"
		tmp_dir.mkdir(parents=True, exist_ok=True)
		
		# 创建临时目录用于打包，文件夹名称保持为节点类型（不加时间戳）
		timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
		temp_package_dir = tmp_dir / node_type
		if temp_package_dir.exists():
			# 如果临时目录已存在，先删除
			shutil.rmtree(temp_package_dir)
		temp_package_dir.mkdir(parents=True, exist_ok=True)
		
		try:
			# 复制节点目录内容（排除不必要的文件）
			for item in node_dir.iterdir():
				if item.name in ['__pycache__', '.git', '.DS_Store', '.pytest_cache']:
					continue
				dst = temp_package_dir / item.name
				if item.is_dir():
					shutil.copytree(item, dst, dirs_exist_ok=True)
				else:
					shutil.copy2(item, dst)
			
			# 打包为 ZIP，zip文件名格式：{node_type}-{timestamp}.zip，但内部文件夹名称保持为节点类型
			zip_filename = f"{node_type}-{timestamp}.zip"
			zip_base_name = tmp_dir / f"{node_type}-{timestamp}"
			# base_dir 使用 node_type，这样 zip 内部文件夹名称就是 node_type
			shutil.make_archive(str(zip_base_name), 'zip', root_dir=str(tmp_dir), base_dir=node_type)
			
			zip_path = tmp_dir / f"{zip_filename}"
			
			if not zip_path.exists():
				raise HTTPException(status_code=500, detail="ZIP文件创建失败")
			
			# 读取文件内容
			with open(zip_path, 'rb') as f:
				zip_content = f.read()
			
			# 清理临时文件
			if zip_path.exists():
				os.remove(zip_path)
			
			# 返回文件内容，前端可以直接使用
			return Response(
				content=zip_content,
				media_type="application/zip",
				headers={
					"Content-Disposition": f"attachment; filename={zip_filename}",
					"Content-Type": "application/zip"
				}
			)
			
		finally:
			# 清理临时目录
			if temp_package_dir.exists():
				shutil.rmtree(temp_package_dir, ignore_errors=True)
				
	except HTTPException:
		raise
	except Exception as e:
		logger.error(f"打包节点失败: {str(e)}")
		logger.error(f"Traceback: {traceback.format_exc()}")
		raise HTTPException(status_code=500, detail=f"打包节点失败: {str(e)}")


@router.post("/jingrow/node/publish")
async def publish_node_to_marketplace(
    node_type: str = Form(...),
    title: str = Form(...),
    subtitle: Optional[str] = Form(None),
    description: Optional[str] = Form(None),
    file_url: str = Form(...),
    repository_url: Optional[str] = Form(None),
    node_image: Optional[str] = Form(None)
):
    """
    发布节点到Jingrow Cloud节点市场
    """
    try:
        # 如果file_url不是绝对地址，拼接完整的服务器地址
        if file_url and not file_url.startswith('http://') and not file_url.startswith('https://'):
            # 确保URL以 / 开头
            if not file_url.startswith('/'):
                file_url = '/' + file_url
            
            # 使用Config中的服务器URL，如果没有则尝试从请求获取
            server_url = Config.jingrow_server_url           
            # 确保服务器URL不以 / 结尾
            server_url = server_url.rstrip('/')
            file_url = f"{server_url}{file_url}"
        
        url = f"{get_jingrow_cloud_url()}/api/action/jcloud.api.jlocal.create_local_node"
        
        headers = get_jingrow_cloud_api_headers()
        headers['Content-Type'] = 'application/json'
        
        response = requests.post(url, json={
            "node_data": {
                "node_type": node_type,
                "title": title,
                "subtitle": subtitle or "",
                "description": description or "",
                "file_url": file_url,
                "repository_url": repository_url,
                "node_image": node_image
            }
        }, headers=headers, timeout=20)
        
        if response.status_code != 200:
            error_detail = response.json().get('detail', f"HTTP {response.status_code}") if response.headers.get('content-type', '').startswith('application/json') else f"HTTP {response.status_code}"
            raise HTTPException(status_code=response.status_code, detail=error_detail)
        
        result = response.json()
        
        # 检查错误
        if isinstance(result, dict) and result.get('error'):
            raise HTTPException(status_code=400, detail=result['error'])
        
        message = result.get('message', {})
        if isinstance(message, dict) and message.get('error'):
            raise HTTPException(status_code=400, detail=message['error'])
        
        # 成功响应
        node_name = message.get('name', 'unknown') if isinstance(message, dict) else result.get('message', 'unknown')
        return {"success": True, "message": f"节点发布成功，节点名称: {node_name}"}
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"发布节点失败: {str(e)}")
        logger.error(f"Traceback: {traceback.format_exc()}")
        raise HTTPException(status_code=500, detail=f"发布节点失败: {str(e)}")


# ==================== 智能体市场 API ====================

@router.get("/jingrow/agent-marketplace")
async def get_agent_marketplace(
    search: Optional[str] = None,
    page: int = 1,
    page_size: int = 20,
    sort_by: Optional[str] = None
):
    """获取智能体市场数据，支持搜索、分页和排序"""
    try:
        url = f"{get_jingrow_cloud_url()}/api/action/jcloud.api.jlocal.get_local_agent_list"
        
        # 构建过滤条件
        filters = {"public": 1}
        if search:
            filters["title"] = ["like", f"%{search}%"]
        
        # 1. 先获取总数（不分页）
        total_params = {
            'filters': json.dumps(filters, ensure_ascii=False),
            'limit_start': 0,
            'limit_page_length': 0
        }
        
        headers = get_jingrow_cloud_api_headers()
        total_response = requests.get(url, params=total_params, headers=headers, timeout=20)
        
        total_count = 0
        if total_response.status_code == 200:
            total_data = total_response.json()
            total_count = len(total_data.get('message', []))
        
        # 2. 获取分页数据
        params = {
            'filters': json.dumps(filters, ensure_ascii=False)
        }
        
        # 排序参数
        if sort_by:
            params['order_by'] = sort_by
        
        # 分页参数
        limit_start = (page - 1) * page_size
        params['limit_start'] = limit_start
        params['limit_page_length'] = page_size
        
        response = requests.get(url, params=params, headers=headers, timeout=20)
        
        if response.status_code == 200:
            data = response.json()
            agents = data.get('message', [])
            
            return {
                "items": agents,
                "total": total_count,
                "page": page,
                "page_size": page_size
            }
        else:
            raise HTTPException(status_code=response.status_code, detail="获取智能体市场数据失败")
            
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"获取智能体市场数据失败: {str(e)}")


@router.get("/jingrow/agent-marketplace/{name}")
async def get_agent_detail(name: str):
    """获取智能体详情"""
    try:
        url = f"{get_jingrow_cloud_url()}/api/action/jcloud.api.jlocal.get_local_agent"
        params = {"name": name}
        
        headers = get_jingrow_cloud_api_headers()
        response = requests.get(url, params=params, headers=headers, timeout=20)
        
        if response.status_code == 200:
            data = response.json()
            return data.get('message')
        else:
            raise HTTPException(status_code=404, detail="智能体不存在")
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"获取智能体详情失败: {str(e)}")


@router.get("/jingrow/check-agent/{agent_name}")
async def check_agent_exists(agent_name: str):
    """检查智能体是否已安装"""
    try:
        exists_res = get_record_id(
            pagetype="Local Ai Agent",
            field="agent_name",
            value=agent_name,
        )
        
        return {"exists": exists_res.get("success", False)}
    except Exception as e:
        logger.error(f"检查智能体是否存在失败: {str(e)}")
        return {"exists": False}


@router.post("/jingrow/install-agent")
async def install_agent(payload: Dict[str, Any]):
    """安装智能体 - 只需要流程数据和智能体名称"""
    try:
        agent_name = payload.get("agent_name")
        agent_flow = payload.get("agent_flow")
        
        if not agent_name:
            raise HTTPException(status_code=400, detail="智能体名称不能为空")
        
        if not agent_flow:
            raise HTTPException(status_code=400, detail="流程数据不能为空")
        
        # 检查是否已存在
        exists_res = get_record_id(
            pagetype="Local Ai Agent",
            field="agent_name",
            value=agent_name,
        )
        
        agent_data = {
            "agent_name": agent_name,
            "agent_flow": json.dumps(agent_flow, ensure_ascii=False) if isinstance(agent_flow, dict) else agent_flow,
            "trigger_mode": "Manual Trigger"
        }
        
        if exists_res.get("success"):
            # 更新现有记录
            record_name = exists_res.get('name')
            if not record_name:
                return {'success': False, 'error': '获取记录名称失败'}
            res = update_record("Local Ai Agent", record_name, agent_data)
        else:
            # 创建新记录
            res = create_record("Local Ai Agent", agent_data)
        
        if res.get("success"):
            return {'success': True, 'agent_name': agent_name, 'message': f'智能体 {agent_name} 安装成功'}
        else:
            return {'success': False, 'error': res.get('error', '导入数据库失败')}
            
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"安装智能体失败: {str(e)}")
        raise HTTPException(status_code=500, detail=f"安装智能体失败: {str(e)}")


@router.get("/jingrow/installed-agent-names")
async def get_installed_agent_names():
    """获取已安装的智能体名称列表"""
    try:
        agents = get_record_list(
            pagetype="Local Ai Agent",
            fields=["agent_name"],
            limit_start=0,
            limit_page_length=1000
        )
        
        agent_names = []
        if agents:
            for agent in agents:
                if agent.get("agent_name"):
                    agent_names.append(agent["agent_name"])
        
        return {"success": True, "agents": agent_names}
    except Exception as e:
        logger.error(f"获取已安装智能体列表失败: {str(e)}")
        return {"success": False, "agents": []}


@router.post("/jingrow/agent/publish")
async def publish_agent_to_marketplace(payload: Dict[str, Any]):
    """
    发布智能体到Jingrow Cloud智能体市场
    使用 create_local_agent API
    """
    try:
        agent_name = payload.get("agent_name")
        agent_flow = payload.get("agent_flow")
        title = payload.get("title")
        description = payload.get("description", "")
        subtitle = payload.get("subtitle")
        
        if not agent_name:
            raise HTTPException(status_code=400, detail="智能体名称不能为空")
        
        if not agent_flow:
            raise HTTPException(status_code=400, detail="流程数据不能为空")
        
        if not title:
            title = agent_name
        
        url = f"{get_jingrow_cloud_url()}/api/action/jcloud.api.jlocal.create_local_agent"
        
        headers = get_jingrow_cloud_api_headers()
        headers['Content-Type'] = 'application/json'
        
        # 准备 agent_data
        # agent_flow 是 JSON 字段：前端发送的可能是对象（dict）或字符串
        # 需要统一转换为字符串格式发送到云端
        agent_flow_str = agent_flow
        if isinstance(agent_flow, dict):
            # 如果是对象，转为 JSON 字符串
            agent_flow_str = json.dumps(agent_flow, ensure_ascii=False)
        elif not isinstance(agent_flow, str):
            # 如果不是字符串也不是对象，尝试转为字符串
            agent_flow_str = json.dumps(agent_flow, ensure_ascii=False)
        
        agent_data = {
            "agent_name": agent_name,
            "title": title,
            "agent_flow": agent_flow_str,
            "description": description,
            "subtitle": subtitle or ""
        }
        
        response = requests.post(url, json={
            "agent_data": agent_data
        }, headers=headers, timeout=20)
        
        if response.status_code != 200:
            error_detail = response.json().get('detail', f"HTTP {response.status_code}") if response.headers.get('content-type', '').startswith('application/json') else f"HTTP {response.status_code}"
            raise HTTPException(status_code=response.status_code, detail=error_detail)
        
        result = response.json()
        
        # 检查错误
        if isinstance(result, dict) and result.get('error'):
            raise HTTPException(status_code=400, detail=result['error'])
        
        message = result.get('message', {})
        if isinstance(message, dict) and message.get('error'):
            raise HTTPException(status_code=400, detail=message['error'])
        
        # 成功响应
        return {"success": True, "message": "智能体发布成功"}
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"发布智能体失败: {str(e)}")
        logger.error(f"Traceback: {traceback.format_exc()}")
        raise HTTPException(status_code=500, detail=f"发布智能体失败: {str(e)}")
