"""CLI commands for K8s allocation."""

from typing import List, Optional

import typer
from rich.console import Console
from rich.table import Table

from ..config import K8sConfig, CLUSTERS_COUNT, NAMESPACES_PER_CLUSTER, WORKLOADS_PER_NAMESPACE, MIN_RESOURCES_PER_WORKLOAD, MINIMUM_RESOURCES_REQUIRED

console = Console()
k8s_app = typer.Typer(help="K8s allocation commands")


@k8s_app.command("run")
def run_k8s_allocation(
    config: str = typer.Option(..., help="Path to k8s configuration YAML file"),
    month: str = typer.Option(None, help="Specific month (YYYY-MM) - optional override"),
    provider: str = typer.Option(None, help="Specific provider (AWS/GCP/Azure) - optional override"),
    debug: bool = typer.Option(False, help="Enable debug logging for troubleshooting"),
    force: bool = typer.Option(False, "--force", help="Force reprocessing: deletes existing k8s data and reprocesses"),
    api: bool = typer.Option(False, "--api", help="Execute via API - same as default (kept for backward compatibility)"),
    local: bool = typer.Option(False, "--local", help="Execute locally instead of via API (for development/debugging)"),
):
    """
    Process K8s allocation for billing data.

    Reads from generate stage S3 output and writes to k8s stage S3 output.
    Processes one month at a time for memory efficiency.
    Uses M:N resource-to-workload mapping with cost splitting.

    Execution modes:
    - Default: Submit to Lambda API for processing on ECS workers (scalable, observable)
    - --local: Execute locally for development, debugging, or small datasets
    - --api: Explicit API execution (same as default, kept for backward compatibility)

    Example:
        czbillgen k8s run --config k8s_config.yaml
        czbillgen k8s run --config k8s_config.yaml --local
        czbillgen k8s run --config k8s_config.yaml --month 2024-01
        czbillgen k8s run --config k8s_config.yaml --month 2024-01 --provider GCP
        czbillgen k8s run --config k8s_config.yaml --debug --local
    """
    # Validate mutually exclusive flags
    if api and local:
        console.print("[red]❌ Cannot specify both --api and --local[/red]")
        raise typer.Exit(1)

    # Execute locally if --local flag provided
    if local:
        console.print("[dim]⚙️  Executing locally...[/dim]")
        _execute_k8s_locally(config, month, provider, debug, force)
        return

    # Default: Execute via API
    console.print("[cyan]☁️  Executing via API (Lambda + ECS workers)...[/cyan]")
    from pathlib import Path
    from src.api.cli_helper import execute_stage_via_api

    execute_stage_via_api(
        job_type="k8s",
        config_path=Path(config),
        month=month,
        provider=provider,
        force=force,
    )


def _execute_k8s_locally(
    config: str,
    month: Optional[str],
    provider: Optional[str],
    debug: bool,
    force: bool
):
    """Execute K8s allocation locally."""

    try:
        # FAIL FAST: Validate AWS credentials before any work
        from src.services.environment_service import EnvironmentService
        env_service = EnvironmentService()
        env_service.validate_aws_credentials_and_access()
        
    except ValueError as e:
        console.print(f"❌ {str(e)}")
        raise typer.Exit(1)
    
    console.print("🚀 [bold green]K8s Allocation Starting[/bold green]")
    
    # Configure debug logging if requested
    if debug:
        import logging
        logging.basicConfig(level=logging.DEBUG)
        console.print("🔍 [yellow]Debug logging enabled[/yellow]")
    
    try:
        # Load configuration
        k8s_config = K8sConfig.from_yaml(config)
        k8s_config.validate()
        
        # Check for existing success marker
        if _check_and_handle_existing_k8s_data(k8s_config.dataset_name, force):
            raise typer.Exit(0)
        
        console.print(f"📊 Dataset: {k8s_config.dataset_name}")
        console.print(f"🔧 Algorithm: Dual-constraint (4 workloads per resource, 2-5 resources per workload)")
        
        # Show K8s topology configuration
        total_namespaces = 4 + NAMESPACES_PER_CLUSTER  # 4 defaults + configured additional
        total_workloads = CLUSTERS_COUNT * total_namespaces * WORKLOADS_PER_NAMESPACE
        console.print(f"🏗️  K8s Topology: {CLUSTERS_COUNT} clusters, {total_workloads} workloads")
        
        # Initialize month processor
        from ..services import MonthProcessor
        month_processor = MonthProcessor(k8s_config, console, debug=debug)
        
        # Process month-by-month data
        processing_summary = month_processor.process_all_months(month, provider)
        
        # Display final status
        if processing_summary["failed_months"] == 0 and processing_summary["processed_months"] > 0:
            console.print("🎉 [bold green]K8s allocation completed successfully![/bold green]")
        elif processing_summary["failed_months"] > 0:
            console.print("⚠️  [yellow]K8s allocation completed with some failures[/yellow]")
        else:
            console.print("❌ [red]No data processed[/red]")
        
    except FileNotFoundError as e:
        console.print(f"❌ [bold red]Configuration file not found:[/bold red] {str(e)}")
        raise typer.Exit(1)
    except ValueError as e:
        console.print(f"❌ [bold red]Configuration error:[/bold red] {str(e)}")
        raise typer.Exit(1)
    except Exception as e:
        console.print(f"❌ [bold red]Error:[/bold red] {str(e)}")
        raise typer.Exit(1)


