from __future__ import annotations

import difflib
from pathlib import Path

from openclaw_lite.schema import ToolContext, ToolSpec
from openclaw_lite.tools.base import fail, ok
from openclaw_lite.utils import ensure_parent


def _resolve_within_root(root: Path, path_value: str) -> Path:
    path = Path(path_value)
    if not path.is_absolute():
        path = (root / path).resolve()
    else:
        path = path.resolve()
    return path


async def read_file_tool(context: ToolContext, arguments: dict):
    path = _resolve_within_root(context.config.paths.root, arguments["path"])
    if not path.exists():
        return fail(f"file not found: {path}")
    return ok({"path": str(path), "content": path.read_text(encoding="utf-8", errors="replace")})


async def write_file_tool(context: ToolContext, arguments: dict):
    path = _resolve_within_root(context.config.paths.root, arguments["path"])
    content = str(arguments.get("content", ""))
    backup = None
    if path.exists():
        backup = path.with_suffix(path.suffix + ".bak")
        backup.write_text(path.read_text(encoding="utf-8", errors="replace"), encoding="utf-8")
    ensure_parent(path)
    path.write_text(content, encoding="utf-8")
    return ok({"path": str(path), "backup": str(backup) if backup else None, "bytes": len(content.encode("utf-8"))})


async def patch_file_tool(context: ToolContext, arguments: dict):
    path = _resolve_within_root(context.config.paths.root, arguments["path"])
    search = str(arguments.get("search", ""))
    replace = str(arguments.get("replace", ""))
    if not path.exists():
        return fail(f"file not found: {path}")
    original = path.read_text(encoding="utf-8", errors="replace")
    if search not in original:
        return fail("search text not found")
    updated = original.replace(search, replace, 1)
    path.write_text(updated, encoding="utf-8")
    diff = "\n".join(
        difflib.unified_diff(
            original.splitlines(),
            updated.splitlines(),
            fromfile=str(path),
            tofile=str(path),
            lineterm="",
        )
    )
    return ok({"path": str(path), "diff": diff})


async def list_dir_tool(context: ToolContext, arguments: dict):
    path = _resolve_within_root(context.config.paths.root, arguments.get("path", str(context.config.paths.root)))
    if not path.exists():
        return fail(f"path not found: {path}")
    items = []
    for child in sorted(path.iterdir(), key=lambda item: item.name):
        items.append(
            {
                "name": child.name,
                "path": str(child),
                "type": "dir" if child.is_dir() else "file",
            }
        )
    return ok({"path": str(path), "items": items})


def build_tools() -> list[ToolSpec]:
    return [
        ToolSpec(
            name="read_file",
            description="Read a text file from the project or workspace.",
            parameters={
                "type": "object",
                "properties": {"path": {"type": "string"}},
                "required": ["path"],
            },
            handler=read_file_tool,
        ),
        ToolSpec(
            name="write_file",
            description="Write or overwrite a text file. Existing files are backed up to .bak.",
            parameters={
                "type": "object",
                "properties": {
                    "path": {"type": "string"},
                    "content": {"type": "string"},
                },
                "required": ["path", "content"],
            },
            handler=write_file_tool,
            dangerous=True,
        ),
        ToolSpec(
            name="patch_file",
            description="Replace one text fragment with another in a file.",
            parameters={
                "type": "object",
                "properties": {
                    "path": {"type": "string"},
                    "search": {"type": "string"},
                    "replace": {"type": "string"},
                },
                "required": ["path", "search", "replace"],
            },
            handler=patch_file_tool,
            dangerous=True,
        ),
        ToolSpec(
            name="list_dir",
            description="List files and directories.",
            parameters={
                "type": "object",
                "properties": {"path": {"type": "string"}},
            },
            handler=list_dir_tool,
        ),
    ]
