# this_file: claif_cla/src/claif_cla/install.py

import shutil
from pathlib import Path

from loguru import logger

# Import common install functionality
try:
    from claif.common.utils import prompt_tool_configuration
    from claif.install import (
        bundle_all_tools,
        ensure_bun_installed,
        get_install_location,
        install_npm_package_globally,
        uninstall_tool,
    )
except ImportError:
    # Fallback if claif package not available
    logger.warning("claif package not found, using local implementations")
    from claif_cla.install_fallback import (
        bundle_all_tools,
        ensure_bun_installed,
        get_install_location,
        install_npm_package_globally,
        uninstall_tool,
    )

    def prompt_tool_configuration(tool_name: str, config_commands: List[str]) -> None:
        """
        Fallback implementation for tool configuration prompt when `claif.common` is not available.

        This function is a no-op and serves as a placeholder.

        Args:
            tool_name: The name of the tool being configured.
            config_commands: A list of commands that could be used for configuration.
        """
        logger.debug(f"Fallback prompt_tool_configuration called for {tool_name}. Commands: {config_commands}")
        # In a non-interactive fallback, we just log that configuration would be prompted.
        pass


def install_claude_bundled(install_dir: Path, dist_dir: Path) -> bool:
    """
    Installs the bundled Claude CLI executable and its dependencies.

    This function assumes that the Claude CLI and its `yoga.wasm` dependency
    are located within a `claude` subdirectory inside the `dist_dir`.
    It copies these files to a dedicated installation directory and creates
    a wrapper script to ensure correct execution.

    Args:
        install_dir: The target directory where the Claude executable will be installed.
        dist_dir: The distribution directory containing the bundled Claude CLI files.

    Returns:
        True if the installation is successful, False otherwise.
    """
    try:
        claude_source: Path = dist_dir / "claude"
        if not claude_source.exists():
            logger.error(f"Bundled Claude source not found at {claude_source}")
            return False

        # Create a dedicated installation directory for Claude (e.g., ~/.local/bin/claude-bin)
        # The -bin suffix is used to avoid naming conflicts with the wrapper script.
        claude_install_dir: Path = install_dir / "claude-bin"
        if claude_install_dir.exists():
            logger.info(f"Removing existing Claude installation directory: {claude_install_dir}")
            shutil.rmtree(claude_install_dir)

        # Copy the entire Claude directory (which should contain the executable and yoga.wasm)
        logger.info(f"Copying Claude files from {claude_source} to {claude_install_dir}")
        shutil.copytree(claude_source, claude_install_dir)

        # Create a wrapper script in the main install_dir that points to the actual executable.
        # This allows calling 'claude' directly from PATH.
        wrapper_path: Path = install_dir / "claude"
        if wrapper_path.exists():
            logger.info(f"Removing existing Claude wrapper script: {wrapper_path}")
            wrapper_path.unlink()  # Remove existing file or symlink

        # The wrapper script changes to the claude_install_dir and then executes the claude binary.
        wrapper_content: str = f"""#!/usr/bin/env bash
# Claude CLI wrapper script
# Generated by claif install

# Change to Claude directory and run the executable
cd "{claude_install_dir}" && exec ./claude "$@"
"""

        wrapper_path.write_text(wrapper_content)
        wrapper_path.chmod(0o755)  # Make the wrapper script executable

        logger.success(f"✓ Claude CLI installed successfully at {claude_install_dir} with wrapper at {wrapper_path}")
        return True

    except Exception as e:
        logger.error(f"Failed to install bundled Claude: {e}")
        return False


