"""
CLI Decorators for runbooks package - Enterprise CLI Standardization

Provides standard decorators for common CLI options, eliminating code duplication
and ensuring consistent user experience across all runbooks modules.

Following KISS & DRY principles - enhance existing structure without creating complexity.

Canonical Decorator Ordering (MANDATORY — violating order causes Click parameter conflicts):

    @click.group() | @click.command()   # 1. Command registration (outermost)
    @common_aws_options                  # 2. AWS options suite (profile/region/dry-run)
    @common_output_options               # 3. Output options (csv/json/markdown) — if needed
    @click.option("--my-opt", ...)       # 4. Command-specific options
    @click.pass_context                  # 5. Context injection (innermost before function)
    def my_command(ctx, profile, region, dry_run, ...):

Notes:
- NEVER combine @profile_option with @common_aws_options on the same command.
  @profile_option is for standalone commands (e.g., vpc/cli.py) that need ONLY --profile.
  @common_aws_options provides --profile, --region, and --dry-run as a bundle.
- @click.pass_context MUST be the last decorator before the function definition.
- Decorators are applied bottom-up (innermost = first applied = last in list).
"""

import time

import click
from functools import wraps
from typing import Callable, Any

from runbooks.common.rich_utils import console as _console


def common_aws_options(f: Callable) -> Callable:
    """
    Standard AWS options for all runbooks commands.

    Provides consistent AWS configuration options across all modules:
    - --profile: AWS profile override (highest priority in 3-tier system)
    - --region: AWS region override
    - --dry-run: Safe analysis mode (enterprise default)

    Usage:
        @common_aws_options
        @click.command()
        def my_command(profile, region, dry_run, **kwargs):
            # Your command logic here
    """

    @click.option(
        "--profile",
        help="""AWS profile for single-account operations.

📋 Profile Selection Guide:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Single Account → Use --profile YOUR_PROFILE
  Example: --profile dev-account
  When: Developer/operator working in one AWS account

Multi-Account LZ → Use --all-profiles (see inventory commands)
  Example: --all-profiles
  When: Platform team discovering across organization

🔐 Enrichment Profiles (Automatic):
  • Organizations: MANAGEMENT_PROFILE
  • Costs: BILLING_PROFILE
  Note: Separate from discovery profile

Decision: Single account = --profile | Multi-account = --all-profiles""",
        type=str,
    )
    @click.option("--region", help="AWS region override (default: ap-southeast-2)", type=str, default="ap-southeast-2")
    @click.option(
        "--dry-run",
        is_flag=True,
        default=True,
        help="Safe analysis mode - no resource modifications (enterprise default)",
    )
    @wraps(f)
    def wrapper(*args, **kwargs):
        return f(*args, **kwargs)

    return wrapper


def common_output_options(f: Callable) -> Callable:
    """
    Standard output options for all runbooks commands.

    Provides consistent output formatting and export options:
    - -f, --format, --output-format: JSON, CSV, table, PDF output formats (triple alias)
    - --output-dir: Output directory for generated files
    - --all-outputs: Generate all output formats (JSON, CSV, PDF, Markdown)
    - --csv, --json, --markdown: Convenience flags for single format export (Track 5: Rich UX)
    - --export: [DEPRECATED] Use --all-outputs instead (backward compatibility)

    Usage:
        @common_output_options
        @click.command()
        def my_command(format, output_dir, all_outputs, csv, json, markdown, export, **kwargs):
            # Convenience flags automatically activate all_outputs
            # Your command logic here (parameter name is 'format')
    """

    @click.option(
        "-f",
        "--format",
        "--output-format",
        type=click.Choice(["json", "csv", "table", "pdf", "markdown"], case_sensitive=False),
        default="table",
        help="Output format for results display (-f/--format preferred, --output-format legacy)",
    )
    @click.option(
        "--output-dir",
        default="./awso_evidence",
        help="Directory for generated files and evidence packages",
        type=click.Path(),
    )
    @click.option(
        "--all-outputs",
        is_flag=True,
        help="Generate all output formats (JSON, CSV, PDF, Markdown) - use with --output-dir",
    )
    # Track 5: Rich UX Adoption - Convenience export flags (finops pattern)
    @click.option(
        "--csv", "export_csv", is_flag=True, help="Export to CSV format (convenience flag, activates --all-outputs)"
    )
    @click.option(
        "--json", "export_json", is_flag=True, help="Export to JSON format (convenience flag, activates --all-outputs)"
    )
    @click.option(
        "--markdown",
        "export_markdown",
        is_flag=True,
        help="Export to Markdown format (convenience flag, activates --all-outputs)",
    )
    # Backward compatibility (deprecated)
    @click.option(
        "--export",
        is_flag=True,
        hidden=True,  # Hide from --help
        help="[DEPRECATED] Use --all-outputs instead",
    )
    @wraps(f)
    def wrapper(*args, **kwargs):
        # Track 5: Auto-activate all_outputs if any convenience flag is set
        if kwargs.get("export_csv") or kwargs.get("export_json") or kwargs.get("export_markdown"):
            kwargs["all_outputs"] = True
        return f(*args, **kwargs)

    return wrapper