@k8s_app.command("validate")
def validate_k8s_workload_resources(
    config: str = typer.Option(..., help="Path to k8s configuration YAML file"),
    month: str = typer.Option(None, help="Specific month to validate (YYYY-MM)"),
    fail_on_violations: bool = typer.Option(
        True, 
        help="Exit with error code if violations found"
    ),
):
    """Validate that K8s workloads meet minimum resource requirements."""
    try:
        # Validate AWS credentials
        from src.services.environment_service import EnvironmentService
        env_service = EnvironmentService()
        env_service.validate_aws_credentials_and_access()
        
        # Load configuration
        k8s_config = K8sConfig.from_yaml(config)
        k8s_config.validate()
        
        # Run validation
        from ..validation import WorkloadResourceValidator
        validator = WorkloadResourceValidator(k8s_config, console)
        results = validator.validate_dataset(month)
        
        # Exit with appropriate code
        if fail_on_violations and results.get("total_violations", 0) > 0:
            console.print("\n❌ [bold red]Validation failed: workload resource violations found[/bold red]")
            raise typer.Exit(1)
        elif results.get("status") == "success":
            console.print("\n✅ [bold green]Validation passed: all workloads meet resource requirements[/bold green]")
        
    except FileNotFoundError as e:
        console.print(f"❌ [bold red]Configuration file not found:[/bold red] {str(e)}")
        raise typer.Exit(1)
    except ValueError as e:
        console.print(f"❌ [bold red]Configuration error:[/bold red] {str(e)}")
        raise typer.Exit(1)
    except Exception as e:
        console.print(f"❌ [bold red]Validation error:[/bold red] {str(e)}")
        raise typer.Exit(1)


@k8s_app.command("status")
def show_k8s_status(
    config: str = typer.Option(..., help="Path to k8s configuration YAML file"),
):
    """Show status of K8s allocation processing."""
    console.print("📈 [bold cyan]K8s Processing Status[/bold cyan]")
    
    try:
        # Load configuration
        k8s_config = K8sConfig.from_yaml(config)
        k8s_config.validate()
        
        console.print(f"📊 Dataset: {k8s_config.dataset_name}")
        
        # TODO: Implement status logic
        console.print("⚠️  [yellow]Implementation in progress...[/yellow]")
        
    except Exception as e:
        console.print(f"❌ [bold red]Error:[/bold red] {str(e)}")
        raise typer.Exit(1)


def _check_and_handle_existing_k8s_data(dataset_name: str, force: bool) -> bool:
    """
    Check for existing K8s success marker and handle force flag logic.
    
    Args:
        dataset_name: Dataset name to check
        force: Whether force flag was provided
        
    Returns:
        True if should exit (data exists and not forced), False if should continue
    """
    if _k8s_success_marker_exists(dataset_name):
        if not force:
            console.print(f"✅ K8s stage already completed for dataset '{dataset_name}'. Use --force to reprocess.")
            return True
        
        # Force flag provided - confirm and delete existing data
        console.print(f"⚠️  This will delete ALL existing K8s data for dataset '{dataset_name}' and reprocess.")
        
        # Confirmation prompt
        confirm = typer.confirm("❓ Continue?")
        if not confirm:
            console.print("Operation cancelled.")
            return True
        
        # Delete existing K8s data
        console.print(f"🗑️  Deleting existing K8s data for dataset '{dataset_name}'...")
        _delete_k8s_stage_data(dataset_name)
        console.print(f"✅ Existing K8s data deleted for dataset '{dataset_name}'")
    
    return False


def _k8s_success_marker_exists(dataset_name: str) -> bool:
    """
    Check if K8s stage success marker exists for a dataset.
    
    Args:
        dataset_name: Dataset name to check
        
    Returns:
        True if success marker exists, False otherwise
    """
    try:
        from src.services.k8s_data_service import K8sDataService
        from src.services.success_marker_service import SuccessMarkerService
        
        k8s_data_service = K8sDataService()
        success_marker_service = SuccessMarkerService(k8s_data_service.s3_service)
        
        return success_marker_service.check_success_marker_exists(dataset_name, "k8s")
        
    except Exception:
        # If any error occurs, assume marker doesn't exist
        return False


