# Copyright (c) 2025, JINGROW and contributors
# For license information, please see license.txt

"""
Jingrow Tools API
工具相关的 FastAPI 路由
"""

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

from jingrow.utils.auth import get_jingrow_cloud_url, get_jingrow_cloud_api_headers
from jingrow.utils.path import get_root_path, get_jingrow_root

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


def snake_to_pascal(snake_str: str) -> str:
    """将 snake_case 转换为 PascalCase"""
    components = snake_str.split('_')
    return ''.join(word.capitalize() for word in components)


def generate_route_name(tool_name: str) -> str:
    """基于 tool_name 生成 routeName（PascalCase）"""
    return snake_to_pascal(tool_name)


def generate_route_path(tool_name: str) -> str:
    """基于 tool_name 生成 routePath（约定：tools/{tool_name}）"""
    return f"tools/{tool_name}"


@router.get("/jingrow/tool-marketplace")
async def get_tool_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_tool_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()
            tools = data.get('message', [])
            
            return {
                "items": tools,
                "total": total_count,
                "page": page,
                "page_size": page_size
            }
        else:
            raise HTTPException(status_code=response.status_code, detail="获取工具市场数据失败")
            
    except Exception as e:
        logger.error(f"获取工具市场数据失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=f"获取工具市场数据失败: {str(e)}")


@router.get("/jingrow/tool-marketplace/{name}")
async def get_tool_detail(name: str):
    """获取工具详情"""
    try:
        url = f"{get_jingrow_cloud_url()}/api/action/jcloud.api.jlocal.get_local_tool"
        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:
        logger.error(f"获取工具详情失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=f"获取工具详情失败: {str(e)}")


@router.post("/jingrow/install-tool-from-file")
async def install_tool_from_file(file: UploadFile = File(...), tool_name: str = Form(...)):
    """从上传的文件安装工具（支持ZIP，每个工具包独立）"""
    try:
        # 从数据库获取工具元数据
        tool_data = await get_tool_detail(tool_name)
        if not tool_data:
            raise HTTPException(status_code=404, detail=f"工具 {tool_name} 不存在")
        
        root = get_root_path()
        tmp_dir = root / "tmp"
        tmp_dir.mkdir(parents=True, exist_ok=True)
        
        # 创建临时文件
        temp_filename = f"tool_upload_{uuid.uuid4().hex[:8]}.zip"
        temp_file_path = tmp_dir / temp_filename
        
        # 保存上传的文件
        with open(temp_file_path, 'wb') as f:
            shutil.copyfileobj(file.file, f)
        
        # 安装工具（传递工具元数据）
        result = _install_tool_from_file(str(temp_file_path), tool_data)
        
        # 清理临时文件
        if temp_file_path.exists():
            os.remove(temp_file_path)
        
        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)}")


@router.post("/jingrow/install-tool-from-url")
async def install_tool_from_url(url: str = Form(...), tool_name: str = Form(...)):
    """从URL安装工具"""
    try:
        # 从数据库获取工具元数据
        tool_data = await get_tool_detail(tool_name)
        if not tool_data:
            raise HTTPException(status_code=404, detail=f"工具 {tool_name} 不存在")
        
        root = get_root_path()
        tmp_dir = root / "tmp"
        tmp_dir.mkdir(parents=True, exist_ok=True)
        
        # 创建临时文件
        temp_filename = f"tool_download_{uuid.uuid4().hex[:8]}{Path(url).suffix or '.zip'}"
        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_tool_from_file(str(temp_file_path), tool_data)
        
        # 清理临时文件
        if temp_file_path.exists():
            os.remove(temp_file_path)
        
        return result
        
    except HTTPException:
        raise
    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)}")