def mcp_validation_option(f: Callable) -> Callable:
    """
    MCP validation option for cost optimization and inventory modules.

    Provides MCP (Model Context Protocol) validation capability for enhanced accuracy:
    - --validate: Enable MCP validation with ≥99.5% accuracy target
    - --confidence-threshold: Minimum confidence threshold (default: 99.5%)

    Usage:
        @mcp_validation_option
        @click.command()
        def my_command(validate, confidence_threshold, **kwargs):
            # Your command logic with MCP validation
    """

    @click.option("--validate", is_flag=True, help="Enable MCP validation for enhanced accuracy (≥99.5% target)")
    @click.option(
        "--confidence-threshold",
        type=float,
        default=99.5,
        help="Minimum confidence threshold for validation (default: 99.5%%)",
    )
    @wraps(f)
    def wrapper(*args, **kwargs):
        return f(*args, **kwargs)

    return wrapper


def enterprise_options(f: Callable) -> Callable:
    """
    Enterprise-specific options for multi-account operations.

    Provides enterprise deployment options:
    - --all: Use all available AWS profiles
    - --combine: Combine profiles from same AWS account
    - --approval-required: Require human approval for operations

    Usage:
        @enterprise_options
        @click.command()
        def my_command(all_profiles, combine, approval_required, **kwargs):
            # Your enterprise command logic
    """

    @click.option(
        "--all", "all_profiles", is_flag=True, help="Use all available AWS profiles for multi-account operations"
    )
    @click.option("--combine", is_flag=True, help="Combine profiles from the same AWS account for unified reporting")
    @click.option(
        "--approval-required",
        is_flag=True,
        default=True,
        help="Require human approval for state-changing operations (enterprise default)",
    )
    @wraps(f)
    def wrapper(*args, **kwargs):
        return f(*args, **kwargs)

    return wrapper


def multi_account_profile_option(f: Callable) -> Callable:
    """KISS wrapper: --profile auto-resolves to management/billing/ops triple from env vars.

    Allows CxO notebooks to pass a single --profile instead of 3 separate flags.
    Triple flags (--management-profile, --billing-profile, --ops-profile /
    --operational-profile) remain for backward compat — --profile only fills in
    what's missing.

    Env var resolution order:
        --profile X  →  management_profile  = AWS_MANAGEMENT_PROFILE (or X)
                     →  billing_profile     = AWS_BILLING_PROFILE    (or X)
                     →  ops_profile /
                        operational_profile = X (the provided profile)

    Usage:
        @multi_account_profile_option
        @click.option("--management-profile", ...)
        @click.option("--billing-profile", ...)
        @click.option("--ops-profile", ...)          # OR --operational-profile
        @click.pass_context
        def my_command(ctx, management_profile, billing_profile, ops_profile, ...):
            # --profile auto-fills any of the three that were omitted
    """

    @click.option(
        "--profile",
        "simple_profile",
        default=None,
        help=(
            "AWS profile (KISS: auto-resolves management/billing/ops from "
            "AWS_MANAGEMENT_PROFILE, AWS_BILLING_PROFILE env vars)"
        ),
    )
    @wraps(f)
    def wrapper(*args, simple_profile=None, **kwargs):
        if simple_profile:
            # Fill in only what the caller did NOT explicitly provide.
            # Supports both --operational-profile (finops) and --ops-profile (inventory).
            if not kwargs.get("management_profile"):
                kwargs["management_profile"] = _os.getenv("AWS_MANAGEMENT_PROFILE", simple_profile)
            if not kwargs.get("billing_profile"):
                kwargs["billing_profile"] = _os.getenv("AWS_BILLING_PROFILE", simple_profile)
            if not kwargs.get("operational_profile"):
                kwargs["operational_profile"] = simple_profile
            if not kwargs.get("ops_profile"):
                kwargs["ops_profile"] = simple_profile
        return f(*args, **kwargs)

    return wrapper


