"""
Jingrow Local 应用安装器
"""

import os
import json
import shutil
import tempfile
import zipfile
from pathlib import Path
from typing import Dict, List, Any
from datetime import datetime
from functools import wraps
import logging

from jingrow.config import Config
from jingrow.utils.jingrow_api import log_info, log_error

logger = logging.getLogger(__name__)


def handle_errors(func):
    """统一错误处理装饰器"""
    @wraps(func)
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            log_error(f"{func.__name__} 失败: {str(e)}")
            return {'success': False, 'error': str(e)}
    return wrapper


def get_app_directories():
    """获取应用目录路径"""
    project_root = Path(__file__).resolve().parents[4]
    apps_dir = project_root / "apps"
    
    apps_dir.mkdir(parents=True, exist_ok=True)
    
    return apps_dir, apps_dir


@handle_errors
def extract_package(zip_path: str) -> Dict[str, Any]:
    """解压安装包"""
    temp_dir = tempfile.mkdtemp(prefix="jingrow_app_install_")
    
    with zipfile.ZipFile(zip_path, 'r') as zip_ref:
        zip_ref.extractall(temp_dir)
    
    return {'success': True, 'temp_dir': temp_dir}


@handle_errors
def analyze_package(temp_dir: str) -> Dict[str, Any]:
    """分析安装包结构"""
    # 查找根目录
    root_items = os.listdir(temp_dir)
    root_dir = os.path.join(temp_dir, root_items[0]) if len(root_items) == 1 and os.path.isdir(os.path.join(temp_dir, root_items[0])) else temp_dir
    
    package_info = {
        'app_name': os.path.basename(root_dir),
        'version': '1.0.0',
        'description': '',
        'has_backend': False,
        'has_frontend': False,
        'backend_files': [],
        'frontend_files': []
    }
    
    # 检查配置文件
    if os.path.exists(os.path.join(root_dir, 'setup.py')):
        package_info['has_backend'] = True
    
    if os.path.exists(os.path.join(root_dir, 'package.json')):
        package_info['has_frontend'] = True
        try:
            with open(os.path.join(root_dir, 'package.json'), 'r') as f:
                pkg_data = json.load(f)
                package_info.update({
                    'app_name': pkg_data.get('name', package_info['app_name']),
                    'version': pkg_data.get('version', package_info['version']),
                    'description': pkg_data.get('description', package_info['description'])
                })
        except:
            pass
    
    # 扫描文件
    for root, dirs, files in os.walk(root_dir):
        for file in files:
            rel_path = os.path.relpath(os.path.join(root, file), root_dir)
            if file.endswith(('.py', '.txt', '.cfg')):
                package_info['backend_files'].append(rel_path)
                package_info['has_backend'] = True
            elif file.endswith(('.vue', '.ts', '.js', '.css')):
                package_info['frontend_files'].append(rel_path)
                package_info['has_frontend'] = True
    
    return {'success': True, 'data': package_info}


@handle_errors
def install_files(temp_dir: str, app_name: str, package_info: Dict[str, Any], is_backend: bool) -> Dict[str, Any]:
    """安装文件到指定目录"""
    apps_dir, _ = get_app_directories()
    app_dir = apps_dir / app_name
    
    # 创建应用目录结构
    if app_dir.exists():
        shutil.rmtree(app_dir)
    app_dir.mkdir(parents=True, exist_ok=True)
    
    # 创建app_name和frontend子目录
    backend_dir = app_dir / app_name
    frontend_dir = app_dir / "frontend"
    backend_dir.mkdir(parents=True, exist_ok=True)
    frontend_dir.mkdir(parents=True, exist_ok=True)
    
    copied_files = []
    
    # 复制后端文件
    if package_info.get('has_backend', False):
        for file_path in package_info.get('backend_files', []):
            src_path = os.path.join(temp_dir, file_path)
            dst_path = backend_dir / file_path
            
            dst_path.parent.mkdir(parents=True, exist_ok=True)
            shutil.copy2(src_path, dst_path)
            copied_files.append(str(dst_path))
    
    # 复制前端文件
    if package_info.get('has_frontend', False):
        for file_path in package_info.get('frontend_files', []):
            src_path = os.path.join(temp_dir, file_path)
            dst_path = frontend_dir / file_path
            
            dst_path.parent.mkdir(parents=True, exist_ok=True)
            shutil.copy2(src_path, dst_path)
            copied_files.append(str(dst_path))
    
    return {
        'success': True,
        'app_dir': str(app_dir),
        'copied_files': copied_files
    }


