"""
Jingrow Local 应用安装 API
提供本地应用安装、管理等功能
"""

from fastapi import APIRouter, HTTPException, UploadFile, File, Form, Request
from typing import Dict, Any, List, Optional
import logging
import tempfile
import os
import uuid
import re
import json
import shutil
import subprocess
import unicodedata
import traceback
from pathlib import Path
from datetime import datetime
import requests

from jingrow.utils.app_installer import install_app, get_installed_apps as get_apps, get_app_directories, ensure_package_and_module
from jingrow.utils.jingrow_api import get_jingrow_api_headers, get_logged_user
from jingrow.utils.auth import get_jingrow_cloud_url, get_jingrow_cloud_api_headers, get_jingrow_cloud_api_url, get_jingrow_api_headers
from jingrow.utils.export_app_package import export_app_package_from_local
from jingrow.config import Config
from jingrow.utils.app_manager import update_apps_txt
import jingrow

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

# 常量定义
JINGROW_APP_NAME = 'jingrow'
DEFAULT_VERSION = '1.0.0'
DEFAULT_BRANCH = 'main'
SYSTEM_APP_TYPE = 'system'
INSTALLED_APP_TYPE = 'installed'

# 全局缓存，避免重复检查
_jingrow_registered_cache = None


def _import_app_package_and_pagetypes(app_name: str, install_result: Dict[str, Any]) -> None:
    """直接导入应用的 Package 和 PageTypes 到数据库"""
    try:
        # 从安装结果获取路径
        app_dir = install_result.get('install_result', {}).get('app_dir') or install_result.get('app_dir')
        
        if not app_dir:
            # 计算应用路径
            current = Path(__file__).resolve()
            root = current.parents[4]
            apps_dir = root / "apps"
            app_dir = apps_dir / app_name
        
        # 构建后端代码目录路径（可能是 app_name/app_name 或直接 app_name）
        backend_dir = Path(app_dir) / app_name
        if not backend_dir.exists():
            backend_dir = Path(app_dir)
        
        if not os.path.exists(backend_dir):
            return
        
        try:
            api_url = f"{Config.jingrow_server_url}/api/action/jingrow.ai.utils.jlocal.sync_app_files"
            response = requests.post(
                api_url,
                json={'app_name': app_name, 'app_path': str(backend_dir), 'force': True},
                headers=get_jingrow_api_headers(),
                timeout=60
            )
        except Exception:
            pass
            
    except Exception:
        pass


@router.post("/jingrow/install/upload")
async def install_app_from_upload(
    file: UploadFile = File(...),
    app_name: Optional[str] = Form(None)
):

    try:
        filename_lower = file.filename.lower()
        
        # 支持zip、tar.gz、tgz、gz格式
        if not (filename_lower.endswith('.zip') or 
                filename_lower.endswith('.tar.gz') or 
                filename_lower.endswith('.tgz') or 
                filename_lower.endswith('.gz')):
            raise HTTPException(status_code=400, detail="只支持ZIP、TAR.GZ、TGZ、GZ格式的安装包")
        
        # 获取项目根目录
        current = Path(__file__).resolve()
        root = current.parents[4]  # apps/jingrow/jingrow/api/ -> apps/ -> root
        tmp_dir = root / "tmp"
        tmp_dir.mkdir(parents=True, exist_ok=True)
        
        # 创建唯一的文件名
        temp_filename = f"upload_{uuid.uuid4().hex[:8]}{Path(filename_lower).suffix}"
        temp_file_path = tmp_dir / temp_filename
        
        # 保存上传的文件到项目tmp目录
        content = await file.read()
        with open(temp_file_path, 'wb') as f:
            f.write(content)
        
        # 统一使用 install_app 函数处理所有格式
        try:
            result = install_app(str(temp_file_path), app_name)
            
            if result.get('success'):
                app_name_result = result.get('app_name')
                app_dir = result.get('app_dir')
                
                # 扩展包不添加到 Local Installed Apps（返回时没有 app_dir）
                if not app_dir:
                    return result
                
                # 对齐扫描安装的执行链
                try:
                    # 1. 添加到 Local Installed Apps PageType
                    pagetype_result = jingrow.get_single("Local Installed Apps")
                    if pagetype_result.get('success'):
                        config = pagetype_result.get('config', {})
                        apps_list = config.get('local_installed_apps', [])
                    else:
                        apps_list = []
                    
                    # 检查是否已存在，如果存在则更新，否则添加
                    app_exists = False
                    for app in apps_list:
                        if app.get('app_name', '') == app_name_result:
                            app['app_version'] = '1.0.0'
                            app['git_branch'] = 'main'
                            app_exists = True
                            break
                    
                    if not app_exists:
                        apps_list.append({
                            'app_name': app_name_result,
                            'app_version': '1.0.0',
                            'git_branch': 'main'
                        })
                    
                    # 更新数据库
                    await _update_local_installed_apps(apps_list)

                    # 额外：确保创建 Package 与 Module Def（custom）记录（上传安装）
                    ensure_package_and_module(app_name_result)

                    # 更新 apps.txt，确保路由生效
                    update_apps_txt(app_name_result, add=True)

                    # 2. 调用 sync_app_files 同步文件到数据库
                    if app_dir:
                        current = Path(__file__).resolve()
                        root = current.parents[4]
                        apps_dir = root / "apps"
                        backend_dir = apps_dir / app_name_result / app_name_result
                        if not backend_dir.exists():
                            backend_dir = apps_dir / app_name_result
                        
                        if backend_dir.exists():
                            try:
                                api_url = f"{Config.jingrow_server_url}/api/action/jingrow.ai.utils.jlocal.sync_app_files"
                                requests.post(
                                    api_url,
                                    json={'app_name': app_name_result, 'app_path': str(backend_dir), 'force': True},
                                    headers=get_jingrow_api_headers(),
                                    timeout=60
                                )
                            except Exception:
                                pass
                            
                except Exception:
                    pass
                
                return result
            else:
                raise HTTPException(status_code=400, detail=result.get('error', '安装失败'))
        finally:
            # 清理上传的文件
            try:
                if temp_file_path.exists():
                    os.unlink(temp_file_path)
            except Exception:
                pass
                
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/jingrow/check-app/{app_name}")
async def check_app_exists(app_name: str):
    """检查应用或扩展包是否已安装"""
    try:
        # 1. 检查应用是否已安装
        result = jingrow.get_single("Local Installed Apps")
        
        if result.get('success'):
            config = result.get('config', {})
            local_installed_apps = config.get('local_installed_apps', [])
            
            for app in local_installed_apps:
                if app.get('app_name', '') == app_name:
                    return {'exists': True, 'installed': True, 'type': 'app'}
        
        # 2. 检查扩展包是否已安装（通过查询Package PageType）
        package_data = jingrow.get_list("Package", filters=[["name", "=", app_name]], limit=1)
        
        if package_data:
            # 如果找到了记录
            return {'exists': True, 'installed': False, 'type': 'package'}
        
        return {'exists': False, 'installed': False}
    except Exception as e:
        return {'exists': False, 'installed': False, 'error': str(e)}