def performance_options(f: Callable) -> Callable:
    """
    Performance monitoring options for enterprise operations.

    Provides performance monitoring and optimization options:
    - --performance-target: Target execution time in seconds
    - --timeout: Maximum execution timeout
    - --parallel: Enable parallel processing where supported

    Usage:
        @performance_options
        @click.command()
        def my_command(performance_target, timeout, parallel, **kwargs):
            # Your performance-monitored command
    """

    @click.option(
        "--performance-target", type=int, default=30, help="Target execution time in seconds (enterprise default: 30s)"
    )
    @click.option("--timeout", type=int, default=300, help="Maximum execution timeout in seconds (default: 5 minutes)")
    @click.option("--parallel", is_flag=True, help="Enable parallel processing for multi-resource operations")
    @wraps(f)
    def wrapper(*args, **kwargs):
        return f(*args, **kwargs)

    return wrapper


def all_standard_options(f: Callable) -> Callable:
    """
    Convenience decorator applying all standard options.

    Combines common_aws_options, common_output_options, mcp_validation_option,
    enterprise_options, and performance_options for comprehensive CLI commands.

    Usage:
        @all_standard_options
        @click.command()
        def comprehensive_command(**kwargs):
            # Command with all standard options available
    """

    @performance_options
    @enterprise_options
    @mcp_validation_option
    @common_output_options
    @common_aws_options
    @wraps(f)
    def wrapper(*args, **kwargs):
        return f(*args, **kwargs)

    return wrapper


def common_multi_account_options(f: Callable) -> Callable:
    """
    Multi-account and multi-region AWS options for enterprise operations.

    Provides:
    - --all-profiles: Process all configured AWS profiles (multi-account)
    - --profiles: [LEGACY] Specific profiles (use --all-profiles for all)
    - --regions: Specific AWS regions (space-separated)
    - --all-regions: Process all enabled AWS regions

    Note: --profile is provided by common_aws_options decorator

    Usage:
        @common_multi_account_options
        @common_aws_options
        @click.command()
        def my_command(profile, all_profiles, profiles, regions, all_regions, **kwargs):
            # Multi-account command logic
    """

    @click.option(
        "--all",
        "all_profiles",
        is_flag=True,
        default=False,
        help="""Multi-account discovery (CENTRALISED_OPS_PROFILE as aggregator).

📋 Behavior:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

• Queries AWS Resource Explorer aggregator index
• Discovers resources across ALL accounts in Landing Zone
• Requires CENTRALISED_OPS_PROFILE with cross-account permissions

🔐 Enrichment Layers (Automatic):
  • Organizations metadata: MANAGEMENT_PROFILE
  • Cost data: BILLING_PROFILE
  Note: Enrichment uses separate profiles regardless of discovery mode

Use Case: Enterprise platform teams managing 67+ account Landing Zones""",
    )
    @click.option(
        "--profiles",
        type=str,
        multiple=True,
        help='Specific AWS profiles (comma-separated, e.g., "billing,security,audit")',
    )
    @click.option("--regions", type=str, multiple=True, help="Specific AWS regions (space-separated)")
    @click.option("--all-regions", is_flag=True, default=False, help="Process all enabled AWS regions")
    @wraps(f)
    def wrapper(*args, **kwargs):
        return f(*args, **kwargs)

    return wrapper


