"""
WriteTool - Write file contents to the filesystem

Following OpenCode architecture, provides file writing capabilities with:
- Path handling (absolute and relative)
- Path traversal protection (SEC-003)
- Async file I/O (PERF-009)
- Parent directory creation
- Overwrite handling
- Error handling (permissions, disk space)
- Type-safe implementation
"""

import asyncio
from concurrent.futures import ThreadPoolExecutor
from functools import partial
from pathlib import Path
from typing import Any, Dict, Optional

from groknroll.tools.base_tool import BaseTool
from groknroll.tools.validators import (
    validate_path_within_workspace,
    validate_path_input,
    validate_content_input,
)


# PERF-009: Shared thread pool for async file operations
_file_executor = ThreadPoolExecutor(max_workers=4, thread_name_prefix="file_io")


class WriteResult:
    """
    Result of a write operation

    Attributes:
        success: Whether the write succeeded
        path: Path to the written file
        created: Whether the file was created (True) or overwritten (False)
        bytes_written: Number of bytes written
    """

    def __init__(self, success: bool, path: Path, created: bool, bytes_written: int):
        self.success = success
        self.path = path
        self.created = created
        self.bytes_written = bytes_written

    def to_dict(self) -> Dict[str, Any]:
        """Convert result to dictionary"""
        return {
            "success": self.success,
            "path": str(self.path),
            "created": self.created,
            "bytes_written": self.bytes_written,
        }

    def __str__(self) -> str:
        """String representation"""
        action = "Created" if self.created else "Overwrote"
        return f"{action} {self.path} ({self.bytes_written} bytes)"


class WriteTool(BaseTool):
    """
    Tool for writing file contents to the filesystem

    Accepts:
        path: str - File path (absolute or relative)
        content: str - Content to write

    Returns:
        WriteResult - Write operation result

    Raises:
        PermissionError: If file can't be written
        OSError: If disk is full or other OS error
        ValueError: If parameters are invalid

    Example:
        tool = WriteTool()
        result = await tool.execute(path="app.py", content="print('Hello')")
        print(result)  # "Created app.py (14 bytes)"
    """

    def __init__(self, workspace_root: Optional[str] = None):
        """
        Initialize WriteTool

        Args:
            workspace_root: Optional root directory for relative paths.
                           Defaults to current working directory.
        """
        self._workspace_root = Path(workspace_root) if workspace_root else Path.cwd()

    @property
    def name(self) -> str:
        """Tool identifier"""
        return "write"

    @property
    def description(self) -> str:
        """Human-readable description"""
        return "Write file contents to the filesystem"

    def validate_params(self, **kwargs) -> dict[str, Any]:
        """
        Validate parameters before execution

        Args:
            **kwargs: Must contain 'path' and 'content' parameters

        Returns:
            Validated parameters dict

        Raises:
            ValueError: If required parameters are missing or invalid
        """
        if "path" not in kwargs:
            raise ValueError("path parameter is required")

        if "content" not in kwargs:
            raise ValueError("content parameter is required")

        # SEC-008: Use standardized validation
        validate_path_input(kwargs["path"])
        validate_content_input(kwargs["content"])

        return kwargs

    async def execute(self, **kwargs) -> WriteResult:
        """
        Execute file write operation

        Args:
            **kwargs: Must contain 'path' and 'content' parameters

        Returns:
            WriteResult with operation details

        Raises:
            PermissionError: If file can't be written
            OSError: If disk is full or other OS error
            ValueError: If parameters are invalid
        """
        # Validate parameters
        validated = self.validate_params(**kwargs)
        path_str = validated["path"]
        content = validated["content"]

        # Convert to Path object
        file_path = Path(path_str)

        # Handle relative paths
        if not file_path.is_absolute():
            file_path = self._workspace_root / file_path

        # Normalize path (resolve .., etc.)
        try:
            file_path = file_path.resolve()
        except (OSError, RuntimeError) as e:
            raise ValueError(f"Cannot resolve path {path_str}: {e}")

        # SEC-003: Validate path is within workspace after resolution
        validate_path_within_workspace(file_path, self._workspace_root)

        # Check if file exists (to determine if we're creating or overwriting)
        file_existed = file_path.exists()

        # If file exists, check if it's actually a file
        if file_existed and not file_path.is_file():
            raise ValueError(f"Path exists but is not a file: {file_path}")

        # Create parent directories if needed
        try:
            file_path.parent.mkdir(parents=True, exist_ok=True)
        except PermissionError:
            raise PermissionError(f"Permission denied creating directory: {file_path.parent}")
        except OSError as e:
            raise OSError(f"Failed to create parent directory {file_path.parent}: {e}")

        # PERF-009: Write file asynchronously to avoid blocking event loop
        try:
            loop = asyncio.get_event_loop()
            await loop.run_in_executor(
                _file_executor,
                partial(file_path.write_text, content, encoding="utf-8")
            )
            bytes_written = len(content.encode("utf-8"))

            return WriteResult(
                success=True,
                path=file_path,
                created=not file_existed,
                bytes_written=bytes_written,
            )

        except PermissionError:
            raise PermissionError(f"Permission denied writing file: {file_path}")

        except OSError as e:
            # Check if error is due to disk full
            if "No space left on device" in str(e) or e.errno == 28:  # ENOSPC
                raise OSError(f"Disk full: cannot write to {file_path}")
            raise OSError(f"Failed to write file {file_path}: {e}")