def _install_tool_from_file(file_path: str, tool_data: Dict[str, Any]) -> Dict[str, Any]:
    """从文件安装工具（支持ZIP，每个工具包独立）"""
    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:
            # 从数据库获取的元数据中提取 tool_name
            tool_name = tool_data.get('tool_name') or tool_data.get('name')
            if not tool_name:
                return {'success': False, 'error': '工具元数据中缺少 tool_name'}
            
            # 查找解压后的工具目录（zip包结构：tool_name/backend/... 和 tool_name/frontend/...）
            temp_dir_path = Path(temp_dir)
            tool_dir = temp_dir_path / tool_name
            
            # 如果temp_dir下直接有tool_name目录，使用它；否则使用temp_dir本身
            if tool_dir.exists() and tool_dir.is_dir():
                tool_dir_path = tool_dir
            else:
                # 兼容：如果temp_dir下只有一个目录，使用它
                subdirs = [d for d in temp_dir_path.iterdir() if d.is_dir() and not d.name.startswith('.')]
                if len(subdirs) == 1:
                    tool_dir_path = subdirs[0]
                else:
                    tool_dir_path = temp_dir_path
            
            # 安装工具（传递工具元数据）
            result = _install_single_tool_directory(str(tool_dir_path), tool_data)
            
            if result.get('success'):
                return {
                    'success': True,
                    'tool_id': result.get('tool_id'),
                    'tool_name': result.get('tool_name'),
                    'tool_title': result.get('tool_title'),
                    'message': f"工具 {result.get('tool_name')} 安装成功"
                }
            else:
                return result
                
        finally:
            cleanup_temp_dir(temp_dir)
            
    except Exception as e:
        logger.error(f"从文件安装工具失败: {str(e)}")
        return {'success': False, 'error': str(e)}


def _install_single_tool_directory(tool_dir: str, tool_data: Dict[str, Any]) -> Dict[str, Any]:
    """安装单个工具目录（每个工具独立）"""
    try:
        tool_dir_path = Path(tool_dir)
        
        # 从数据库获取的元数据中提取 tool_name
        tool_name = tool_data.get('tool_name') or tool_data.get('name')
        if not tool_name:
            return {'success': False, 'error': '工具元数据中缺少 tool_name'}
        
        # 确定目标目录：apps/jingrow/frontend/src/views/tools/{tool_name}
        jingrow_root = get_jingrow_root()
        frontend_root = jingrow_root.parent / "frontend" / "src"
        tool_frontend_dir = frontend_root / "views" / "tools" / tool_name
        tool_frontend_dir.mkdir(parents=True, exist_ok=True)
        
        # 复制前端组件文件（如果存在）
        # 结构：tool_name/frontend/tool_name/tool_name.vue
        frontend_source = tool_dir_path / "frontend" / tool_name
        if frontend_source.exists() and frontend_source.is_dir():
            # 复制 frontend/tool_name/ 目录下的所有内容到目标目录
            if tool_frontend_dir.exists():
                shutil.rmtree(tool_frontend_dir)
            shutil.copytree(frontend_source, tool_frontend_dir)
            logger.info(f"复制前端组件目录: {frontend_source} -> {tool_frontend_dir}")
        
        # 复制后端文件（如果存在）
        # 结构：tool_name/backend/tool_name/tool_name.py
        backend_source = tool_dir_path / "backend" / tool_name
        if backend_source.exists() and backend_source.is_dir():
            backend_target = jingrow_root / "tools" / tool_name
            # 复制 backend/tool_name/ 目录下的所有内容到目标目录
            if backend_target.exists():
                shutil.rmtree(backend_target)
            shutil.copytree(backend_source, backend_target)
            logger.info(f"复制后端文件目录: {backend_source} -> {backend_target}")
        
        return {
            'success': True,
            'tool_name': tool_name,
            'tool_title': tool_data.get('title', tool_name),
            'message': f"工具 {tool_name} 安装成功"
        }
        
    except Exception as e:
        logger.error(f"安装工具目录失败: {str(e)}")
        return {'success': False, 'error': str(e)}