def _delete_k8s_stage_data(dataset_name: str) -> None:
    """
    Delete all K8s stage data for a dataset.
    
    Deletes entire K8s folder: s3://bucket/czbillgen/datasets/{dataset_name}/k8s/
    This ensures no mixed configuration data remains.
    
    Args:
        dataset_name: Dataset name to delete data for
    """
    try:
        from src.services.k8s_data_service import K8sDataService
        k8s_data_service = K8sDataService()
        
        stage_prefix = f"czbillgen/datasets/{dataset_name}/k8s/"
        
        # List all objects in K8s stage folder
        s3_client = k8s_data_service.s3_service.s3_client
        bucket = k8s_data_service.s3_service.bucket
        
        paginator = s3_client.get_paginator('list_objects_v2')
        page_iterator = paginator.paginate(
            Bucket=bucket,
            Prefix=stage_prefix
        )
        
        objects_to_delete = []
        for page in page_iterator:
            if 'Contents' in page:
                objects_to_delete.extend([{'Key': obj['Key']} for obj in page['Contents']])
        
        if objects_to_delete:
            # Delete objects in batches (S3 delete_objects limit is 1000)
            for i in range(0, len(objects_to_delete), 1000):
                batch = objects_to_delete[i:i+1000]
                s3_client.delete_objects(
                    Bucket=bucket,
                    Delete={'Objects': batch}
                )
            
            console.print(f"🗑️  Deleted {len(objects_to_delete)} objects from K8s stage")
        else:
            console.print("📂 No existing K8s data found to delete")
        
        # Also delete success marker using standardized service
        from src.services.success_marker_service import SuccessMarkerService
        success_marker_service = SuccessMarkerService(k8s_data_service.s3_service)
        success_marker_service.delete_success_marker(dataset_name, "k8s")
            
    except Exception as e:
        console.print(f"❌ Failed to delete K8s stage data: {e}")
        raise typer.Exit(1)


