"""Goal definitions for Claude Code plugin installation."""

from __future__ import annotations

import logging
from dataclasses import dataclass

from pants.engine.addresses import Addresses
from pants.engine.console import Console
from pants.engine.goal import Goal, GoalSubsystem
from pants.engine.internals.selectors import Get, MultiGet
from pants.engine.rules import collect_rules, goal_rule
from pants.engine.target import Targets, WrappedTarget, WrappedTargetRequest
from pants.option.global_options import GlobalOptions
from pants.util.strutil import softwrap

from pants_claude_plugins.bundled import discover_all_bundled_plugins
from pants_claude_plugins.rules import (
    ClaudePluginBatchInstallRequest,
    ClaudePluginBatchInstallResult,
    ClaudePluginFromTargetRequest,
    ClaudePluginInstallRequest,
    MarketplaceBatchAddRequest,
    MarketplaceBatchAddResult,
)
from pants_claude_plugins.subsystem import ClaudePluginsSubsystem
from pants_claude_plugins.targets import ClaudePlugin, EnabledField

logger = logging.getLogger(__name__)


class ClaudeInstallSubsystem(GoalSubsystem):
    """Install Claude Code plugins defined in BUILD files."""

    name = "claude-install"
    help = softwrap(
        """
        Install Claude Code plugins defined as `claude_plugin` targets.

        This goal reads `claude_plugin` targets from your BUILD files and installs
        them using the Claude Code CLI.

        Example usage:

            # Install all plugins in the repo
            pants claude-install ::

            # Install plugins in a specific directory
            pants claude-install tools/claude:

            # Install a specific plugin target
            pants claude-install tools/claude:github-integration

            # Dry run to see what would be installed
            pants claude-install --dry-run ::

            # Include plugins bundled with other Pants backends
            pants claude-install --include-bundled ::
        """
    )


class ClaudeInstall(Goal):
    """The claude-install goal."""

    subsystem_cls = ClaudeInstallSubsystem


@goal_rule
async def claude_install(
    console: Console,
    targets: Targets,
    subsystem: ClaudePluginsSubsystem,
    global_options: GlobalOptions,
) -> ClaudeInstall:
    """Install Claude Code plugins from claude_plugin targets."""
    if subsystem.skip:
        console.print_stderr("Skipping claude-install (--skip is set)")
        return ClaudeInstall(exit_code=0)

    # Filter to only ClaudePlugin targets that are enabled
    plugin_targets = [
        t for t in targets
        if t.has_field(EnabledField) and t[EnabledField].value
    ]

    # Collect install requests from BUILD file targets
    install_requests: list[ClaudePluginInstallRequest] = []
    bundled_requests: list[ClaudePluginInstallRequest] = []

    if plugin_targets:
        # Convert targets to install requests
        wrapped_targets = await MultiGet(
            Get(WrappedTarget, WrappedTargetRequest(t.address, description_of_origin="claude-install goal"))
            for t in plugin_targets
        )

        target_requests = await MultiGet(
            Get(ClaudePluginInstallRequest, ClaudePluginFromTargetRequest(wt))
            for wt in wrapped_targets
        )
        install_requests.extend(target_requests)

    # Discover bundled plugins if requested
    bundled_sources: tuple[str, ...] = ()
    bundled_marketplaces: tuple[str, ...] = ()
    if subsystem.include_bundled:
        backend_packages = tuple(global_options.backend_packages)
        discovered = discover_all_bundled_plugins(
            backend_packages=backend_packages,
            default_scope=subsystem.default_scope,
        )
        bundled_requests.extend(discovered.plugins)
        bundled_sources = discovered.sources
        bundled_marketplaces = discovered.marketplaces

    # Add bundled marketplaces first (if any)
    if bundled_marketplaces:
        mode = "[DRY RUN] " if subsystem.dry_run else ""
        console.print_stdout(f"\n{mode}Adding {len(bundled_marketplaces)} marketplace(s):\n")
        for marketplace in bundled_marketplaces:
            console.print_stdout(f"  - {marketplace}")
        console.print_stdout("")

        marketplace_result = await Get(
            MarketplaceBatchAddResult,
            MarketplaceBatchAddRequest(sources=bundled_marketplaces),
        )

        for result in marketplace_result.succeeded:
            console.print_stdout(f"✓ Added marketplace {result.source}")

        for result in marketplace_result.failed:
            # Marketplace already exists is not a fatal error
            if "already" in result.error.lower() or "already" in result.output.lower():
                console.print_stdout(f"• Marketplace {result.source} already added")
            else:
                console.print_stderr(f"✗ Failed to add marketplace {result.source}")
                if result.error.strip():
                    for line in result.error.strip().split("\n"):
                        console.print_stderr(f"    {line}")

    # Check if we have anything to install
    total_requests = install_requests + bundled_requests
    if not total_requests:
        if subsystem.include_bundled:
            console.print_stdout("No claude_plugin targets or bundled plugins found.")
        else:
            console.print_stdout("No enabled claude_plugin targets found.")
            console.print_stdout("Hint: Use --include-bundled to install plugins from Pants backends.")
        return ClaudeInstall(exit_code=0)

    # Log what we're about to do
    mode = "[DRY RUN] " if subsystem.dry_run else ""

    if install_requests:
        console.print_stdout(f"\n{mode}Installing {len(install_requests)} plugin(s) from BUILD files:\n")
        for req in install_requests:
            console.print_stdout(f"  - {req.plugin_name}@{req.marketplace} (scope: {req.scope})")

    if bundled_requests:
        console.print_stdout(f"\n{mode}Installing {len(bundled_requests)} bundled plugin(s):\n")
        if bundled_sources:
            console.print_stdout(f"  Sources: {', '.join(bundled_sources)}\n")
        for req in bundled_requests:
            console.print_stdout(f"  - {req.plugin_name}@{req.marketplace} (scope: {req.scope})")

    console.print_stdout("")

    # Perform the installations
    batch_result = await Get(
        ClaudePluginBatchInstallResult,
        ClaudePluginBatchInstallRequest(plugins=tuple(total_requests)),
    )

    # Report results
    success_count = len(batch_result.succeeded)
    fail_count = len(batch_result.failed)

    for result in batch_result.succeeded:
        console.print_stdout(f"✓ Installed {result.full_name}")
        if result.output.strip():
            for line in result.output.strip().split("\n"):
                console.print_stdout(f"    {line}")

    for result in batch_result.failed:
        console.print_stderr(f"✗ Failed to install {result.full_name}")
        if result.error.strip():
            for line in result.error.strip().split("\n"):
                console.print_stderr(f"    {line}")

    console.print_stdout(f"\n{mode}Summary: {success_count} succeeded, {fail_count} failed")

    if batch_result.failed and subsystem.fail_on_error:
        return ClaudeInstall(exit_code=1)

    return ClaudeInstall(exit_code=0)


def rules():
    """Return all rules for this module."""
    return collect_rules()