@router.get("/jingrow/installed-app-names")
async def get_installed_app_names():
    """获取所有已安装的应用名称列表"""
    try:
        installed = set()
        
        # 从 Local Installed Apps 获取应用列表
        result = jingrow.get_single("Local Installed Apps")
        if result.get('success'):
            config = result.get('config', {})
            local_installed_apps = config.get('local_installed_apps', [])
            for app in local_installed_apps:
                app_name = app.get('app_name', '')
                if app_name:
                    installed.add(app_name.lower())
        
        # 从 Package PageType 获取扩展包列表
        package_data = jingrow.get_list("Package", fields=["name"], limit=1000)
        for pkg in package_data:
            pkg_name = pkg.get('name', '')
            if pkg_name:
                installed.add(pkg_name.lower())
        
        return {'success': True, 'apps': list(installed)}
    except Exception as e:
        return {'success': False, 'error': str(e), 'apps': []}


@router.get("/jingrow/local-apps")
async def get_local_installed_apps(request: Request):
    """扫描本地未安装的App"""
    try:
        current = Path(__file__).resolve()
        root = current.parents[4]
        apps_dir = root / "apps" 
        
        # 获取已安装的App列表 - 通过get_single API获取
        result = jingrow.get_single("Local Installed Apps")
        installed_names = set()
        if result.get('success'):
            config = result.get('config', {})
            local_installed_apps = config.get('local_installed_apps', [])
            installed_names = {app.get('app_name', '') for app in local_installed_apps}
        
        # 系统默认App列表（需要排除）
        system_apps = {
            'jingrow', 'local_app_installer', 'local_jobs', 
            'local_apps', 'local_app', 'local_app_installer'
        }
        
        # 扫描apps目录
        local_apps = []
        if apps_dir.exists():
            for app_dir in apps_dir.iterdir():
                if (app_dir.is_dir() and 
                    app_dir.name not in installed_names and 
                    app_dir.name not in system_apps):
                    
                    # 检查是否有hooks.py文件
                    hooks_file = app_dir / app_dir.name / 'hooks.py'
                    
                    if hooks_file.exists():
                        try:
                            # 读取hooks.py获取App信息
                            with open(hooks_file, 'r', encoding='utf-8') as f:
                                content = f.read()
                                
                            app_info = {
                                'name': app_dir.name,
                                'path': str(app_dir),
                                'type': 'both' if (app_dir / "frontend").exists() else app_dir.name,
                                'title': app_dir.name.replace('_', ' ').title(),
                                'description': '',
                                'publisher': '',
                                'version': '1.0.0'
                            }
                            
                            # 提取App信息
                            import re
                            title_match = re.search(r'app_title\s*=\s*["\']([^"\']+)["\']', content)
                            if title_match:
                                app_info['title'] = title_match.group(1)
                                
                            desc_match = re.search(r'app_description\s*=\s*["\']([^"\']+)["\']', content)
                            if desc_match:
                                app_info['description'] = desc_match.group(1)
                                
                            pub_match = re.search(r'app_publisher\s*=\s*["\']([^"\']+)["\']', content)
                            if pub_match:
                                app_info['publisher'] = pub_match.group(1)
                            
                            local_apps.append(app_info)
                        except Exception as e:
                            # 如果读取失败，仍然添加基本信息
                            local_apps.append({
                                'name': app_dir.name,
                                'path': str(app_dir),
                                'type': app_dir.name,
                                'title': app_dir.name.replace('_', ' ').title(),
                                'description': '',
                                'publisher': '',
                                'version': '1.0.0'
                            })
        
        return {
            'success': True,
            'data': {
                'apps': local_apps,
                'total': len(local_apps)
            }
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/jingrow/install-local/{app_name}")
async def install_local_app(request: Request, app_name: str):
    """安装本地App"""
    try:
        # 获取应用目录路径
        current = Path(__file__).resolve()
        root = current.parents[4]
        apps_dir = root / "apps"
        app_dir = apps_dir / app_name
        backend_dir = app_dir / app_name  # 后端代码目录
        
        if not app_dir.exists():
            raise HTTPException(status_code=404, detail=f"App '{app_name}' not found")
        
        if not backend_dir.exists():
            backend_dir = app_dir
        
        # 检查是否已经安装 - 通过get_single API检查
        result = jingrow.get_single("Local Installed Apps")
        if result.get('success'):
            config = result.get('config', {})
            local_installed_apps = config.get('local_installed_apps', [])
            for app in local_installed_apps:
                if app.get('app_name', '') == app_name:
                    raise HTTPException(status_code=400, detail=f"App '{app_name}' is already installed")
        
        # 将App信息添加到Local Installed Apps PageType
        try:
            # 获取当前应用列表
            result = jingrow.get_single("Local Installed Apps")
            if result.get('success'):
                config = result.get('config', {})
                apps_list = config.get('local_installed_apps', [])
            else:
                apps_list = []
            
            for app in apps_list:
                if app.get('app_name', '') == app_name:
                    break
            else:
                new_app = {
                    'app_name': app_name,
                    'app_version': "1.0.0",
                    'git_branch': "main"
                }
                apps_list.append(new_app)
                
                if await _update_local_installed_apps(apps_list):
                    # 确保创建 Package 与 Module Def（custom）记录（本地扫描安装）
                    ensure_package_and_module(app_name)

                    # 更新 apps.txt，确保路由生效
                    update_apps_txt(app_name, add=True)

                    try:
                        api_url = f"{Config.jingrow_server_url}/api/action/jingrow.ai.utils.jlocal.sync_app_files"
                        requests.post(
                            api_url,
                            json={'app_name': app_name, 'app_path': str(backend_dir), 'force': True},
                            headers=get_jingrow_api_headers(),
                            timeout=60
                        )
                    except Exception:
                        pass
        except Exception:
            pass
        
        return {
            'success': True,
            'message': f"App '{app_name}' installed successfully",
            'app_name': app_name
        }
        
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


async def _update_local_installed_apps(apps_list):
    """更新Local Installed Apps数据库记录"""
    import requests
    from jingrow.utils.auth import get_jingrow_api_headers
    from jingrow.config import Config
    
    api_url = f"{Config.jingrow_server_url}/api/data/Local Installed Apps/Local Installed Apps"
    payload = {"local_installed_apps": apps_list}
    response = requests.put(api_url, json=payload, headers=get_jingrow_api_headers(), timeout=10)
    return response.status_code == 200

async def ensure_jingrow_registered():
    """确保jingrow应用已注册到数据库中（带缓存）"""
    global _jingrow_registered_cache
    
    # 如果已经检查过且成功，直接返回
    if _jingrow_registered_cache is True:
        return
    
    try:
        result = jingrow.get_single("Local Installed Apps")
        if not result.get('success'):
            return
        
        apps = result.get('config', {}).get('local_installed_apps', [])
        
        if not any(app.get('app_name') == JINGROW_APP_NAME for app in apps):
            apps.append({'app_name': JINGROW_APP_NAME, 'app_version': DEFAULT_VERSION, 'git_branch': DEFAULT_BRANCH})
            await _update_local_installed_apps(apps)
            _jingrow_registered_cache = True
        else:
            _jingrow_registered_cache = True
                
    except Exception:
        _jingrow_registered_cache = False


@router.get("/jingrow/installed-apps")
async def get_installed_apps(request: Request):
    """获取已安装的应用列表 - 通过get_single API获取"""
    try:
        # 确保jingrow应用已注册
        await ensure_jingrow_registered()
        
        # 通过get_single API获取Local Installed Apps数据
        result = jingrow.get_single("Local Installed Apps")
        
        if not result.get('success'):
            return {
                'success': True,
                'data': {
                    'apps': [],
                    'total': 0
                }
            }
        
        config = result.get('config', {})
        local_installed_apps = config.get('local_installed_apps', [])
        
        apps = [
            {
                'name': app.get('app_name', ''),
                'version': app.get('app_version', '1.0.0'),
                'git_branch': app.get('git_branch', 'main'),
                'type': SYSTEM_APP_TYPE if app.get('app_name') == JINGROW_APP_NAME else INSTALLED_APP_TYPE,
                'uninstallable': app.get('app_name') != JINGROW_APP_NAME
            }
            for app in local_installed_apps
        ]
        
        return {
            'success': True,
            'data': {
                'apps': apps,
                'total': len(apps)
            }
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/jingrow/install-from-git")
async def install_from_git(repo_url: str = Form(...)):
    """从 git 仓库克隆并安装应用或扩展包"""
    import subprocess
    
    current = Path(__file__).resolve()
    root = current.parents[4]
    tmp_dir = root / "tmp"
    tmp_dir.mkdir(parents=True, exist_ok=True)
    
    # 创建临时目录用于克隆
    clone_dir = tmp_dir / f"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:
            logger.error(f"Git clone failed: {result.stderr}")
            raise HTTPException(status_code=400, detail=f"Git 克隆失败: {result.stderr}")
        
        # 直接使用克隆的目录进行分析和安装
        from jingrow.utils.app_installer import analyze_package, install_files, is_app_installed, install_package
        
        # 分析包结构
        package_info = analyze_package(str(clone_dir))
        if not package_info.get('success'):
            raise HTTPException(status_code=400, detail=package_info.get('error', '无法识别应用'))
        
        has_hooks = package_info['data'].get('has_hooks', False)
        app_name = package_info['data'].get('app_name')
        
        if not app_name:
            raise HTTPException(status_code=400, detail='无法识别应用名称')
        
        # 判断是扩展包还是独立应用
        if not has_hooks:
            # 作为扩展包安装到 jingrow 应用内部
            # 注意：package_info['data']['root_dir'] 已经是 git clone 后进入仓库目录的路径
            # 但我们传给 install_package 的 temp_dir 应该是仓库内容的实际路径
            result = install_package(package_info['data']['root_dir'], package_info['data'])
            shutil.rmtree(clone_dir, ignore_errors=True)
            return result
        else:
            # 独立应用安装
            # 如果应用已安装，先删除旧版本
            if is_app_installed(app_name):
                apps_dir, _ = get_app_directories()
                app_dir = apps_dir / app_name
                if app_dir.exists():
                    shutil.rmtree(app_dir)
            
            # 安装文件（完整复制整个包结构）
            install_result = install_files(str(clone_dir), app_name, package_info['data'])
            if not install_result.get('success'):
                shutil.rmtree(clone_dir, ignore_errors=True)
                raise HTTPException(status_code=400, detail=install_result.get('error'))
            
            # 清理临时文件
            shutil.rmtree(clone_dir, ignore_errors=True)
            
            # 只有独立应用才需要注册到 Local Installed Apps
            app_dir = install_result.get('app_dir')
            
            if app_dir:
                try:
                    pagetype_result = jingrow.get_single("Local Installed Apps")
                    apps_list = pagetype_result.get('config', {}).get('local_installed_apps', []) if pagetype_result.get('success') else []
                    
                    app_exists = False
                    for app in apps_list:
                        if app.get('app_name', '') == app_name:
                            app.update({'app_version': '1.0.0', 'git_repo': repo_url})
                            app_exists = True
                            break
                    
                    if not app_exists:
                        apps_list.append({'app_name': app_name, 'app_version': '1.0.0', 'git_repo': repo_url})
                    
                    await _update_local_installed_apps(apps_list)
                    
                    # 同步文件到数据库
                    _import_app_package_and_pagetypes(app_name, {'install_result': install_result, 'app_dir': install_result.get('app_dir')})

                    # 确保创建 Package 与 Module Def（custom）记录
                    ensure_package_and_module(app_name)

                    # 更新 apps.txt，确保路由生效
                    update_apps_txt(app_name, add=True)
                except Exception as e:
                    logger.error(f"Failed to register app: {e}")
            
            return {
                'success': True,
                'message': f'应用 {app_name} 安装成功',
                'app_name': app_name,
                'package_info': package_info['data'],
                'app_dir': install_result.get('app_dir')
            }
            
    except subprocess.TimeoutExpired:
        if clone_dir.exists():
            shutil.rmtree(clone_dir, ignore_errors=True)
        raise HTTPException(status_code=400, detail="Git 克隆超时")
    except Exception as e:
        if clone_dir.exists():
            shutil.rmtree(clone_dir, ignore_errors=True)
        raise HTTPException(status_code=500, detail=f"安装失败: {str(e)}")


@router.post("/jingrow/install-from-url")
async def install_from_url(url: str = Form(...)):
    """从URL安装应用或扩展包"""
    try:
        # 下载文件
        import tempfile
        import requests
        
        current = Path(__file__).resolve()
        root = current.parents[4]
        tmp_dir = root / "tmp"
        tmp_dir.mkdir(parents=True, exist_ok=True)
        
        # 创建临时文件
        temp_filename = f"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)
        
        # 使用现有的 install_app 函数安装
        try:
            result = install_app(str(temp_file_path), None)
            
            if result.get('success'):
                app_name_result = result.get('app_name')
                app_dir = result.get('app_dir')
                
                # 扩展包不添加到 Local Installed Apps（返回时没有 app_dir）
                if not app_dir:
                    return result
                
                # 对齐扫描安装的执行链
                try:
                    # 1. 添加到 Local Installed Apps PageType
                    pagetype_result = jingrow.get_single("Local Installed Apps")
                    if pagetype_result.get('success'):
                        config = pagetype_result.get('config', {})
                        apps_list = config.get('local_installed_apps', [])
                    else:
                        apps_list = []
                    
                    # 检查是否已存在，如果存在则更新，否则添加
                    app_exists = False
                    for app in apps_list:
                        if app.get('app_name', '') == app_name_result:
                            app['app_version'] = '1.0.0'
                            app['git_branch'] = 'main'
                            app_exists = True
                            break
                    
                    if not app_exists:
                        apps_list.append({
                            'app_name': app_name_result,
                            'app_version': '1.0.0',
                            'git_branch': 'main'
                        })
                    
                    # 更新数据库
                    await _update_local_installed_apps(apps_list)

                    # 额外：确保创建 Package 与 Module Def（custom）记录（URL安装）
                    ensure_package_and_module(app_name_result)

                    # 更新 apps.txt，确保路由生效
                    update_apps_txt(app_name_result, add=True)

                    # 2. 调用 sync_app_files 同步文件到数据库
                    if app_dir:
                        current = Path(__file__).resolve()
                        root = current.parents[4]
                        apps_dir = root / "apps"
                        backend_dir = apps_dir / app_name_result / app_name_result
                        if not backend_dir.exists():
                            backend_dir = apps_dir / app_name_result
                        
                        if backend_dir.exists():
                            try:
                                api_url = f"{Config.jingrow_server_url}/api/action/jingrow.ai.utils.jlocal.sync_app_files"
                                requests.post(
                                    api_url,
                                    json={'app_name': app_name_result, 'app_path': str(backend_dir), 'force': True},
                                    headers=get_jingrow_api_headers(),
                                    timeout=60
                                )
                            except Exception:
                                pass
                            
                except Exception:
                    pass
                
                return result
            else:
                raise HTTPException(status_code=400, detail=result.get('error', '安装失败'))
        finally:
            # 清理下载的文件
            try:
                if temp_file_path.exists():
                    os.unlink(temp_file_path)
            except:
                pass
        
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/jingrow/uninstall-extension/{package_name}")
async def uninstall_extension(request: Request, package_name: str):
    """卸载扩展包 - 先获取模块列表，卸载数据库，再删除本地目录"""
    try:
        # 获取 jingrow 应用目录
        apps_dir, _ = get_app_directories()
        jingrow_backend_dir = apps_dir / "jingrow" / "jingrow"
        
        if not jingrow_backend_dir.exists():
            return {'success': False, 'error': '找不到 jingrow 应用目录'}
        
        # 1. 先从数据库获取模块列表
        modules = []
        try:
            deps_url = f"{Config.jingrow_server_url}/api/action/jingrow.core.pagetype.package.package.get_package_dependencies"
            response = requests.post(
                deps_url,
                json={'package_name': package_name},
                headers=get_jingrow_api_headers(),
                timeout=60
            )
            
            if response.status_code == 200:
                result = response.json()
                if result.get('message', {}).get('status') == 'success':
                    modules = result['message'].get('modules', [])
        except Exception as e:
            pass
        
        # 2. 调用云端API卸载数据库记录
        try:
            uninstall_url = f"{Config.jingrow_server_url}/api/action/jingrow.core.pagetype.package.package.uninstall_package"
            requests.post(
                uninstall_url,
                json={'package_name': package_name},
                headers=get_jingrow_api_headers(),
                timeout=60
            )
        except Exception:
            pass
        
        # 3. 删除本地模块目录
        removed_count = 0
        for module_name in modules:
            module_dir_name = module_name.lower().replace(" ", "_").replace("-", "_")
            module_dir = jingrow_backend_dir / module_dir_name
            
            if module_dir.exists():
                shutil.rmtree(module_dir)
                removed_count += 1
        
        return {
            'success': True,
            'message': f'扩展包卸载成功，删除了 {removed_count} 个本地模块目录',
            'removed_count': removed_count
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/jingrow/uninstall/{app_name}")
async def uninstall_app(request: Request, app_name: str):
    """卸载应用 - 直接删除整个app目录"""
    try:
        if app_name == JINGROW_APP_NAME:
            raise HTTPException(status_code=403, detail=f"系统应用 '{JINGROW_APP_NAME}' 不允许卸载")
        
        apps_dir, _ = get_app_directories()
        app_dir = apps_dir / app_name
        
        # 检查应用是否存在
        if not app_dir.exists():
            raise HTTPException(status_code=404, detail=f"应用 {app_name} 不存在")
        
        try:
            api_url = f"{Config.jingrow_server_url}/api/action/jingrow.core.pagetype.package.package.uninstall_package"
            response = requests.post(
                api_url,
                json={'package_name': app_name},
                headers=get_jingrow_api_headers(),
                timeout=60
            )
        except Exception:
            pass
        
        shutil.rmtree(app_dir)
        await _remove_from_database(app_name)
        
        # 更新 apps.txt，确保路由生效
        update_apps_txt(app_name, add=False)
        
        return {
            'success': True,
            'message': f'应用 {app_name} 卸载成功'
        }
        
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/jingrow/app-info/{app_name}")
async def get_app_info(request: Request, app_name: str):

    try:
        backend_dir, frontend_dir = get_app_directories()
        backend_app_dir = backend_dir / app_name / app_name
        frontend_app_dir = frontend_dir / app_name / "frontend"
        
        app_info = {
            'name': app_name,
            'has_backend': backend_app_dir.exists(),
            'has_frontend': frontend_app_dir.exists(),
            'backend_path': str(backend_app_dir) if backend_app_dir.exists() else None,
            'frontend_path': str(frontend_app_dir) if frontend_app_dir.exists() else None
        }
        
        # 尝试读取应用信息
        if backend_app_dir.exists():
            setup_py = backend_app_dir / 'setup.py'
            if setup_py.exists():
                try:
                    with open(setup_py, 'r', encoding='utf-8') as f:
                        content = f.read()
                        # 简单提取版本信息
                        version_match = re.search(r'version\s*=\s*["\']([^"\']+)["\']', content)
                        if version_match:
                            app_info['version'] = version_match.group(1)
                except:
                    pass
        
        if frontend_app_dir.exists():
            package_json = frontend_app_dir / 'package.json'
            if package_json.exists():
                try:
                    with open(package_json, 'r', encoding='utf-8') as f:
                        pkg_data = json.load(f)
                        app_info['version'] = pkg_data.get('version', app_info.get('version'))
                        app_info['description'] = pkg_data.get('description')
                except:
                    pass
        
        return {
            'success': True,
            'data': app_info
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


async def _remove_from_database(app_name: str) -> Dict[str, Any]:
    """从数据库中删除应用记录"""
    try:
        # 使用API方式操作数据库
        # 获取当前数据
        result = jingrow.get_single("Local Installed Apps")
        if not result.get('success'):
            return {'success': True, 'message': '未找到Local Installed Apps记录'}
        
        config = result.get('config', {})
        local_installed_apps = config.get('local_installed_apps', [])
        original_count = len(local_installed_apps)
        local_installed_apps = [app for app in local_installed_apps if app.get('app_name', '') != app_name]
        
        if len(local_installed_apps) < original_count:
            if await _update_local_installed_apps(local_installed_apps):
                return {'success': True, 'message': '数据库记录删除成功'}
            else:
                return {'success': False, 'error': '更新数据库失败'}
        else:
            return {'success': True, 'message': '未找到要删除的应用记录'}
            
    except Exception as e:
        return {'success': False, 'error': str(e)}


@router.get("/jingrow/app-marketplace")
async def get_app_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_app_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()
            apps = data.get('message', [])
            
            # 返回分页格式的数据
            return {
                "items": apps,
                "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/app-marketplace/{app_name}")
async def get_app_detail(app_name: str):
    """获取应用详情"""
    try:
        url = f"{get_jingrow_cloud_url()}/api/action/jcloud.api.jlocal.get_local_app"
        params = {"name": app_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/my-published-apps")
async def get_my_published_apps(
    request: Request,
    search: Optional[str] = None,
    page: int = 1,
    page_size: int = 20,
    sort_by: Optional[str] = None
):
    """获取当前用户已发布的应用列表，支持搜索、分页和排序"""
    session_cookie = request.cookies.get('sid')
    if not session_cookie:
        raise HTTPException(status_code=401, detail="未提供认证信息")
    
    url = f"{get_jingrow_cloud_url()}/api/action/jcloud.api.jlocal.get_my_local_app_list"
    
    # 构建参数
    params = {
        'order_by': sort_by or "app_name asc",
        'limit_start': (page - 1) * page_size,
        'limit_page_length': page_size
    }
    
    if search:
        params['filters'] = json.dumps({"title": ["like", f"%{search}%"]}, ensure_ascii=False)
    
    # 获取总数
    total_params = params.copy()
    total_params['limit_start'] = 0
    total_params['limit_page_length'] = 0
    
    headers = get_jingrow_cloud_api_headers()
    headers['Cookie'] = f'sid={session_cookie}'
    
    total_response = requests.get(url, params=total_params, headers=headers, timeout=20)
    total_response.raise_for_status()
    total_count = len(total_response.json().get('message', []))
    
    # 获取分页数据
    response = requests.get(url, params=params, headers=headers, timeout=20)
    response.raise_for_status()
    apps = response.json().get('message', [])
    
    return {
        "items": apps,
        "total": total_count,
        "page": page,
        "page_size": page_size
    }


@router.post("/jingrow/delete-published-app")
async def delete_published_app(request: Request, payload: Dict[str, Any]):
    """删除已发布的应用，根据记录的name字段删除"""
    session_cookie = request.cookies.get('sid')
    if not session_cookie:
        raise HTTPException(status_code=401, detail="未提供认证信息")
    
    # 使用记录的name字段，不是app_name字段
    record_name = payload.get('name')
    if not record_name:
        raise HTTPException(status_code=400, detail="记录名称不能为空")
    
    url = f"{get_jingrow_cloud_url()}/api/action/jcloud.api.jlocal.delete_local_app"
    
    headers = get_jingrow_cloud_api_headers()
    headers['Cookie'] = f'sid={session_cookie}'
    
    # 传递记录的name字段到云端API
    response = requests.post(url, json={'name': record_name}, headers=headers, timeout=20)
    response.raise_for_status()
    
    data = response.json()
    result = data.get('message', data)
    
    if result.get('success'):
        return {"success": True, "message": result.get('message', '应用删除成功')}
    else:
        raise HTTPException(status_code=400, detail=result.get('message', '删除失败'))


@router.get("/jingrow/my-published-nodes")
async def get_my_published_nodes(
    request: Request,
    search: Optional[str] = None,
    page: int = 1,
    page_size: int = 20,
    sort_by: Optional[str] = None
):
    """获取当前用户已发布的节点列表，支持搜索、分页和排序"""
    session_cookie = request.cookies.get('sid')
    if not session_cookie:
        raise HTTPException(status_code=401, detail="未提供认证信息")
    
    url = f"{get_jingrow_cloud_url()}/api/action/jcloud.api.jlocal.get_my_local_node_list"
    
    # 构建参数
    params = {
        'order_by': sort_by or "name asc",
        'limit_start': (page - 1) * page_size,
        'limit_page_length': page_size
    }
    
    if search:
        params['filters'] = json.dumps({"title": ["like", f"%{search}%"]}, ensure_ascii=False)
    
    # 获取总数
    total_params = params.copy()
    total_params['limit_start'] = 0
    total_params['limit_page_length'] = 0
    
    headers = get_jingrow_cloud_api_headers()
    headers['Cookie'] = f'sid={session_cookie}'
    
    total_response = requests.get(url, params=total_params, headers=headers, timeout=20)
    total_response.raise_for_status()
    total_count = len(total_response.json().get('message', []))
    
    # 获取分页数据
    response = requests.get(url, params=params, headers=headers, timeout=20)
    response.raise_for_status()
    nodes = response.json().get('message', [])
    
    return {
        "items": nodes,
        "total": total_count,
        "page": page,
        "page_size": page_size
    }


@router.get("/jingrow/my-published-agents")
async def get_my_published_agents(
    request: Request,
    search: Optional[str] = None,
    page: int = 1,
    page_size: int = 20,
    sort_by: Optional[str] = None
):
    """获取当前用户已发布的智能体列表，支持搜索、分页和排序"""
    session_cookie = request.cookies.get('sid')
    if not session_cookie:
        raise HTTPException(status_code=401, detail="未提供认证信息")
    
    url = f"{get_jingrow_cloud_url()}/api/action/jcloud.api.jlocal.get_my_local_agent_list"
    
    # 构建参数
    params = {
        'order_by': sort_by or "name asc",
        'limit_start': (page - 1) * page_size,
        'limit_page_length': page_size
    }
    
    if search:
        params['filters'] = json.dumps({"title": ["like", f"%{search}%"]}, ensure_ascii=False)
    
    # 获取总数
    total_params = params.copy()
    total_params['limit_start'] = 0
    total_params['limit_page_length'] = 0
    
    headers = get_jingrow_cloud_api_headers()
    headers['Cookie'] = f'sid={session_cookie}'
    
    total_response = requests.get(url, params=total_params, headers=headers, timeout=20)
    total_response.raise_for_status()
    total_count = len(total_response.json().get('message', []))
    
    # 获取分页数据
    response = requests.get(url, params=params, headers=headers, timeout=20)
    response.raise_for_status()
    agents = response.json().get('message', [])
    
    return {
        "items": agents,
        "total": total_count,
        "page": page,
        "page_size": page_size
    }


@router.post("/jingrow/delete-published-node")
async def delete_published_node(request: Request, payload: Dict[str, Any]):
    """删除已发布的节点，根据记录的name字段删除"""
    session_cookie = request.cookies.get('sid')
    if not session_cookie:
        raise HTTPException(status_code=401, detail="未提供认证信息")
    
    # 使用记录的name字段
    record_name = payload.get('name')
    if not record_name:
        raise HTTPException(status_code=400, detail="记录名称不能为空")
    
    url = f"{get_jingrow_cloud_url()}/api/action/jcloud.api.jlocal.delete_local_node"
    
    headers = get_jingrow_cloud_api_headers()
    headers['Cookie'] = f'sid={session_cookie}'
    
    # 传递记录的name字段到云端API
    response = requests.post(url, json={'name': record_name}, headers=headers, timeout=20)
    response.raise_for_status()
    
    data = response.json()
    result = data.get('message', data)
    
    if result.get('success'):
        return {"success": True, "message": result.get('message', '节点删除成功')}
    else:
        raise HTTPException(status_code=400, detail=result.get('message', '删除失败'))


@router.post("/jingrow/delete-published-agent")
async def delete_published_agent(request: Request, payload: Dict[str, Any]):
    """删除已发布的智能体，根据记录的name字段删除"""
    session_cookie = request.cookies.get('sid')
    if not session_cookie:
        raise HTTPException(status_code=401, detail="未提供认证信息")
    
    # 使用记录的name字段
    record_name = payload.get('name')
    if not record_name:
        raise HTTPException(status_code=400, detail="记录名称不能为空")
    
    url = f"{get_jingrow_cloud_url()}/api/action/jcloud.api.jlocal.delete_local_agent"
    
    headers = get_jingrow_cloud_api_headers()
    headers['Cookie'] = f'sid={session_cookie}'
    
    # 传递记录的name字段到云端API
    response = requests.post(url, json={'name': record_name}, headers=headers, timeout=20)
    response.raise_for_status()
    
    data = response.json()
    result = data.get('message', data)
    
    if result.get('success'):
        return {"success": True, "message": result.get('message', '智能体删除成功')}
    else:
        raise HTTPException(status_code=400, detail=result.get('message', '删除失败'))


@router.post("/jingrow/upload-image")
async def upload_image(file: UploadFile = File(...)):
    """上传应用图片"""
    try:
        # 验证文件类型
        if not file.content_type or not file.content_type.startswith('image/'):
            raise HTTPException(status_code=400, detail="只支持图片格式")
        
        # 验证文件大小 (5MB限制)
        if file.size and file.size > 5 * 1024 * 1024:
            raise HTTPException(status_code=400, detail="图片大小不能超过5MB")
        
        # 读取文件内容
        file_content = await file.read()
        
        url = f"{get_jingrow_cloud_url()}/api/action/upload_file"
        headers = get_jingrow_cloud_api_headers()
        headers.pop('Content-Type', None)

        files = {'file': (file.filename, file_content, file.content_type)}
        data = {'file_name': file.filename, 'is_private': 0, 'folder': 'Home/Attachments'}
        
        response = requests.post(url, files=files, data=data, headers=headers, timeout=30)
        
        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('file_url'):
            file_url = message.get('file_url')
            # 确保URL格式正确
            if not file_url.startswith('/files/'):
                file_url = f"/files/{file_url.lstrip('/')}"
            
            return {
                "success": True,
                "url": file_url,
                "file_name": message.get('file_name'),
                "message": "图片上传成功"
            }
        
        raise HTTPException(status_code=400, detail="API响应格式错误")
        
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))



@router.post("/jingrow/create-app")
async def create_app(
    title: str = Form(...),
    app_name: str = Form(...),
    subtitle: str = Form(None),
    category: str = Form(None),
    description: str = Form(None),
    repository_url: str = Form(None),
    file_url: str = Form(None),
    app_image: str = Form(None)
):
    try:
        url = f"{get_jingrow_cloud_url()}/api/action/jcloud.api.jlocal.create_local_app"
        
        headers = get_jingrow_cloud_api_headers()
        headers['Content-Type'] = 'application/json'
        
        response = requests.post(url, json={
            "app_data": {
                "app_name": app_name,
                "title": title,
                "subtitle": subtitle,
                "description": description,
                "category": category,
                "repository_url": repository_url,
                "file_url": file_url,
                "app_image": app_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'])
        
        # 成功响应
        app_name = message.get('name', 'unknown') if isinstance(message, dict) else result.get('message', 'unknown')
        return {"success": True, "message": f"应用发布成功，应用名称: {app_name}"}
        
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"发布失败: {str(e)}")


@router.post("/jingrow/export-app-package/{app_name}")
async def export_app_package(app_name: str):
	"""导出应用安装包 - 直接打包本地应用源码"""
	try:
		logger.info(f"开始导出应用安装包: {app_name}")
		
		# 获取应用目录
		current = Path(__file__).resolve()
		root = current.parents[4]
		apps_dir = root / "apps"
		
		# 调用辅助函数
		result = export_app_package_from_local(app_name, apps_dir)
		
		if not result.get("success"):
			raise HTTPException(status_code=400, detail=result.get("error", "导出失败"))
		
		return result
		
	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)}")