def common_filter_options(f: Callable) -> Callable:
    """
    Common filtering options for resource discovery.

    Provides:
    - --tags: Filter by tags (key=value format)
    - --accounts: Filter by specific account IDs

    Usage:
        @common_filter_options
        @click.command()
        def my_command(tags, accounts, **kwargs):
            # Filtering logic
    """

    @click.option("--tags", type=str, multiple=True, help="Filter by tags (key=value format)")
    @click.option("--accounts", type=str, multiple=True, help="Filter by specific account IDs")
    @wraps(f)
    def wrapper(*args, **kwargs):
        return f(*args, **kwargs)

    return wrapper


def profile_option(func: Callable) -> Callable:
    """
    Standardized --profile option for AWS commands.

    Provides a consistent --profile / -p option backed by the AWS_PROFILE
    environment variable, replacing hand-written @click.option("--profile"...)
    declarations across individual command functions.

    WARNING: Do NOT use this decorator on commands that also use @common_aws_options,
    as both register a --profile option, causing Click UsageError at startup.
    Use EITHER @profile_option (simple single-option) OR @common_aws_options
    (full AWS option suite including --profile, --region, --dry-run) — never both.

    Usage:
        @profile_option
        @click.command()
        def my_command(profile, **kwargs):
            # profile is None by default; falls back to AWS_PROFILE env var
    """
    return click.option(
        "--profile",
        "-p",
        default=None,
        envvar="AWS_PROFILE",
        help="AWS profile name (default: from AWS_PROFILE env var)",
    )(func)


# ---------------------------------------------------------------------------
# Utility decorators migrated from runbooks.common.decorators (DRY promotion)
# ---------------------------------------------------------------------------


def performance_timing(f: Callable) -> Callable:
    """
    Performance timing decorator for measuring command execution time.

    Automatically tracks and reports command execution time for performance
    monitoring and optimization analysis.
    """

    @wraps(f)
    def wrapper(*args: Any, **kwargs: Any) -> Any:
        start_time = time.time()
        try:
            result = f(*args, **kwargs)
            execution_time = time.time() - start_time

            # Only show timing for operations > 1 second
            if execution_time > 1.0:
                _console.print(f"[dim]Completed in {execution_time:.2f}s[/dim]")

            return result
        except Exception as e:
            execution_time = time.time() - start_time
            _console.print(f"[red]Failed after {execution_time:.2f}s: {e}[/red]")
            raise

    return wrapper


def error_handler(f: Callable) -> Callable:
    """
    Common error handling decorator for consistent error reporting.

    Provides enterprise-grade error handling with:
    - Rich formatting for better UX
    - Consistent error message structure
    - Debug information when enabled
    """

    @wraps(f)
    def wrapper(*args: Any, **kwargs: Any) -> Any:
        try:
            return f(*args, **kwargs)
        except click.ClickException:
            # Re-raise Click exceptions as-is
            raise
        except ImportError as e:
            _console.print(f"[red]Module not available: {e}[/red]")
            _console.print("[yellow]This functionality may require additional dependencies[/yellow]")
            raise click.ClickException("Required module not available")
        except Exception as e:
            _console.print(f"[red]Unexpected error: {e}[/red]")
            _console.print("[yellow]Run with --debug for detailed error information[/yellow]")
            raise click.ClickException(str(e))

    return wrapper


def require_aws_profile(f: Callable) -> Callable:
    """
    Decorator to ensure AWS profile is properly configured.

    Validates that the AWS profile exists and is accessible before
    executing commands that require AWS API access.
    """

    @wraps(f)
    def wrapper(*args: Any, **kwargs: Any) -> Any:
        ctx = click.get_current_context()
        profile = ctx.obj.get("profile", "default")

        try:
            import boto3

            session = boto3.Session(profile_name=profile)
            session.get_credentials()

            return f(*args, **kwargs)
        except Exception as e:
            _console.print(f"[red]AWS profile '{profile}' not accessible: {e}[/red]")
            _console.print("[yellow]Run 'aws configure list-profiles' to see available profiles[/yellow]")
            raise click.ClickException(f"AWS profile '{profile}' not accessible")

    return wrapper