@k8s_app.command("check-resources")
def check_k8s_eligible_resources(
    dataset: str = typer.Option(..., help="Dataset name to analyze"),
    provider: str = typer.Option(..., help="Provider to check (AWS, Azure, GCP)"),
    month: str = typer.Option(None, help="Specific month to analyze (YYYY-MM), default: all months")
):
    """
    Check unique eligible compute resources for K8s allocation.
    
    Shows how many unique compute resources are available for K8s allocation
    in the specified dataset and provider, compared to reference requirements.
    """
    # Validate provider
    valid_providers = {"AWS", "Azure", "GCP"}
    if provider not in valid_providers:
        console.print(f"❌ [red]Invalid provider '{provider}'. Must be one of: {', '.join(sorted(valid_providers))}[/red]")
        raise typer.Exit(1)
    
    console.print("🔍 [bold cyan]K8s Eligible Resources Analysis[/bold cyan]")
    console.print(f"📊 Dataset: {dataset}")
    console.print(f"📊 Provider: {provider}")
    console.print(f"📊 Stage: generate")
    
    if month:
        console.print(f"📊 Month: {month}")
    else:
        console.print("📊 Month: All months")
    
    try:
        from src.services.k8s_data_service import K8sDataService
        from src.services.compute_filter_service import ComputeFilterService
        from src.services.column_mapper import column_mapper
        
        # Initialize services
        data_service = K8sDataService()
        
        # Create a temporary config for compute filter service
        temp_config = type('TempConfig', (), {
            'dataset_name': dataset,
            'dual_constraint_algorithm': True
        })()
        compute_filter = ComputeFilterService(temp_config)
        
        # Get available months for this provider
        try:
            available_months = data_service.list_available_months(dataset, provider)
        except Exception as e:
            console.print(f"❌ [red]Failed to access dataset '{dataset}' for provider '{provider}': {str(e)}[/red]")
            raise typer.Exit(1)
        
        if not available_months:
            console.print(f"❌ [red]No data found for dataset '{dataset}' provider '{provider}'[/red]")
            raise typer.Exit(1)
        
        # Filter months if specific month requested
        if month:
            if month not in available_months:
                console.print(f"❌ [red]Month '{month}' not found for dataset '{dataset}' provider '{provider}'[/red]")
                console.print(f"Available months: {', '.join(sorted(available_months))}")
                raise typer.Exit(1)
            months_to_analyze = [month]
        else:
            months_to_analyze = sorted(available_months)
        
        console.print(f"\n📋 [cyan]Analyzing {len(months_to_analyze)} month(s)...[/cyan]")
        
        # Create results table
        table = Table(show_header=True, header_style="bold magenta")
        table.add_column("Month", style="cyan")
        table.add_column("Unique Resources", justify="right", style="green")
        table.add_column("Service → Resource Type", style="yellow")
        table.add_column("Status", style="white")
        
        # Analyze each month
        total_unique_resources = 0
        monthly_results = []
        
        for month_str in months_to_analyze:
            try:
                # Download month data
                month_data = data_service.download_month_data(dataset, provider, month_str)
                
                if month_data is None or month_data.is_empty():
                    monthly_results.append({
                        "month": month_str,
                        "unique_resources": 0,
                        "status": "No data"
                    })
                    continue
                
                # Apply compute filter
                compute_mask = compute_filter.create_compute_mask(month_data)
                eligible_compute_df = month_data.filter(compute_mask)
                
                if eligible_compute_df.is_empty():
                    unique_resources = 0
                else:
                    resource_id_col = column_mapper.get_column_name(eligible_compute_df, "ResourceID")
                    unique_resources = eligible_compute_df[resource_id_col].n_unique()
                
                monthly_results.append({
                    "month": month_str,
                    "unique_resources": unique_resources,
                    "status": "✅" if unique_resources > 0 else "❌ No resources"
                })
                
                if unique_resources > total_unique_resources:
                    total_unique_resources = unique_resources
                    
            except Exception as e:
                monthly_results.append({
                    "month": month_str,
                    "unique_resources": 0,
                    "status": f"❌ Error: {str(e)[:30]}..."
                })
        
        # Determine service-resource mapping for this provider
        service_resource_mapping = {
            "AWS": "AmazonEC2 → instance",
            "Azure": "microsoft.compute → virtualmachines", 
            "GCP": "Compute Engine → instances"
        }
        mapping = service_resource_mapping.get(provider, "Unknown")
        
        # Add rows to table
        for result in monthly_results:
            table.add_row(
                result["month"],
                str(result["unique_resources"]),
                mapping,
                result["status"]
            )
        
        console.print(table)
        
        # Use new validation logic with fixed topology
        from ..core.allocator import DeterministicIdGenerator
        from ..config.models import MIN_TOPOLOGY_RESOURCE_CAPACITY, MAX_TOPOLOGY_RESOURCE_CAPACITY
        
        id_generator = DeterministicIdGenerator({"default", "kube-system", "kube-node-lease", "kube-public"})
        validation = id_generator.validate_k8s_resource_availability(total_unique_resources)
        
        # Display summary
        console.print(f"\n📈 [bold cyan]Summary:[/bold cyan]")
        console.print(f"• Maximum unique resources found: {total_unique_resources}")
        console.print(f"• Service-Resource mapping: {mapping}")
        
        # Show validation results
        if validation["feasible"]:
            console.print(f"\n✅ [bold green]K8s allocation ready[/bold green]")
            console.print(f"• Eligible resources: {validation['eligible_resources']}")
            console.print(f"• Target allocation ({MINIMUM_RESOURCES_REQUIRED:.0%}): {validation['target_resources']} resources")
            console.print(f"• Will allocate: {validation['actual_resources']} resources")
            
            if validation["capped_allocation"]:
                console.print(f"• Status: [yellow]Topology-limited[/yellow]")
                console.print(f"• Remaining: {validation['remaining_resources']} resources will stay as regular billing data")
            else:
                console.print(f"• Status: [green]Within optimal range ({MIN_TOPOLOGY_RESOURCE_CAPACITY}-{MAX_TOPOLOGY_RESOURCE_CAPACITY})[/green]")
                
            console.print(f"• Fixed topology: 5 clusters × 7 namespaces × {validation['workloads_per_namespace']} workloads = {validation['total_workloads']} workloads")
            console.print(f"• Avg resources per workload: {validation['avg_resources_per_workload']:.1f}")
            
        else:
            console.print(f"\n❌ [bold red]Insufficient resources for K8s allocation[/bold red]")
            console.print(f"• Found: {validation['eligible_resources']} eligible resources")
            console.print(f"• Target allocation ({MINIMUM_RESOURCES_REQUIRED:.0%}): {validation['target_resources']} resources")
            console.print(f"• Required minimum: {validation['min_required']} resources")
            console.print(f"• Deficit: {validation['min_required'] - validation['target_resources']} additional resources needed")
            console.print(f"\n💡 [yellow]Recommendation:[/yellow] Add more {mapping} resources or reduce MINIMUM_RESOURCES_REQUIRED")
        
    except typer.Exit:
        raise
    except Exception as e:
        console.print(f"❌ [bold red]Error analyzing resources:[/bold red] {str(e)}")
        raise typer.Exit(1)




if __name__ == "__main__":
    k8s_app()