@handle_errors
def record_installation(app_name: str, package_info: Dict[str, Any], upload_path: str) -> Dict[str, Any]:
    """记录安装信息到数据库"""
    api_url = f"{Config.jingrow_server_url}/api/action/jingrow.core.pagetype.local_installed_apps.local_installed_apps.insert"
    
    from jingrow.utils.auth import get_jingrow_api_headers
    headers = get_jingrow_api_headers()
    
    import requests
    payload = {
        'app_name': app_name,
        'version': package_info.get('version', '1.0.0'),
        'description': package_info.get('description', ''),
        'user_site': Config.jingrow_site or 'local',
        'installation_date': datetime.now().isoformat(),
        'upload_path': upload_path,
        'has_backend': package_info.get('has_backend', False),
        'has_frontend': package_info.get('has_frontend', False)
    }
    
    response = requests.post(api_url, json=payload, headers=headers, timeout=30)
    
    if response.status_code == 200:
        return {'success': True, 'message': '记录成功'}
    else:
        return {'success': False, 'error': f'记录失败: HTTP {response.status_code}'}


def cleanup_temp_dir(temp_dir: str):
    """清理临时目录"""
    try:
        if os.path.exists(temp_dir):
            shutil.rmtree(temp_dir)
    except Exception as e:
        log_error(f"清理临时目录失败: {str(e)}")


def is_app_installed(app_name: str) -> bool:
    """检查应用是否已安装"""
    apps_dir, _ = get_app_directories()
    app_dir = apps_dir / app_name
    return app_dir.exists()


def get_installed_apps() -> List[Dict[str, Any]]:
    """获取已安装的应用列表"""
    apps = []
    apps_dir, _ = get_app_directories()
    
    # 扫描apps目录
    if apps_dir.exists():
        for app_dir in apps_dir.iterdir():
            if app_dir.is_dir():
                app_info = {
                    'name': app_dir.name,
                    'type': 'unknown',
                    'path': str(app_dir),
                    'exists': True
                }
                
                # 检查是否有app_name和frontend
                backend_dir = app_dir / app_dir.name
                frontend_dir = app_dir / "frontend"
                
                if backend_dir.exists() and frontend_dir.exists():
                    app_info['type'] = 'both'
                elif backend_dir.exists():
                    app_info['type'] = app_dir.name
                elif frontend_dir.exists():
                    app_info['type'] = 'frontend'
                
                apps.append(app_info)
    
    return apps


def install_app(uploaded_file_path: str, app_name: str = None) -> Dict[str, Any]:
    """
    安装应用的主函数
    
    Args:
        uploaded_file_path: 上传文件的路径
        app_name: 应用名称（可选）
        
    Returns:
        Dict: 安装结果
    """
    try:
        log_info(f"开始安装应用包: {uploaded_file_path}")
        
        # 验证文件
        if not os.path.exists(uploaded_file_path) or not uploaded_file_path.lower().endswith('.zip'):
            return {'success': False, 'error': '无效的ZIP文件'}
        
        # 解压文件
        extract_result = extract_package(uploaded_file_path)
        if not extract_result.get('success'):
            return extract_result
        
        temp_dir = extract_result['temp_dir']
        
        # 分析包结构
        analyze_result = analyze_package(temp_dir)
        if not analyze_result.get('success'):
            cleanup_temp_dir(temp_dir)
            return analyze_result
        
        package_info = analyze_result['data']
        
        # 确定应用名称
        if not app_name:
            app_name = package_info.get('app_name')
            if not app_name:
                cleanup_temp_dir(temp_dir)
                return {'success': False, 'error': '无法识别应用名称'}
        
        # 检查是否已安装
        if is_app_installed(app_name):
            cleanup_temp_dir(temp_dir)
            return {'success': False, 'error': f'应用 {app_name} 已安装'}
        
        # 安装后端文件
        backend_result = None
        if package_info.get('has_backend'):
            backend_result = install_files(temp_dir, app_name, package_info, is_backend=True)
            if not backend_result.get('success'):
                cleanup_temp_dir(temp_dir)
                return backend_result
        
        # 安装前端文件
        frontend_result = None
        if package_info.get('has_frontend'):
            frontend_result = install_files(temp_dir, app_name, package_info, is_backend=False)
            if not frontend_result.get('success'):
                cleanup_temp_dir(temp_dir)
                return frontend_result
        
        # 记录安装信息
        db_result = record_installation(app_name, package_info, uploaded_file_path)
        
        # 清理临时文件
        cleanup_temp_dir(temp_dir)
        
        log_info(f"应用 {app_name} 安装成功")
        return {
            'success': True,
            'message': f'应用 {app_name} 安装成功',
            'app_name': app_name,
            'package_info': package_info,
            'backend_result': backend_result,
            'frontend_result': frontend_result,
            'db_result': db_result
        }
        
    except Exception as e:
        log_error(f"安装应用失败: {str(e)}")
        return {'success': False, 'error': str(e)}
