#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
脚本文件管理服务
提供脚本文件的CRUD操作和文件系统管理
"""

import os
import zipfile
import json
from datetime import datetime
from pathlib import Path
from typing import Dict, Any
from sqlalchemy import and_, or_, select
from io import BytesIO

from data.session import get_async_session
from data.models.script_project import VWEDScriptProject
from data.models.script_file import VWEDScriptFile
from utils.logger import get_logger
from config.settings import settings

logger = get_logger("services.script_file")


class ScriptFileService:
    """脚本文件管理服务"""
    
    def __init__(self):
        self.base_path = Path(settings.SCRIPT_SAVE_PATH)
        self.base_path.mkdir(parents=True, exist_ok=True)
        
    async def create_project(self, project_name: str, description: str = "", 
                           created_by: str = None) -> Dict[str, Any]:
        """创建新项目"""
        try:
            # 创建项目路径
            project_path = f"projects/{project_name}"
            full_project_path = self.base_path / project_path
            
            # 检查项目是否已存在
            async with get_async_session() as session:
                query = select(VWEDScriptProject).where(
                    VWEDScriptProject.project_name == project_name
                )
                result = await session.execute(query)
                existing_project = result.scalar_one_or_none()
                
                if existing_project:
                    return {"success": False, "error": f"项目 {project_name} 已存在"}
                
                # 创建文件系统目录
                full_project_path.mkdir(parents=True, exist_ok=True)
                # 创建数据库记录
                new_project = VWEDScriptProject(
                    project_name=project_name,
                    project_path=project_path,
                    description=description,
                    created_by=created_by
                )
                session.add(new_project)
                await session.commit()
                await session.refresh(new_project)
                
                logger.info(f"项目创建成功: {project_name}")
                return {
                    "success": True,
                    "project": new_project.to_dict(),
                    "message": f"项目 {project_name} 创建成功"
                }
                
        except Exception as e:
            logger.error(f"创建项目失败: {e}", exc_info=True)
            return {"success": False, "error": f"创建项目失败: {str(e)}"}
    
    async def get_projects(self, status: str = "active") -> Dict[str, Any]:
        """获取项目列表"""
        try:
            async with get_async_session() as session:
                query = select(VWEDScriptProject)
                if status:
                    query = query.where(VWEDScriptProject.status == status)
                
                result = await session.execute(query)
                projects = result.scalars().all()
                
                return {
                    "success": True,
                    "projects": [project.to_dict() for project in projects]
                }
                
        except Exception as e:
            logger.error(f"获取项目列表失败: {e}", exc_info=True)
            return {"success": False, "error": f"获取项目列表失败: {str(e)}"}
    
    async def create_file(self, project_id: int, file_name: str, file_path: str,
                         content: str = "", file_type: str = "python",
                         created_by: str = None) -> Dict[str, Any]:
        """创建脚本文件"""
        try:
            async with get_async_session() as session:
                # 检查项目是否存在
                project = await session.get(VWEDScriptProject, project_id)
                if not project:
                    return {"success": False, "error": f"项目ID {project_id} 不存在"}
                
                # 构建完整文件路径
                full_file_path = self.base_path / project.project_path / file_path
                
                # 检查文件名是否已存在（同一项目下不能有重名文件）
                name_query = select(VWEDScriptFile).where(
                    and_(VWEDScriptFile.project_id == project_id,
                         VWEDScriptFile.file_name == file_name, 
                         VWEDScriptFile.is_deleted==False)
                )
                name_result = await session.execute(name_query)
                existing_names = name_result.scalars().all()
                
                if existing_names:
                    return {"success": False, "error": f"同一项目下已存在名为 '{file_name}' 的文件"}
                
                # 检查文件路径是否已存在
                path_query = select(VWEDScriptFile).where(
                    and_(VWEDScriptFile.project_id == project_id,
                         VWEDScriptFile.file_path == file_path,
                         VWEDScriptFile.is_deleted==False
                         )
                )
                path_result = await session.execute(path_query)
                existing_paths = path_result.scalars().all()
                
                if existing_paths:
                    return {"success": False, "error": f"文件路径 '{file_path}' 已存在"}
                
                # 创建目录
                full_file_path.parent.mkdir(parents=True, exist_ok=True)
                
                # 写入文件内容
                with open(full_file_path, 'w', encoding='utf-8') as f:
                    f.write(content)
                
                # 检测boot函数
                has_boot_function = self._check_boot_function(content)
                
                # 计算文件大小
                file_size = full_file_path.stat().st_size
                
                # 创建数据库记录
                new_file = VWEDScriptFile(
                    project_id=project_id,
                    file_name=file_name,
                    file_path=file_path,
                    file_type=file_type,
                    content=content,
                    size=file_size,
                    has_boot_function=has_boot_function,
                    created_by=created_by
                )
                session.add(new_file)
                await session.commit()
                await session.refresh(new_file)
                
                logger.info(f"文件创建成功: {full_file_path}")
                return {
                    "success": True,
                    "file": new_file.to_dict(),
                    "message": f"文件 {file_name} 创建成功"
                }
                
        except Exception as e:
            logger.error(f"创建文件失败: {e}", exc_info=True)
            return {"success": False, "error": f"创建文件失败: {str(e)}"}
    
    async def update_file_content(self, file_id: int, content: str,
                                updated_by: str = None) -> Dict[str, Any]:
        """更新文件内容"""
        try:
            async with get_async_session() as session:
                script_file = await session.get(VWEDScriptFile, file_id)
                if not script_file or script_file.is_deleted:
                    return {"success": False, "error": f"文件ID {file_id} 不存在或已被删除"}
                
                # 获取项目信息
                project = await session.get(VWEDScriptProject, script_file.project_id)
                
                # 处理文件路径，如果是绝对路径则直接使用，否则相对于基础路径
                # full_file_path = Path(script_file.file_path)
                if Path(script_file.file_path).is_absolute():
                    full_file_path = Path(script_file.file_path)
                else:
                    full_file_path = Path(os.path.join(self.base_path, project.project_path, script_file.file_path))


                # 写入新内容
                with open(full_file_path, 'w', encoding='utf-8') as f:
                    f.write(content)
                
                # 更新数据库记录
                script_file.content = content
                script_file.size = full_file_path.stat().st_size
                script_file.has_boot_function = self._check_boot_function(content)
                script_file.updated_at = datetime.now()
                if updated_by:
                    script_file.updated_by = updated_by
                
                await session.commit()
                
                logger.info(f"文件更新成功: {project.project_name}/{script_file.file_path}")
                return {
                    "success": True,
                    "file": script_file.to_dict(),
                    "message": "文件内容更新成功"
                }
                
        except Exception as e:
            logger.error(f"更新文件内容失败: {e}", exc_info=True)
            return {"success": False, "error": f"更新文件内容失败: {str(e)}"}
    
    async def get_file_content(self, file_id: int) -> Dict[str, Any]:
        """获取文件内容"""
        try:
            async with get_async_session() as session:
                script_file = await session.get(VWEDScriptFile, file_id)
                if not script_file or script_file.is_deleted:
                    return {"success": False, "error": f"文件ID {file_id} 不存在或已被删除"}
                
                # # 从数据库读取内容（优先）
                # if script_file.content:
                #     return {
                #         "success": True,
                #         "file": script_file.to_dict(),
                #         "content": script_file.content
                #     }

                # 获取项目信息
                project = await session.get(VWEDScriptProject, script_file.project_id)
                
                # 处理文件路径，如果是绝对路径则直接使用，否则相对于基础路径
                if Path(script_file.file_path).is_absolute():
                    full_file_path = Path(script_file.file_path)
                else:
                    full_file_path = Path(os.path.join(self.base_path, project.project_path, script_file.file_path))
                if not full_file_path.exists():
                    return {"success": False, "error": f"物理文件不存在: {full_file_path}"}
                
                with open(full_file_path, 'r', encoding='utf-8') as f:
                    content = f.read()
                
                return {
                    "success": True,
                    "file": script_file.to_dict(),
                    "content": content
                }
                
        except Exception as e:
            logger.error(f"获取文件内容失败: {e}", exc_info=True)
            return {"success": False, "error": f"获取文件内容失败: {str(e)}"}
    
    async def get_project_files(self, project_id: int, include_content: bool = False) -> Dict[str, Any]:
        """获取项目的所有文件"""
        try:
            async with get_async_session() as session:
                project = await session.get(VWEDScriptProject, project_id)
                if not project:
                    return {"success": False, "error": f"项目ID {project_id} 不存在"}
                
                query = select(VWEDScriptFile).where(
                    and_(
                        VWEDScriptFile.project_id == project_id,
                        VWEDScriptFile.is_deleted == False
                    )
                )
                result = await session.execute(query)
                files = result.scalars().all()
                
                file_list = []
                for file in files:
                    file_dict = file.to_dict()
                    if include_content and file.content:
                        file_dict['content'] = file.content
                    file_list.append(file_dict)
                
                return {
                    "success": True,
                    "project": project.to_dict(),
                    "files": file_list
                }
                
        except Exception as e:
            logger.error(f"获取项目文件列表失败: {e}", exc_info=True)
            return {"success": False, "error": f"获取项目文件列表失败: {str(e)}"}
    
    async def delete_file(self, file_id: int) -> Dict[str, Any]:
        """删除文件"""
        try:
            async with get_async_session() as session:
                script_file = await session.get(VWEDScriptFile, file_id)
                if not script_file or script_file.is_deleted:
                    return {"success": False, "error": f"文件ID {file_id} 不存在或已被删除"}
                
                project = await session.get(VWEDScriptProject, script_file.project_id)
                
                # 构建完整文件路径
                if Path(script_file.file_path).is_absolute():
                    full_file_path = Path(script_file.file_path)
                else:
                    full_file_path = Path(os.path.join(self.base_path, project.project_path, script_file.file_path))
                
                # 删除物理文件
                try:
                    if full_file_path.exists():
                        full_file_path.unlink()
                        logger.info(f"物理文件已删除: {full_file_path}")
                except Exception as file_error:
                    logger.warning(f"删除物理文件失败: {file_error}")
                
                # 软删除：标记为已删除
                script_file.is_deleted = True
                script_file.status = 'deleted'
                script_file.updated_at = datetime.now()
                
                await session.commit()
                
                logger.info(f"文件删除成功: {project.project_name}/{script_file.file_path}")
                return {
                    "success": True,
                    "message": f"文件 {script_file.file_name} 删除成功"
                }
                
        except Exception as e:
            logger.error(f"删除文件失败: {e}", exc_info=True)
            return {"success": False, "error": f"删除文件失败: {str(e)}"}
    
    async def search_files(self, keyword: str, project_id: int = None,
                          file_type: str = None, has_boot: bool = None) -> Dict[str, Any]:
        """搜索文件"""
        try:
            async with get_async_session() as session:
                # 基础查询：排除已删除的文件
                query = select(VWEDScriptFile).where(VWEDScriptFile.is_deleted == False)
                
                # 关键字搜索（文件名或内容）
                if keyword:
                    query = query.where(
                        or_(VWEDScriptFile.file_name.contains(keyword),
                            VWEDScriptFile.content.contains(keyword))
                    )
                
                # 项目筛选
                if project_id:
                    query = query.where(VWEDScriptFile.project_id == project_id)
                
                # 文件类型筛选
                if file_type:
                    query = query.where(VWEDScriptFile.file_type == file_type)
                
                # boot函数筛选
                if has_boot is not None:
                    query = query.where(VWEDScriptFile.has_boot_function == has_boot)
                
                result = await session.execute(query)
                files = result.scalars().all()
                
                # 加载项目信息
                file_list = []
                for file in files:
                    project = await session.get(VWEDScriptProject, file.project_id)
                    file_dict = file.to_dict()
                    file_dict['project_name'] = project.project_name
                    file_list.append(file_dict)
                
                return {
                    "success": True,
                    "files": file_list,
                    "count": len(file_list)
                }
                
        except Exception as e:
            logger.error(f"搜索文件失败: {e}", exc_info=True)
            return {"success": False, "error": f"搜索文件失败: {str(e)}"}
    
    def _check_boot_function(self, content: str) -> bool:
        """检查是否包含boot函数"""
        try:
            # 简单检查是否定义了boot函数
            return "def boot(" in content or "def boot():" in content
        except:
            return False
    
    async def validate_script_syntax(self, content: str) -> Dict[str, Any]:
        """验证脚本语法"""
        try:
            compile(content, '<script>', 'exec')
            return {"success": True, "valid": True, "message": "语法检查通过"}
        except SyntaxError as e:
            return {
                "success": True,
                "valid": False,
                "error": f"语法错误: {e.msg}",
                "line": e.lineno,
                "offset": e.offset
            }
        except Exception as e:
            return {"success": False, "error": f"语法检查失败: {str(e)}"}
    
    async def delete_project(self, project_id: int) -> Dict[str, Any]:
        """删除项目（逻辑删除）"""
        try:
            async with get_async_session() as session:
                project = await session.get(VWEDScriptProject, project_id)
                if not project or project.status == 'deleted':
                    return {"success": False, "error": f"项目ID {project_id} 不存在或已被删除"}
                
                # 逻辑删除：标记为已删除
                project.status = 'deleted'
                project.updated_at = datetime.now()
                
                # 同时逻辑删除项目下的所有文件
                query = select(VWEDScriptFile).where(
                    and_(
                        VWEDScriptFile.project_id == project_id,
                        VWEDScriptFile.is_deleted == False
                    )
                )
                result = await session.execute(query)
                files = result.scalars().all()
                
                for file in files:
                    file.is_deleted = True
                    file.status = 'deleted'
                    file.updated_at = datetime.now()
                
                await session.commit()
                
                logger.info(f"项目删除成功: {project.project_name}")
                return {
                    "success": True,
                    "message": f"项目 {project.project_name} 删除成功"
                }
                
        except Exception as e:
            logger.error(f"删除项目失败: {e}", exc_info=True)
            return {"success": False, "error": f"删除项目失败: {str(e)}"}
    
    async def export_file(self, file_id: int) -> Dict[str, Any]:
        """导出单个脚本文件"""
        try:
            async with get_async_session() as session:
                script_file = await session.get(VWEDScriptFile, file_id)
                if not script_file or script_file.is_deleted:
                    return {"success": False, "error": f"文件ID {file_id} 不存在或已被删除"}
                
                # 获取项目信息
                project = await session.get(VWEDScriptProject, script_file.project_id)
                
                # 获取文件内容
                if script_file.content:
                    content = script_file.content
                else:
                    # 从文件系统读取
                    if Path(script_file.file_path).is_absolute():
                        full_file_path = Path(script_file.file_path)
                    else:
                        full_file_path = Path(os.path.join(self.base_path, project.project_path, script_file.file_path))
                    
                    if full_file_path.exists():
                        with open(full_file_path, 'r', encoding='utf-8') as f:
                            content = f.read()
                    else:
                        return {"success": False, "error": f"物理文件不存在: {full_file_path}"}
                
                logger.info(f"文件导出成功: {script_file.file_name}")
                return {
                    "success": True,
                    "file_name": script_file.file_name,
                    "content": content,
                    "file_info": {
                        "id": script_file.id,
                        "file_name": script_file.file_name,
                        "file_path": script_file.file_path,
                        "file_type": script_file.file_type,
                        "project_name": project.project_name,
                        "size": len(content.encode('utf-8')),
                        "created_at": script_file.created_at.isoformat() if script_file.created_at else None,
                        "updated_at": script_file.updated_at.isoformat() if script_file.updated_at else None
                    },
                    "message": f"文件 {script_file.file_name} 导出成功"
                }
                
        except Exception as e:
            logger.error(f"导出文件失败: {e}", exc_info=True)
            return {"success": False, "error": f"导出文件失败: {str(e)}"}


# 全局文件服务实例
_file_service = ScriptFileService()


def get_file_service() -> ScriptFileService:
    """获取文件服务实例"""
    return _file_service