"""Write tool for writing file contents."""

from pathlib import Path
from typing import Any, Optional

from oclawma.tools.base import (
    BaseTool,
    ToolParameter,
    ToolResult,
    ToolSchema,
)


class WriteTool(BaseTool):
    """Tool for writing file contents.

    Writes content to files with options for append mode, encoding,
    and automatic directory creation. Includes safety checks for
    path traversal and overwrite protection.
    """

    DEFAULT_MAX_SIZE = 10 * 1024 * 1024  # 10MB max write

    def __init__(
        self,
        max_size: int = DEFAULT_MAX_SIZE,
        allowed_paths: Optional[list] = None,
        create_dirs: bool = True,
    ) -> None:
        """Initialize the write tool.

        Args:
            max_size: Maximum content size in bytes
            allowed_paths: Optional list of allowed path prefixes
            create_dirs: Whether to create parent directories automatically
        """
        self._max_size = max_size
        self._allowed_paths = allowed_paths
        self._create_dirs = create_dirs
        super().__init__()

    def _define_schema(self) -> ToolSchema:
        """Define the write tool schema."""
        return ToolSchema(
            name="write",
            description="Write content to a file. Creates parent directories if needed.",
            parameters=[
                ToolParameter(
                    name="path",
                    description="Path to the file to write (absolute or relative)",
                    type="string",
                    required=True,
                ),
                ToolParameter(
                    name="content",
                    description="Content to write to the file",
                    type="string",
                    required=True,
                ),
                ToolParameter(
                    name="append",
                    description="Append to existing file instead of overwriting (default: false)",
                    type="boolean",
                    required=False,
                    default=False,
                ),
                ToolParameter(
                    name="encoding",
                    description="File encoding (default: utf-8)",
                    type="string",
                    required=False,
                    default="utf-8",
                ),
                ToolParameter(
                    name="create_dirs",
                    description="Create parent directories if they don't exist (default: true)",
                    type="boolean",
                    required=False,
                    default=True,
                ),
            ],
            returns="string",
            examples=[
                'write(path="hello.txt", content="Hello, World!")',
                'write(path="/tmp/data.json", content="{}\n")',
                'write(path="log.txt", content="New entry\n", append=True)',
                'write(path="notes.txt", content="TODO: ...", encoding="utf-8")',
            ],
        )

    async def execute(
        self,
        path: str,
        content: str,
        append: bool = False,
        encoding: str = "utf-8",
        create_dirs: bool = True,
    ) -> ToolResult:
        """Write content to a file.

        Args:
            path: Path to the file
            content: Content to write
            append: Whether to append instead of overwrite
            encoding: File encoding
            create_dirs: Whether to create parent directories

        Returns:
            ToolResult with write status
        """
        # Validate content
        if content is None:
            return ToolResult(
                tool_name=self.name,
                success=False,
                output="",
                error_message="Content cannot be None",
                metadata={"path": path},
            )

        # Check content size
        content_bytes = content.encode(encoding, errors="replace")
        if len(content_bytes) > self._max_size:
            return ToolResult(
                tool_name=self.name,
                success=False,
                output="",
                error_message=(
                    f"Content too large ({len(content_bytes):,} bytes). "
                    f"Maximum size: {self._max_size:,} bytes"
                ),
                metadata={
                    "path": path,
                    "content_size": len(content_bytes),
                    "max_size": self._max_size,
                },
            )

        # Resolve path
        try:
            file_path = self._resolve_path(path)
        except ValueError as e:
            return ToolResult(
                tool_name=self.name,
                success=False,
                output="",
                error_message=str(e),
                metadata={"path": path},
            )

        # Check if it's a directory
        if file_path.exists() and file_path.is_dir():
            return ToolResult(
                tool_name=self.name,
                success=False,
                output="",
                error_message=f"Path is a directory: {path}",
                metadata={"resolved_path": str(file_path)},
            )

        # Check if file exists and we're not appending
        file_existed = file_path.exists()
        overwrite_warning = file_existed and not append

        # Create parent directories if needed
        parent_dir = file_path.parent
        if create_dirs and not parent_dir.exists():
            try:
                parent_dir.mkdir(parents=True, exist_ok=True)
            except OSError as e:
                return ToolResult(
                    tool_name=self.name,
                    success=False,
                    output="",
                    error_message=f"Failed to create directory: {e}",
                    metadata={"path": str(file_path)},
                )

        # Write the file
        try:
            mode = "a" if append else "w"
            with open(file_path, mode, encoding=encoding, newline="") as f:
                f.write(content)

            # Get file stats
            file_size = file_path.stat().st_size

            metadata = {
                "path": str(file_path),
                "bytes_written": len(content_bytes),
                "file_size": file_size,
                "encoding": encoding,
                "append": append,
                "file_existed": file_existed,
            }

            if overwrite_warning:
                metadata["warning"] = "File was overwritten"

            return ToolResult(
                tool_name=self.name,
                success=True,
                output=f"File written successfully: {file_path}",
                metadata=metadata,
            )

        except PermissionError:
            return ToolResult(
                tool_name=self.name,
                success=False,
                output="",
                error_message=f"Permission denied: {path}",
                metadata={"path": str(file_path)},
            )
        except OSError as e:
            return ToolResult(
                tool_name=self.name,
                success=False,
                output="",
                error_message=f"OS error: {e}",
                metadata={"path": str(file_path)},
            )
        except Exception as e:
            return ToolResult(
                tool_name=self.name,
                success=False,
                output="",
                error_message=f"Error writing file: {e}",
                metadata={"path": str(file_path)},
            )

    def _resolve_path(self, path: str) -> Path:
        """Resolve and validate a file path.

        Args:
            path: The path string

        Returns:
            Resolved Path object

        Raises:
            ValueError: If path is invalid or outside allowed paths
        """
        # Expand user home directory
        expanded = Path(path).expanduser()

        # If relative, resolve against current working directory
        if not expanded.is_absolute():
            expanded = expanded.resolve()

        # Security: Check allowed paths if configured
        if self._allowed_paths:
            is_allowed = any(
                expanded.is_relative_to(Path(allowed).expanduser().resolve())
                for allowed in self._allowed_paths
            )
            if not is_allowed:
                raise ValueError(f"Access denied: Path '{path}' is outside allowed directories")

        return expanded

    def validate_params(self, params: dict[str, Any]) -> dict[str, Any]:
        """Validate write parameters.

        Args:
            params: Parameters to validate

        Returns:
            Validated parameters
        """
        validated = super().validate_params(params)

        # Ensure content is a string
        if "content" in validated:
            content = validated["content"]
            if not isinstance(content, str):
                validated["content"] = str(content)

        # Apply instance default for create_dirs if not specified
        if "create_dirs" not in validated:
            validated["create_dirs"] = self._create_dirs

        return validated
