# modules/file_manager.py - 文件管理模块（添加行编辑功能）

import os
import shutil
from pathlib import Path
import re
from bisect import bisect_right
from typing import Any, Optional, Dict, List, Set, Tuple, TYPE_CHECKING
from datetime import datetime
try:
    from config import (
        MAX_FILE_SIZE,
        FORBIDDEN_PATHS,
        FORBIDDEN_ROOT_PATHS,
        OUTPUT_FORMATS,
        READ_TOOL_MAX_FILE_SIZE,
        PROJECT_MAX_STORAGE_BYTES,
        TERMINAL_SANDBOX_MODE,
        LINUX_SAFETY,
    )
except ImportError:  # 兼容全局环境中存在同名包的情况
    import sys
    from pathlib import Path
    project_root = Path(__file__).resolve().parents[1]
    if str(project_root) not in sys.path:
        sys.path.insert(0, str(project_root))
    from config import (
        MAX_FILE_SIZE,
        FORBIDDEN_PATHS,
        FORBIDDEN_ROOT_PATHS,
        OUTPUT_FORMATS,
        READ_TOOL_MAX_FILE_SIZE,
        PROJECT_MAX_STORAGE_BYTES,
        TERMINAL_SANDBOX_MODE,
        LINUX_SAFETY,
    )
from modules.container_file_proxy import ContainerFileProxy
from modules.host_sandbox_policy import get_macos_writable_paths, get_macos_readable_paths
from utils.logger import setup_logger

if TYPE_CHECKING:
    from modules.user_container_manager import ContainerHandle

# 临时禁用长度检查
DISABLE_LENGTH_CHECK = True

logger = setup_logger(__name__)