@router.post("/jingrow/uninstall-tool/{tool_name}")
async def uninstall_tool(tool_name: str):
    """卸载工具 - 删除前后端对应的文件夹"""
    try:
        logger.info(f"开始卸载工具: {tool_name}")
        jingrow_root = get_jingrow_root()
        
        # 前端目录：apps/jingrow/frontend/src/views/tools/{tool_name}
        frontend_root = jingrow_root.parent / "frontend" / "src"
        tool_frontend_dir = frontend_root / "views" / "tools" / tool_name
        
        # 后端目录：apps/jingrow/jingrow/tools/{tool_name}
        tool_backend_dir = jingrow_root / "tools" / tool_name
        
        logger.info(f"前端目录路径: {tool_frontend_dir}")
        logger.info(f"后端目录路径: {tool_backend_dir}")
        logger.info(f"前端目录存在: {tool_frontend_dir.exists()}")
        logger.info(f"后端目录存在: {tool_backend_dir.exists()}")
        
        deleted_paths = []
        
        # 删除前端目录
        if tool_frontend_dir.exists():
            shutil.rmtree(tool_frontend_dir)
            deleted_paths.append(f"前端: {tool_frontend_dir}")
            logger.info(f"删除工具前端目录成功: {tool_frontend_dir}")
        else:
            logger.warning(f"前端目录不存在: {tool_frontend_dir}")
        
        # 删除后端目录
        if tool_backend_dir.exists():
            shutil.rmtree(tool_backend_dir)
            deleted_paths.append(f"后端: {tool_backend_dir}")
            logger.info(f"删除工具后端目录成功: {tool_backend_dir}")
        else:
            logger.warning(f"后端目录不存在: {tool_backend_dir}")
        
        if not deleted_paths:
            logger.warning(f"工具 {tool_name} 的文件目录不存在")
            return {
                'success': False,
                'error': f'工具 {tool_name} 的文件目录不存在',
                'frontend_path': str(tool_frontend_dir),
                'backend_path': str(tool_backend_dir)
            }
        
        logger.info(f"工具 {tool_name} 卸载成功，已删除路径: {deleted_paths}")
        return {
            'success': True,
            'message': f'工具 {tool_name} 卸载成功',
            'deleted_paths': deleted_paths
        }
        
    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/tool/publish")
async def publish_tool_to_marketplace(
    tool_name: str = Form(...),
    title: str = Form(...),
    subtitle: Optional[str] = Form(None),
    description: Optional[str] = Form(None),
    category: Optional[str] = Form(None),
    file_url: Optional[str] = Form(None),
    repository_url: Optional[str] = Form(None),
    tool_image: Optional[str] = Form(None),
    icon: Optional[str] = Form(None),
    color: Optional[str] = Form(None)
):
    """
    发布工具到Jingrow Cloud工具市场
    """
    try:
        # 如果file_url不是绝对地址，拼接完整的服务器地址
        if file_url and not file_url.startswith('http://') and not file_url.startswith('https://'):
            from jingrow.config import Config
            # 确保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_tool"
        
        headers = get_jingrow_cloud_api_headers()
        headers['Content-Type'] = 'application/json'
        
        response = requests.post(url, json={
            "tool_data": {
                "tool_name": tool_name,
                "title": title,
                "subtitle": subtitle or "",
                "description": description or "",
                "category": category or "",
                "file_url": file_url,
                "repository_url": repository_url,
                "tool_image": tool_image,
                "icon": icon,
                "color": color
            }
        }, 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'])
        
        # 成功响应
        tool_name_result = message.get('name', 'unknown') if isinstance(message, dict) else result.get('message', 'unknown')
        return {"success": True, "message": f"工具发布成功，工具名称: {tool_name_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)}")


@router.get("/jingrow/my-published-tools")
async def get_my_published_tools(
    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_tool_list"
    
    # 构建参数
    params = {
        'order_by': sort_by or "tool_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}'
    
    try:
        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()
        tools = response.json().get('message', [])
        
        return {
            "items": tools,
            "total": total_count,
            "page": page,
            "page_size": page_size
        }
    except requests.exceptions.RequestException as e:
        logger.error(f"获取已发布工具列表失败: {str(e)}")
        raise HTTPException(status_code=500, detail=f"获取已发布工具列表失败: {str(e)}")


@router.post("/jingrow/delete-published-tool")
async def delete_published_tool(request: Request, payload: Dict[str, Any]):
    """删除已发布的工具，根据记录的name字段删除"""
    session_cookie = request.cookies.get('sid')
    if not session_cookie:
        raise HTTPException(status_code=401, detail="未提供认证信息")
    
    # 使用记录的name字段，不是tool_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_tool"
    
    headers = get_jingrow_cloud_api_headers()
    headers['Cookie'] = f'sid={session_cookie}'
    
    try:
        # 传递记录的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', '删除失败'))
    except requests.exceptions.RequestException as e:
        logger.error(f"删除已发布工具失败: {str(e)}")
        raise HTTPException(status_code=500, detail=f"删除已发布工具失败: {str(e)}")

