#!/usr/bin/env python3

import logging
import os
import pathlib
from typing import Optional

from ..access import check_edit_permission
from ..common import normalize_file_path
from ..git import commit_changes, get_repository_root, is_git_repository
from ..mcp import mcp
from ..shell import run_command
from .commit_utils import append_commit_hash

__all__ = [
    "rm",
]


@mcp.tool()
async def rm(
    path: str, description: str, chat_id: str, commit_hash: Optional[str] = None
) -> str:
    """Removes a file using git rm and commits the change.
    Provide a short description of why the file is being removed.

    Before using this tool:
    1. Ensure the file exists and is tracked by git
    2. Provide a meaningful description of why the file is being removed

    Args:
        path: The path to the file to remove (can be relative to the project root or absolute)
        description: Short description of why the file is being removed
        chat_id: The unique ID to identify the chat session
        commit_hash: Optional Git commit hash for version tracking

    Returns:
        A success message

    """
    # Normalize the file path
    full_path = normalize_file_path(path)

    # Validate the file path
    if not os.path.exists(full_path):
        raise FileNotFoundError(f"File does not exist: {path}")

    # Safety check: Verify the file is within a git repository with codemcp.toml
    if not await is_git_repository(os.path.dirname(full_path)):
        raise ValueError(f"File is not in a Git repository: {path}")

    # Check edit permission (which verifies codemcp.toml exists)
    is_permitted, permission_message = await check_edit_permission(full_path)
    if not is_permitted:
        raise ValueError(permission_message)

    # Determine if it's a file or directory
    os.path.isdir(full_path)

    # Get git repository root
    git_root = await get_repository_root(os.path.dirname(full_path))
    # Ensure paths are absolute and resolve any symlinks
    full_path_resolved = os.path.realpath(full_path)
    git_root_resolved = os.path.realpath(git_root)

    # Use pathlib to check if the file is within the git repo
    # This handles path traversal correctly on all platforms
    try:
        # Convert to Path objects
        full_path_obj = pathlib.Path(full_path_resolved)
        git_root_obj = pathlib.Path(git_root_resolved)

        # Check if file is inside the git repo using Path.relative_to
        # This will raise ValueError if full_path is not inside git_root
        full_path_obj.relative_to(git_root_obj)
    except ValueError:
        msg = f"Path {full_path} is not within the git repository at {git_root}"
        logging.error(msg)
        raise ValueError(msg)

    # Get the relative path using pathlib
    rel_path = os.path.relpath(full_path_resolved, git_root_resolved)
    logging.info(f"Using relative path: {rel_path}")

    # Check if the file is tracked by git from the git root
    await run_command(
        ["git", "ls-files", "--error-unmatch", rel_path],
        cwd=git_root_resolved,
        check=True,
        capture_output=True,
        text=True,
    )

    # If we get here, the file is tracked by git, so we can remove it
    await run_command(
        ["git", "rm", rel_path],
        cwd=git_root_resolved,
        check=True,
        capture_output=True,
        text=True,
    )

    # Commit the changes
    logging.info(f"Committing removal of file: {rel_path}")
    success, commit_message = await commit_changes(
        git_root_resolved,
        f"Remove {rel_path}: {description}",
        chat_id,
        commit_all=False,  # No need for commit_all since git rm already stages the change
    )

    result = ""
    if success:
        result = f"Successfully removed file {rel_path}."
    else:
        result = f"File {rel_path} was removed but failed to commit: {commit_message}"

    # Append commit hash
    result, _ = await append_commit_hash(result, git_root_resolved, commit_hash)
    return result
