"""File read/write/edit operations with path restrictions."""

from __future__ import annotations

import os
import shutil
from pathlib import Path
from typing import Optional

WRITABLE_PREFIXES = [
    str(Path.home() / ".openclaw"),
    str(Path.home() / ".easyclaw"),
    "/etc/systemd/system/openclaw",
    "/etc/systemd/system/easyclaw",
    "/opt/openclaw",
    "/tmp/easyclaw-",
]

# Files that must NEVER be written directly — use CLI commands instead
PROTECTED_FILENAMES = [
    "openclaw.json",
]

MAX_WRITE_SIZE = 1_000_000  # 1MB
MAX_READ_SIZE = 100_000  # 100K chars


def _is_protected(path: str) -> tuple[bool, str]:
    """Check if a file is protected from direct writes."""
    resolved = Path(path).resolve()
    filename = resolved.name

    if filename in PROTECTED_FILENAMES:
        return True, (
            f"BLOCKED: Cannot write directly to {filename}. "
            f"Direct writes can set invalid values that crash the gateway.\n"
            f"Use the OpenClaw CLI instead:\n"
            f"  openclaw config set <dotpath> <value>   # Set a config value (validates schema)\n"
            f"  openclaw config validate                 # Check config is valid\n"
            f"  openclaw doctor --fix                    # Auto-fix config issues\n"
            f"Example: openclaw config set gateway.bind lan"
        )
    return False, ""


def _is_writable(path: str) -> bool:
    resolved = str(Path(path).resolve())
    return any(resolved.startswith(p) for p in WRITABLE_PREFIXES)


async def read_file(path: str) -> str:
    """Read file contents. No path restrictions for reading."""
    try:
        p = Path(path)
        if not p.exists():
            return f"Error: File not found: {path}"
        if not p.is_file():
            return f"Error: Not a file: {path}"

        content = p.read_text(errors="replace")
        if len(content) > MAX_READ_SIZE:
            content = content[:MAX_READ_SIZE] + f"\n... [truncated, {len(content)} chars total]"
        return content
    except PermissionError:
        return f"Error: Permission denied: {path}"
    except Exception as e:
        return f"Error reading {path}: {e}"


async def write_file(path: str, content: str) -> str:
    """Write content to a file. Creates backup if file exists. Path-restricted."""
    protected, msg = _is_protected(path)
    if protected:
        return msg

    if not _is_writable(path):
        return f"Error: Path not in allowed writable set: {path}"

    if len(content) > MAX_WRITE_SIZE:
        return f"Error: Content too large ({len(content)} bytes, max {MAX_WRITE_SIZE})"

    try:
        p = Path(path)
        p.parent.mkdir(parents=True, exist_ok=True)

        # Backup existing file
        if p.exists():
            bak = p.with_suffix(p.suffix + ".bak")
            shutil.copy2(str(p), str(bak))

        p.write_text(content)
        return f"OK: Wrote {len(content)} bytes to {path}"
    except Exception as e:
        return f"Error writing {path}: {e}"


async def edit_file(path: str, old_text: str, new_text: str) -> str:
    """Find and replace text in a file. Path-restricted."""
    protected, msg = _is_protected(path)
    if protected:
        return msg

    if not _is_writable(path):
        return f"Error: Path not in allowed writable set: {path}"

    try:
        p = Path(path)
        if not p.exists():
            return f"Error: File not found: {path}"

        content = p.read_text(errors="replace")
        if old_text not in content:
            return f"Error: old_text not found in {path}"

        # Backup
        bak = p.with_suffix(p.suffix + ".bak")
        shutil.copy2(str(p), str(bak))

        new_content = content.replace(old_text, new_text, 1)
        p.write_text(new_content)
        return f"OK: Replaced text in {path}"
    except Exception as e:
        return f"Error editing {path}: {e}"


async def list_directory(path: Optional[str] = None) -> str:
    """List directory contents with type indicators."""
    try:
        p = Path(path) if path else Path.home()
        if not p.exists():
            return f"Error: Directory not found: {p}"
        if not p.is_dir():
            return f"Error: Not a directory: {p}"

        entries = []
        for item in sorted(p.iterdir()):
            prefix = "d " if item.is_dir() else "f "
            try:
                size = item.stat().st_size if item.is_file() else 0
                entries.append(f"{prefix}{item.name}  ({size} bytes)" if size else f"{prefix}{item.name}/")
            except OSError:
                entries.append(f"{prefix}{item.name}")

        if not entries:
            return f"(empty directory: {p})"
        return "\n".join(entries)
    except PermissionError:
        return f"Error: Permission denied: {path}"
    except Exception as e:
        return f"Error listing {path}: {e}"