class CrudMixin:
    """FileManager crud mixin 能力 mixin。"""

    def create_file(self, path: str, content: str = "", file_type: str = "txt") -> Dict:
        """创建文件"""
        valid, error, full_path = self._validate_path(path)
        if not valid:
            return {"success": False, "error": error}
        
        # 添加文件扩展名
        if not full_path.suffix:
            full_path = full_path.with_suffix(f".{file_type}")
        ok, msg = self._ensure_host_access(full_path, "write")
        if not ok:
            return {"success": False, "error": msg}
        relative_path = self._relative_path(full_path)
        
        try:
            # 检查是否允许在根目录创建文件
            personalization_config = self._load_personalization_config()
            allow_root_creation = bool(
                personalization_config.get("allow_root_file_creation", False)
            ) if isinstance(personalization_config, dict) else False
            
            if full_path.parent == self.project_path and not allow_root_creation:
                return {
                    "success": False,
                    "error": "禁止在项目根目录直接创建文件，请先创建或选择合适的子目录。",
                    "suggestion": "然后必须**重新**再次创建文件。"
                }
            if self._use_container():
                result = self._container_call("create_file", {
                    "path": relative_path,
                    "content": ""
                })
                if result.get("success"):
                    print(f"{OUTPUT_FORMATS['file']} 创建文件: {relative_path}")
                return result

            # 创建父目录
            full_path.parent.mkdir(parents=True, exist_ok=True)
            
            with open(full_path, 'w', encoding='utf-8') as f:
                f.write("")
            
            print(f"{OUTPUT_FORMATS['file']} 创建文件: {relative_path}")
            
            return {
                "success": True,
                "path": relative_path,
                "size": 0
            }
        except Exception as e:
            return {"success": False, "error": str(e)}

    def delete_file(self, path: str) -> Dict:
        """删除文件"""
        valid, error, full_path = self._validate_path(path)
        if not valid:
            return {"success": False, "error": error}
        
        if not full_path.exists():
            return {"success": False, "error": "文件不存在"}
        
        if not full_path.is_file():
            return {"success": False, "error": "不是文件"}
        ok, msg = self._ensure_host_access(full_path, "write")
        if not ok:
            return {"success": False, "error": msg}
        
        try:
            relative_path = self._relative_path(full_path)
            if self._use_container():
                result = self._container_call("delete_file", {
                    "path": relative_path
                })
                if result.get("success"):
                    print(f"{OUTPUT_FORMATS['file']} 删除文件: {relative_path}")
                return result

            full_path.unlink()
            print(f"{OUTPUT_FORMATS['file']} 删除文件: {relative_path}")
            
            # 删除文件备注（如果存在）
            # 这需要通过context_manager处理，但file_manager没有直接访问权限
            # 所以返回相对路径，让调用者处理备注删除
            
            return {
                "success": True,
                "path": relative_path,
                "action": "deleted"
            }
        except Exception as e:
            return {"success": False, "error": str(e)}

    def rename_file(self, old_path: str, new_path: str) -> Dict:
        """重命名文件"""
        valid_old, error_old, full_old_path = self._validate_path(old_path)
        if not valid_old:
            return {"success": False, "error": error_old}
        
        valid_new, error_new, full_new_path = self._validate_path(new_path)
        if not valid_new:
            return {"success": False, "error": error_new}
        
        if not full_old_path.exists():
            return {"success": False, "error": "原文件不存在"}
        
        if full_new_path.exists():
            return {"success": False, "error": "目标文件已存在"}
        ok_old, msg_old = self._ensure_host_access(full_old_path, "write")
        if not ok_old:
            return {"success": False, "error": msg_old}
        ok_new, msg_new = self._ensure_host_access(full_new_path, "write")
        if not ok_new:
            return {"success": False, "error": msg_new}
        
        try:
            old_relative = self._relative_path(full_old_path)
            new_relative = self._relative_path(full_new_path)
            if self._use_container():
                result = self._container_call("rename_file", {
                    "old_path": old_relative,
                    "new_path": new_relative
                })
                if result.get("success"):
                    print(f"{OUTPUT_FORMATS['file']} 重命名: {old_relative} -> {new_relative}")
                return result

            full_old_path.rename(full_new_path)
            
            print(f"{OUTPUT_FORMATS['file']} 重命名: {old_relative} -> {new_relative}")
            
            return {
                "success": True,
                "old_path": old_relative,
                "new_path": new_relative,
                "action": "renamed"
            }
        except Exception as e:
            return {"success": False, "error": str(e)}

    def create_folder(self, path: str) -> Dict:
        """创建文件夹"""
        valid, error, full_path = self._validate_path(path)
        if not valid:
            return {"success": False, "error": error}
        
        if full_path.exists():
            return {"success": False, "error": "文件夹已存在"}
        ok, msg = self._ensure_host_access(full_path, "write")
        if not ok:
            return {"success": False, "error": msg}
        
        try:
            relative_path = self._relative_path(full_path)
            if self._use_container():
                result = self._container_call("create_folder", {"path": relative_path})
                if result.get("success"):
                    print(f"{OUTPUT_FORMATS['file']} 创建文件夹: {relative_path}")
                return result

            full_path.mkdir(parents=True, exist_ok=True)
            print(f"{OUTPUT_FORMATS['file']} 创建文件夹: {relative_path}")
            
            return {"success": True, "path": relative_path}
        except Exception as e:
            return {"success": False, "error": str(e)}

    def delete_folder(self, path: str) -> Dict:
        """删除文件夹"""
        valid, error, full_path = self._validate_path(path)
        if not valid:
            return {"success": False, "error": error}
        
        if not full_path.exists():
            return {"success": False, "error": "文件夹不存在"}
        
        if not full_path.is_dir():
            return {"success": False, "error": "不是文件夹"}
        
        try:
            relative_path = self._relative_path(full_path)
            if self._use_container():
                result = self._container_call("delete_folder", {"path": relative_path})
                if result.get("success"):
                    print(f"{OUTPUT_FORMATS['file']} 删除文件夹: {relative_path}")
                return result

            shutil.rmtree(full_path)
            print(f"{OUTPUT_FORMATS['file']} 删除文件夹: {relative_path}")
            
            return {"success": True, "path": relative_path}
        except Exception as e:
            return {"success": False, "error": str(e)}

    def write_file(self, path: str, content: str, mode: str = "w") -> Dict:
        """
        写入文件
        
        Args:
            path: 文件路径
            content: 内容
            mode: 写入模式 - "w"(覆盖), "a"(追加)
        """
        valid, error, full_path = self._validate_path(path)
        if not valid:
            return {"success": False, "error": error}
        ok, msg = self._ensure_host_access(full_path, "write")
        if not ok:
            return {"success": False, "error": msg}
        
        # === 新增：内容预处理和验证 ===
        if content:
            # 长度检查
            if not DISABLE_LENGTH_CHECK and len(content) > 9999999999:  # 100KB限制
                return {
                    "success": False,
                    "error": f"内容过长({len(content)}字符)，超过100KB限制",
                    "suggestion": "请分块处理或使用部分修改方式"
                }
            
            # 检查潜在的JSON格式问题
            if content.count('"') % 2 != 0:
                print(f"{OUTPUT_FORMATS['warning']} 检测到奇数个引号，可能存在格式问题")
            
            # 检查大量转义字符
            if content.count('\\') > len(content) / 20:
                print(f"{OUTPUT_FORMATS['warning']} 检测到大量转义字符，建议检查内容格式")
        
        try:
            relative_path = self._relative_path(full_path)
            if PROJECT_MAX_STORAGE_BYTES and self._is_docker_mode():
                current_size = self._get_project_size()
                existing_size = full_path.stat().st_size if full_path.exists() else 0
                if mode == "a":
                    projected_total = current_size + len(content)
                else:
                    projected_total = current_size - existing_size + len(content)
                if projected_total > PROJECT_MAX_STORAGE_BYTES:
                    return {
                        "success": False,
                        "error": "写入失败：超出项目磁盘配额",
                        "limit_bytes": PROJECT_MAX_STORAGE_BYTES,
                        "project_size_bytes": current_size,
                        "attempt_size_bytes": len(content)
                    }
            # 记录写入前的原始内容（用于版本控制和前端 diff 显示）
            original_file: Optional[str] = None
            try:
                if full_path.exists():
                    original_file = full_path.read_text(encoding='utf-8')
            except Exception:
                original_file = None

            if self._use_container():
                result = self._container_call("write_file", {
                    "path": relative_path,
                    "content": content,
                    "mode": mode
                })
                if result.get("success"):
                    action = "覆盖" if mode == "w" else "追加"
                    print(f"{OUTPUT_FORMATS['file']} {action}文件: {relative_path}")
                    result["original_file"] = original_file
                return result

            # 创建父目录
            full_path.parent.mkdir(parents=True, exist_ok=True)
            
            with open(full_path, mode, encoding='utf-8') as f:
                f.write(content)
            
            action = "覆盖" if mode == "w" else "追加"
            print(f"{OUTPUT_FORMATS['file']} {action}文件: {relative_path}")
            
            # 读取写入后的实际内容（追加模式需要从文件读取）
            try:
                new_file = full_path.read_text(encoding='utf-8')
            except Exception:
                new_file = content
            
            return {
                "success": True,
                "path": relative_path,
                "size": len(content),
                "mode": mode,
                "original_file": original_file,
                "new_file": new_file,
            }
        except Exception as e:
            return {"success": False, "error": str(e)}

    def append_file(self, path: str, content: str) -> Dict:
        """追加内容到文件"""
        return self.write_file(path, content, mode="a")

    def clear_file(self, path: str) -> Dict:
        """清空文件内容"""
        return self.write_file(path, "", mode="w")