def install_claude() -> Dict[str, Any]:
    """
    Orchestrates the installation of the Claude CLI, including bun, npm package,
    bundling, and final executable installation.

    Returns:
        A dictionary indicating the installation status, including lists of
        installed and failed components, and a message.
    """
    # Step 1: Ensure 'bun' is installed, as it's required for bundling.
    logger.info("Ensuring bun is installed...")
    if not ensure_bun_installed():
        return {"installed": [], "failed": ["bun"], "message": "Bun installation failed. Please install Bun manually or check your internet connection."}

    install_dir: Path = get_install_location()

    # Step 2: Install the @anthropic-ai/claude-code npm package globally.
    logger.info("Installing @anthropic-ai/claude-code npm package globally...")
    if not install_npm_package_globally("@anthropic-ai/claude-code"):
        return {"installed": [], "failed": ["@anthropic-ai/claude-code"], "message": "Failed to install @anthropic-ai/claude-code npm package. Check npm/network."}

    # Step 3: Bundle all CLI tools into a distribution directory.
    logger.info("Bundling Claude CLI tools...")
    dist_dir: Optional[Path] = bundle_all_tools()
    if not dist_dir:
        return {"installed": [], "failed": ["bundling"], "message": "Failed to bundle Claude CLI tools. Check bun installation and permissions."}

    # Step 4: Install the specifically bundled Claude executable.
    logger.info("Installing bundled Claude executable...")
    if install_claude_bundled(install_dir, dist_dir):
        logger.success("🎉 Claude CLI installed successfully!")
        logger.info(f"Claude executable available at: {install_dir / 'claude'}")

        # Prompt for post-installation configuration steps.
        config_commands: List[str] = [
            "claude auth login",
            "claude --help",
        ]
        prompt_tool_configuration("Claude", config_commands)

        return {"installed": ["claude"], "failed": [], "install_dir": str(install_dir)}
    else:
        return {"installed": [], "failed": ["claude"], "message": "Failed to install the bundled Claude executable."}


def uninstall_claude() -> Dict[str, Any]:
    """
    Uninstalls the Claude CLI by removing its bundled executable and associated directory.

    Returns:
        A dictionary indicating the uninstallation status, including lists of
        uninstalled and failed components, and a message.
    """
    logger.info("Attempting to uninstall Claude CLI...")

    # Attempt to uninstall the main 'claude' wrapper script.
    if uninstall_tool("claude"):
        logger.success("✓ Claude wrapper script uninstalled.")
        uninstalled_components = ["claude"]
    else:
        logger.warning("Claude wrapper script not found or failed to uninstall.")
        uninstalled_components = []

    # Also remove the 'claude-bin' directory where the actual executable resides.
    install_dir: Path = get_install_location()
    claude_bin_dir: Path = install_dir / "claude-bin"
    if claude_bin_dir.exists() and claude_bin_dir.is_dir():
        try:
            shutil.rmtree(claude_bin_dir)
            logger.success(f"✓ Removed Claude installation directory: {claude_bin_dir}")
            uninstalled_components.append("claude-bin-dir")
        except Exception as e:
            logger.error(f"Failed to remove Claude installation directory {claude_bin_dir}: {e}")
            return {"uninstalled": uninstalled_components, "failed": ["claude-bin-dir"], "message": f"Failed to remove directory: {e}"}

    if uninstalled_components:
        return {"uninstalled": uninstalled_components, "failed": [], "message": "Claude uninstalled successfully!"}
    else:
        return {"uninstalled": [], "failed": ["claude"], "message": "Claude was not found or failed to uninstall."}


def is_claude_installed() -> bool:
    """
    Checks if the Claude CLI is considered installed.

    This checks for the presence of the main 'claude' wrapper script and/or
    the 'claude-bin' installation directory.

    Returns:
        True if Claude is considered installed, False otherwise.
    """
    install_dir: Path = get_install_location()
    claude_wrapper_path: Path = install_dir / "claude"
    claude_bin_dir: Path = install_dir / "claude-bin"

    return (claude_wrapper_path.exists() and claude_wrapper_path.is_file()) or \
           (claude_bin_dir.exists() and claude_bin_dir.is_dir())


def get_claude_status() -> Dict[str, Any]:
    """
    Retrieves the installation status of the Claude CLI.

    Returns:
        A dictionary containing:
        - 'installed': True if Claude is installed, False otherwise.
        - 'path': The path to the main Claude executable/wrapper if installed, else None.
        - 'type': A description of the installation type (e.g., "bundled (claif-owned)"), else None.
    """
    if is_claude_installed():
        install_dir: Path = get_install_location()
        claude_path: Path = install_dir / "claude"
        return {"installed": True, "path": str(claude_path), "type": "bundled (claif-owned)"}
    return {"installed": False, "path": None, "type": None}
