# ClawTex — Governed Agent with Genetic Memory
# Built by DeVere Cooley / AN2B LLC · an2b.com
# BB4C — Breath Before Code

"""
File operations tools — read, write, list, move.

action_types:
  file.read   → ALLOW  (safe)
  file.write  → REVIEW (modifies state)
  file.delete → DENY   (irreversible)
"""

from __future__ import annotations

import logging
import os
import shutil
from pathlib import Path
from typing import Any

from .registry import registry

logger = logging.getLogger(__name__)

# Workspace root (default: cwd)
_WORKSPACE = Path(os.environ.get("CLAWTEX_WORKSPACE", ".")).resolve()


def _safe_path(path: str) -> Path:
    """Resolve a path relative to workspace root. Prevents path traversal."""
    resolved = (_WORKSPACE / path).resolve()
    if not str(resolved).startswith(str(_WORKSPACE)):
        raise PermissionError(f"Path traversal blocked: {path!r}")
    return resolved


# ------------------------------------------------------------------
# file.read
# ------------------------------------------------------------------

@registry.register(
    name="file_read",
    action_type="file.read",
    description="Read the contents of a file in the workspace.",
    parameters={
        "type": "object",
        "properties": {
            "path": {"type": "string", "description": "Relative path to the file."},
            "encoding": {"type": "string", "default": "utf-8"},
        },
        "required": ["path"],
    },
)
async def file_read(path: str, encoding: str = "utf-8", **_: Any) -> dict[str, Any]:
    target = _safe_path(path)
    if not target.exists():
        return {"error": f"File not found: {path}"}
    if not target.is_file():
        return {"error": f"Not a file: {path}"}
    content = target.read_text(encoding=encoding)
    return {"path": str(target.relative_to(_WORKSPACE)), "content": content}


# ------------------------------------------------------------------
# file.write
# ------------------------------------------------------------------

@registry.register(
    name="file_write",
    action_type="file.write",
    description="Write or append content to a file in the workspace.",
    parameters={
        "type": "object",
        "properties": {
            "path": {"type": "string", "description": "Relative path to the file."},
            "content": {"type": "string", "description": "Content to write."},
            "mode": {
                "type": "string",
                "enum": ["overwrite", "append"],
                "default": "overwrite",
            },
        },
        "required": ["path", "content"],
    },
)
async def file_write(
    path: str, content: str, mode: str = "overwrite", **_: Any
) -> dict[str, Any]:
    target = _safe_path(path)
    target.parent.mkdir(parents=True, exist_ok=True)
    write_mode = "a" if mode == "append" else "w"
    target.open(write_mode).write(content)
    return {"path": str(target.relative_to(_WORKSPACE)), "bytes_written": len(content)}


# ------------------------------------------------------------------
# file.list
# ------------------------------------------------------------------

@registry.register(
    name="file_list",
    action_type="file.read",
    description="List files and directories at a given path.",
    parameters={
        "type": "object",
        "properties": {
            "path": {
                "type": "string",
                "description": "Relative directory path (default: workspace root).",
                "default": ".",
            },
        },
    },
)
async def file_list(path: str = ".", **_: Any) -> dict[str, Any]:
    target = _safe_path(path)
    if not target.exists():
        return {"error": f"Directory not found: {path}"}
    if not target.is_dir():
        return {"error": f"Not a directory: {path}"}
    entries = []
    for entry in sorted(target.iterdir()):
        entries.append(
            {
                "name": entry.name,
                "type": "dir" if entry.is_dir() else "file",
                "size": entry.stat().st_size if entry.is_file() else None,
            }
        )
    return {"path": path, "entries": entries}


# ------------------------------------------------------------------
# file.move
# ------------------------------------------------------------------

@registry.register(
    name="file_move",
    action_type="file.write",
    description="Move or rename a file within the workspace.",
    parameters={
        "type": "object",
        "properties": {
            "src": {"type": "string", "description": "Source path (relative)."},
            "dst": {"type": "string", "description": "Destination path (relative)."},
        },
        "required": ["src", "dst"],
    },
)
async def file_move(src: str, dst: str, **_: Any) -> dict[str, Any]:
    src_path = _safe_path(src)
    dst_path = _safe_path(dst)
    if not src_path.exists():
        return {"error": f"Source not found: {src}"}
    dst_path.parent.mkdir(parents=True, exist_ok=True)
    shutil.move(str(src_path), str(dst_path))
    return {"moved": src, "to": dst}
