"""
EditTool20250728 - File editor with view/create/str_replace/insert commands.

Anthropic's native trained tool for file editing operations.
"""

from pathlib import Path
from typing import Any, Literal

from .base import BaseAnthropicTool, CLIResult


class EditTool20250728(BaseAnthropicTool):
    """
    File editor supporting view, create, str_replace, and insert operations.

    Trained by Anthropic, this tool provides comprehensive file editing
    capabilities with strict safety checks.
    """

    api_type: Literal["text_editor_20250728"] = "text_editor_20250728"
    name: Literal["str_replace_based_edit_tool"] = "str_replace_based_edit_tool"
    beta_flag: str | None = None

    async def __call__(
        self,
        command: Literal["view", "create", "str_replace", "insert"],
        path: str,
        file_text: str | None = None,
        old_str: str | None = None,
        new_str: str | None = None,
        insert_line: int | None = None,
        view_range: list[int] | None = None,
        **kwargs: Any,
    ) -> CLIResult:
        """
        Execute a file editing command.

        Args:
            command: The operation to perform
            path: Absolute path to the file
            file_text: Full file content (for create)
            old_str: String to replace (for str_replace)
            new_str: Replacement string (for str_replace/insert)
            insert_line: Line number to insert at (for insert)
            view_range: [start, end] line range (for view)
            **kwargs: Additional arguments (ignored)

        Returns:
            CLIResult with exit code, output, and error
        """
        # Validate absolute path
        file_path = Path(path)
        if not file_path.is_absolute():
            return CLIResult(
                exit_code=1,
                output="",
                error=f"Error: path must be absolute, got: {path}"
            )

        try:
            if command == "view":
                return await self._view(file_path, view_range)
            elif command == "create":
                return await self._create(file_path, file_text)
            elif command == "str_replace":
                return await self._str_replace(file_path, old_str, new_str)
            elif command == "insert":
                return await self._insert(file_path, insert_line, new_str)
            else:
                return CLIResult(
                    exit_code=1,
                    output="",
                    error=f"Error: unknown command: {command}"
                )
        except Exception as e:
            return CLIResult(
                exit_code=1,
                output="",
                error=f"Error: {str(e)}"
            )

    async def _view(self, path: Path, view_range: list[int] | None) -> CLIResult:
        """View file contents with line numbers."""
        if not path.exists():
            return CLIResult(
                exit_code=1,
                output="",
                error=f"Error: file not found: {path}"
            )

        content = path.read_text()
        lines = content.splitlines(keepends=True)

        # Apply view range if specified
        if view_range:
            start, end = view_range
            lines = lines[start - 1:end]
            start_num = start
        else:
            start_num = 1

        # Format with line numbers
        formatted_lines = [
            f"{start_num + i}|{line.rstrip()}"
            for i, line in enumerate(lines)
        ]

        return CLIResult(
            exit_code=0,
            output="\n".join(formatted_lines),
            error=""
        )

    async def _create(self, path: Path, file_text: str | None) -> CLIResult:
        """Create a new file with the given content."""
        if file_text is None:
            return CLIResult(
                exit_code=1,
                output="",
                error="Error: file_text is required for create command"
            )

        if path.exists():
            return CLIResult(
                exit_code=1,
                output="",
                error=f"Error: file already exists: {path}"
            )

        # Create parent directories if needed
        path.parent.mkdir(parents=True, exist_ok=True)

        # Write the file
        path.write_text(file_text)

        return CLIResult(
            exit_code=0,
            output=f"File created: {path}",
            error=""
        )

    async def _str_replace(
        self,
        path: Path,
        old_str: str | None,
        new_str: str | None
    ) -> CLIResult:
        """Replace a unique occurrence of old_str with new_str."""
        if old_str is None:
            return CLIResult(
                exit_code=1,
                output="",
                error="Error: old_str is required for str_replace command"
            )

        if new_str is None:
            return CLIResult(
                exit_code=1,
                output="",
                error="Error: new_str is required for str_replace command"
            )

        if not path.exists():
            return CLIResult(
                exit_code=1,
                output="",
                error=f"Error: file not found: {path}"
            )

        content = path.read_text()

        # Check for unique match
        count = content.count(old_str)
        if count == 0:
            return CLIResult(
                exit_code=1,
                output="",
                error=f"Error: old_str not found in file: {old_str!r}"
            )
        elif count > 1:
            return CLIResult(
                exit_code=1,
                output="",
                error=f"Error: old_str must match exactly once, found {count} matches"
            )

        # Perform replacement
        new_content = content.replace(old_str, new_str)
        path.write_text(new_content)

        return CLIResult(
            exit_code=0,
            output=f"Replaced 1 occurrence in: {path}",
            error=""
        )

    async def _insert(
        self,
        path: Path,
        insert_line: int | None,
        new_str: str | None
    ) -> CLIResult:
        """Insert new_str at the specified line number."""
        if insert_line is None:
            return CLIResult(
                exit_code=1,
                output="",
                error="Error: insert_line is required for insert command"
            )

        if new_str is None:
            return CLIResult(
                exit_code=1,
                output="",
                error="Error: new_str is required for insert command"
            )

        if not path.exists():
            return CLIResult(
                exit_code=1,
                output="",
                error=f"Error: file not found: {path}"
            )

        content = path.read_text()
        lines = content.splitlines(keepends=True)

        # Validate line number
        if insert_line < 0 or insert_line > len(lines):
            return CLIResult(
                exit_code=1,
                output="",
                error=f"Error: insert_line {insert_line} out of range [0, {len(lines)}]"
            )

        # Insert the new string
        lines.insert(insert_line, new_str)
        new_content = "".join(lines)
        path.write_text(new_content)

        return CLIResult(
            exit_code=0,
            output=f"Inserted text at line {insert_line} in: {path}",
            error=""
        )

    def to_params(self) -> dict[str, Any]:
        """Convert to Anthropic API tool parameter format.

        Returns:
            Tool definition for Anthropic API with text_editor_20250728 type
        """
        return {
            "type": self.api_type,
            "name": self.name,
        }
