# -*- coding: utf-8 -*-
"""
文件阅读工具
读取指定文件的内容，支持指定行范围
"""
import os
from .base import Tool, coerce_int
from .file_cache import get_default_cache
from .file_io import EncodingDetector
from ..security.path_security import get_default_service as get_path_security


class FileReadTool(Tool):
    """读取文件内容的工具。"""

    @property
    def name(self) -> str:
        return "file_read"

    @property
    def description(self) -> str:
        return "读取指定文件的内容，支持指定行范围。"

    @property
    def parameters(self) -> dict:
        return {
            "type": "object",
            "properties": {
                "path": {"type": "string", "description": "文件路径"},
                "offset": {"type": "integer", "description": "起始行号，默认1"},
                "limit": {"type": "integer", "description": "最大读取行数"}
            },
            "required": ["path"]
        }

    def execute(self, path: str, offset: int = 1, limit: int = None) -> dict:
        """读取文件内容"""
        # PathSecurityService 5 重校验（Phase 6 Tier 1-5 AP）
        # 读操作：受保护路径需确认，但仍允许（与旧版 find_protected_name 行为不同）
        # 这里采用宽松策略：警告但不阻断读取（除路径穿越/Windows 绕过外）
        sec = get_path_security()
        check = sec.check_read(path)
        if not check.allowed and not check.needs_confirmation:
            # 硬拒绝（路径穿越、Windows 绕过等）
            return {"success": False, "error": check.reason}
        # needs_confirmation 时允许读取（受保护路径读取），但通过 warning 字段提示
        warning = check.reason if check.needs_confirmation else None

        if not os.path.exists(path):
            return {"success": False, "error": f"文件不存在: {path}"}

        # 目录路径给出明确提示（避免 Permission denied 误导用户）
        if os.path.isdir(path):
            hint = "（如需查看目录结构，请用 shell_exec 执行 Get-ChildItem 命令）"
            return {
                "success": False,
                "error": f"路径是目录不是文件: {path} {hint}",
            }

        # 兼容 LLM 将 offset/limit 序列化为字符串的情形
        offset = coerce_int(offset, default=1)
        if limit is not None:
            limit = coerce_int(limit, default=None)

        # 优先查 FileStateCache（Phase 6 AQ）：命中则跳过 IO
        cache = get_default_cache()
        cached_content = cache.get(path)
        if cached_content is not None:
            lines = cached_content.splitlines(keepends=True)
            start = max(0, offset - 1)
            end = start + limit if limit else len(lines)
            content = "".join(lines[start:end])
            return {
                "success": True,
                "path": path,
                "content": content,
                "total_lines": len(lines),
                "read_lines": min(end, len(lines)) - start,
                "encoding": "cached",
                "cached": True,
            }

        try:
            # 检测文件编码（Phase 6 Tier 1-3 AO）
            encoding = EncodingDetector.detect(path)
            if encoding is None:
                return {
                    "success": False,
                    "error": "无法读取二进制文件",
                    "path": path,
                }

            with open(path, "r", encoding=encoding) as f:
                content_full = f.read()

            # 写入缓存（全文，供后续切片复用）
            cache.put(path, content_full)

            lines = content_full.splitlines(keepends=True)
            start = max(0, offset - 1)
            end = start + limit if limit else len(lines)
            content = "".join(lines[start:end])

            return {
                "success": True,
                "path": path,
                "content": content,
                "total_lines": len(lines),
                "read_lines": min(end, len(lines)) - start,
                "encoding": encoding,
            }
        except Exception as e:
            return {"success": False, "error": str(e)}