def enterprise_audit_trail(f: Callable) -> Callable:
    """
    Enterprise audit trail decorator for compliance and governance.

    Automatically logs command execution for audit purposes with:
    - Command name and parameters
    - User context and timestamp
    - Execution results and duration
    """

    @wraps(f)
    def wrapper(*args: Any, **kwargs: Any) -> Any:
        ctx = click.get_current_context()

        audit_data: dict = {
            "command": ctx.command.name,
            "profile": ctx.obj.get("profile", "default"),
            "region": ctx.obj.get("region", "default"),
            "dry_run": ctx.obj.get("dry_run", False),
            "timestamp": time.time(),
        }

        try:
            result = f(*args, **kwargs)
            audit_data["status"] = "success"
            audit_data["duration"] = time.time() - audit_data["timestamp"]

            if ctx.obj.get("debug"):
                _console.print(f"[dim]Audit: {audit_data}[/dim]")

            return result
        except Exception as e:
            audit_data["status"] = "error"
            audit_data["error"] = str(e)
            audit_data["duration"] = time.time() - audit_data["timestamp"]

            if ctx.obj.get("debug"):
                _console.print(f"[dim]Audit: {audit_data}[/dim]")

            raise

    return wrapper


def rich_progress(description: str = "Processing") -> Callable:
    """
    Rich progress indicator decorator for long-running operations.

    Args:
        description: Description text for the progress indicator

    Automatically shows a progress spinner for operations that take time,
    improving user experience for long-running commands.
    """

    def decorator(f: Callable) -> Callable:
        @wraps(f)
        def wrapper(*args: Any, **kwargs: Any) -> Any:
            from rich.progress import Progress, SpinnerColumn, TextColumn

            with Progress(
                SpinnerColumn(), TextColumn("[progress.description]{task.description}"), console=_console
            ) as progress:
                task = progress.add_task(description, total=None)

                try:
                    result = f(*args, **kwargs)
                    progress.update(task, description=f"{description} completed")
                    return result
                except Exception:
                    progress.update(task, description=f"{description} failed")
                    raise

        return wrapper

    return decorator


# ---------------------------------------------------------------------------
# KISS Profile Resolution — single --profile for triple-profile commands
# ---------------------------------------------------------------------------

import os as _os


def kiss_profile_resolver(f: Callable) -> Callable:
    """KISS wrapper: resolve --profile to management/billing/operational profiles.

    When --profile is provided and -m/-b/-p are NOT explicitly set,
    auto-resolves using environment variables:
      AWS_MANAGEMENT_PROFILE → management_profile
      AWS_BILLING_PROFILE    → billing_profile
      AWS_PROFILE            → operational_profile

    This enables CxO to use a single --profile flag while power users
    can still override with explicit -m/-b/-p flags.

    Usage:
        @click.option("--management-profile", "-m", ...)
        @click.option("--billing-profile", "-b", ...)
        @click.option("--operational-profile", "-p", ...)
        @click.option("--profile", default=None, help="KISS: Single profile, auto-resolves all three")
        @kiss_profile_resolver
        def my_command(management_profile, billing_profile, operational_profile, ...):
    """

    @wraps(f)
    def wrapper(*args, **kwargs):
        profile = kwargs.get("profile")
        if profile:
            if not kwargs.get("management_profile"):
                kwargs["management_profile"] = _os.getenv("AWS_MANAGEMENT_PROFILE", profile)
            if not kwargs.get("billing_profile"):
                kwargs["billing_profile"] = _os.getenv("AWS_BILLING_PROFILE", profile)
            if not kwargs.get("operational_profile"):
                kwargs["operational_profile"] = _os.getenv("AWS_PROFILE", profile)
        # Remove 'profile' from kwargs — downstream function doesn't expect it
        kwargs.pop("profile", None)
        return f(*args, **kwargs)

    return wrapper


# ---------------------------------------------------------------------------
# Backward-compatibility aliases
# module_cli_base.py imports these names; map them to the canonical functions.
# ---------------------------------------------------------------------------
rich_output_options = common_output_options
enterprise_safety_options = enterprise_options
