"""
GCP cost optimization analyzer.
Provides cost-saving recommendations for GCP resources.
"""

import argparse
import json
import sys
from typing import Dict, List, Any


class CostOptimizer:
    """Analyze GCP costs and provide optimization recommendations."""

    def __init__(self, current_resources: Dict[str, Any], monthly_spend: float):
        """
        Initialize with current GCP resources and spending.

        Args:
            current_resources: Dictionary of current GCP resources
            monthly_spend: Current monthly GCP spend in USD
        """
        self.resources = current_resources
        self.monthly_spend = monthly_spend
        self.recommendations = []

    def analyze_and_optimize(self) -> Dict[str, Any]:
        """
        Analyze current setup and generate cost optimization recommendations.

        Returns:
            Dictionary with recommendations and potential savings
        """
        self.recommendations = []
        potential_savings = 0.0

        compute_savings = self._analyze_compute()
        potential_savings += compute_savings

        storage_savings = self._analyze_storage()
        potential_savings += storage_savings

        database_savings = self._analyze_database()
        potential_savings += database_savings

        network_savings = self._analyze_networking()
        potential_savings += network_savings

        general_savings = self._analyze_general_optimizations()
        potential_savings += general_savings

        return {
            'current_monthly_spend': self.monthly_spend,
            'potential_monthly_savings': round(potential_savings, 2),
            'optimized_monthly_spend': round(self.monthly_spend - potential_savings, 2),
            'savings_percentage': round((potential_savings / self.monthly_spend) * 100, 2) if self.monthly_spend > 0 else 0,
            'recommendations': self.recommendations,
            'priority_actions': self._prioritize_recommendations()
        }

    def _analyze_compute(self) -> float:
        """Analyze compute resources (GCE, GKE, Cloud Run)."""
        savings = 0.0

        gce_instances = self.resources.get('gce_instances', [])
        if gce_instances:
            idle_count = sum(1 for inst in gce_instances if inst.get('cpu_utilization', 100) < 10)
            if idle_count > 0:
                idle_cost = idle_count * 50
                savings += idle_cost
                self.recommendations.append({
                    'service': 'Compute Engine',
                    'type': 'Idle Resources',
                    'issue': f'{idle_count} GCE instances with <10% CPU utilization',
                    'recommendation': 'Stop or delete idle instances, or downsize to smaller machine types',
                    'potential_savings': idle_cost,
                    'priority': 'high'
                })

            # Check for committed use discounts
            on_demand_count = sum(1 for inst in gce_instances if inst.get('pricing', 'on-demand') == 'on-demand')
            if on_demand_count >= 2:
                cud_savings = on_demand_count * 50 * 0.37  # 37% savings with 1-yr CUD
                savings += cud_savings
                self.recommendations.append({
                    'service': 'Compute Engine',
                    'type': 'Committed Use Discounts',
                    'issue': f'{on_demand_count} instances on on-demand pricing',
                    'recommendation': 'Purchase 1-year committed use discounts for predictable workloads (37% savings) or 3-year (55% savings)',
                    'potential_savings': cud_savings,
                    'priority': 'medium'
                })

            # Check for sustained use discounts awareness
            short_lived = sum(1 for inst in gce_instances if inst.get('uptime_hours_month', 730) < 200)
            if short_lived > 0:
                self.recommendations.append({
                    'service': 'Compute Engine',
                    'type': 'Scheduling',
                    'issue': f'{short_lived} instances running <200 hours/month',
                    'recommendation': 'Use Instance Scheduler to stop dev/test instances outside business hours',
                    'potential_savings': short_lived * 20,
                    'priority': 'medium'
                })
                savings += short_lived * 20

        # GKE optimization
        gke_clusters = self.resources.get('gke_clusters', [])
        for cluster in gke_clusters:
            if cluster.get('mode', 'standard') == 'standard':
                node_utilization = cluster.get('avg_node_utilization', 100)
                if node_utilization < 40:
                    autopilot_savings = cluster.get('monthly_cost', 500) * 0.30
                    savings += autopilot_savings
                    self.recommendations.append({
                        'service': 'GKE',
                        'type': 'Cluster Mode',
                        'issue': f'Standard GKE cluster with <40% node utilization',
                        'recommendation': 'Migrate to GKE Autopilot to pay only for pod resources, or enable cluster autoscaler',
                        'potential_savings': autopilot_savings,
                        'priority': 'high'
                    })

        # Cloud Run optimization
        cloud_run_services = self.resources.get('cloud_run_services', [])
        for svc in cloud_run_services:
            if svc.get('min_instances', 0) > 0 and svc.get('avg_rps', 100) < 1:
                min_inst_savings = svc.get('min_instances', 1) * 15
                savings += min_inst_savings
                self.recommendations.append({
                    'service': 'Cloud Run',
                    'type': 'Min Instances',
                    'issue': f'Service {svc.get("name", "unknown")} has min instances but very low traffic',
                    'recommendation': 'Set min-instances to 0 for low-traffic services to enable scale-to-zero',
                    'potential_savings': min_inst_savings,
                    'priority': 'medium'
                })

        return savings

    def _analyze_storage(self) -> float:
        """Analyze Cloud Storage resources."""
        savings = 0.0

        gcs_buckets = self.resources.get('gcs_buckets', [])
        for bucket in gcs_buckets:
            size_gb = bucket.get('size_gb', 0)
            storage_class = bucket.get('storage_class', 'STANDARD')

            if not bucket.get('has_lifecycle_policy', False) and size_gb > 100:
                lifecycle_savings = size_gb * 0.012
                savings += lifecycle_savings
                self.recommendations.append({
                    'service': 'Cloud Storage',
                    'type': 'Lifecycle Policy',
                    'issue': f'Bucket {bucket.get("name", "unknown")} ({size_gb} GB) has no lifecycle policy',
                    'recommendation': 'Add lifecycle rule: Transition to Nearline after 30 days, Coldline after 90 days, Archive after 365 days',
                    'potential_savings': lifecycle_savings,
                    'priority': 'medium'
                })

            if storage_class == 'STANDARD' and size_gb > 500:
                class_savings = size_gb * 0.006
                savings += class_savings
                self.recommendations.append({
                    'service': 'Cloud Storage',
                    'type': 'Storage Class',
                    'issue': f'Large bucket ({size_gb} GB) using Standard class',
                    'recommendation': 'Enable Autoclass for automatic storage class management based on access patterns',
                    'potential_savings': class_savings,
                    'priority': 'high'
                })

        return savings

    def _analyze_database(self) -> float:
        """Analyze Cloud SQL, Firestore, and BigQuery costs."""
        savings = 0.0

        cloud_sql_instances = self.resources.get('cloud_sql_instances', [])
        for db in cloud_sql_instances:
            if db.get('connections_per_day', 1000) < 10:
                db_cost = db.get('monthly_cost', 100)
                savings += db_cost * 0.8
                self.recommendations.append({
                    'service': 'Cloud SQL',
                    'type': 'Idle Resource',
                    'issue': f'Database {db.get("name", "unknown")} has <10 connections/day',
                    'recommendation': 'Stop database if not needed, or take a backup and delete',
                    'potential_savings': db_cost * 0.8,
                    'priority': 'high'
                })

            if db.get('utilization', 100) < 30 and not db.get('has_ha', False):
                rightsize_savings = db.get('monthly_cost', 200) * 0.35
                savings += rightsize_savings
                self.recommendations.append({
                    'service': 'Cloud SQL',
                    'type': 'Right-sizing',
                    'issue': f'Cloud SQL instance {db.get("name", "unknown")} has low utilization (<30%)',
                    'recommendation': 'Downsize to a smaller machine type (e.g., db-custom-2-8192 to db-f1-micro for dev)',
                    'potential_savings': rightsize_savings,
                    'priority': 'medium'
                })

        # BigQuery optimization
        bigquery_datasets = self.resources.get('bigquery_datasets', [])
        for dataset in bigquery_datasets:
            if dataset.get('pricing_model', 'on_demand') == 'on_demand':
                monthly_tb_scanned = dataset.get('monthly_tb_scanned', 0)
                if monthly_tb_scanned > 10:
                    slot_savings = (monthly_tb_scanned * 6.25) * 0.30
                    savings += slot_savings
                    self.recommendations.append({
                        'service': 'BigQuery',
                        'type': 'Pricing Model',
                        'issue': f'Scanning {monthly_tb_scanned} TB/month on on-demand pricing',
                        'recommendation': 'Switch to BigQuery editions with slots for predictable costs (30%+ savings at this volume)',
                        'potential_savings': slot_savings,
                        'priority': 'high'
                    })

            if not dataset.get('has_partitioning', False):
                partition_savings = dataset.get('monthly_query_cost', 50) * 0.50
                savings += partition_savings
                self.recommendations.append({
                    'service': 'BigQuery',
                    'type': 'Table Partitioning',
                    'issue': f'Tables in {dataset.get("name", "unknown")} lack partitioning',
                    'recommendation': 'Partition tables by date and add clustering columns to reduce bytes scanned',
                    'potential_savings': partition_savings,
                    'priority': 'medium'
                })

        return savings

    def _analyze_networking(self) -> float:
        """Analyze networking costs (egress, Cloud NAT, etc.)."""
        savings = 0.0

        cloud_nat_gateways = self.resources.get('cloud_nat_gateways', [])
        if len(cloud_nat_gateways) > 1:
            extra_nats = len(cloud_nat_gateways) - 1
            nat_savings = extra_nats * 45
            savings += nat_savings
            self.recommendations.append({
                'service': 'Cloud NAT',
                'type': 'Resource Consolidation',
                'issue': f'{len(cloud_nat_gateways)} Cloud NAT gateways deployed',
                'recommendation': 'Consolidate NAT gateways in dev/staging, or use Private Google Access for GCP services',
                'potential_savings': nat_savings,
                'priority': 'high'
            })

        egress_gb = self.resources.get('monthly_egress_gb', 0)
        if egress_gb > 1000:
            cdn_savings = egress_gb * 0.04  # CDN is cheaper than direct egress
            savings += cdn_savings
            self.recommendations.append({
                'service': 'Networking',
                'type': 'CDN Optimization',
                'issue': f'High egress volume ({egress_gb} GB/month)',
                'recommendation': 'Enable Cloud CDN to serve cached content at lower egress rates',
                'potential_savings': cdn_savings,
                'priority': 'medium'
            })

        return savings

    def _analyze_general_optimizations(self) -> float:
        """General GCP cost optimizations."""
        savings = 0.0

        # Log retention
        log_sinks = self.resources.get('log_sinks', [])
        if not log_sinks:
            log_volume_gb = self.resources.get('monthly_log_volume_gb', 0)
            if log_volume_gb > 50:
                log_savings = log_volume_gb * 0.50 * 0.6
                savings += log_savings
                self.recommendations.append({
                    'service': 'Cloud Logging',
                    'type': 'Log Exclusion',
                    'issue': f'{log_volume_gb} GB/month of logs without exclusion filters',
                    'recommendation': 'Create log exclusion filters for verbose/debug logs and route remaining to Cloud Storage via log sinks',
                    'potential_savings': log_savings,
                    'priority': 'medium'
                })

        # Unattached persistent disks
        persistent_disks = self.resources.get('persistent_disks', [])
        unattached = sum(1 for disk in persistent_disks if not disk.get('attached', True))
        if unattached > 0:
            disk_savings = unattached * 10  # ~$10/month per 100 GB disk
            savings += disk_savings
            self.recommendations.append({
                'service': 'Compute Engine',
                'type': 'Unused Resources',
                'issue': f'{unattached} unattached persistent disks',
                'recommendation': 'Snapshot and delete unused persistent disks',
                'potential_savings': disk_savings,
                'priority': 'high'
            })

        # Static external IPs
        static_ips = self.resources.get('static_ips', [])
        unused_ips = sum(1 for ip in static_ips if not ip.get('in_use', True))
        if unused_ips > 0:
            ip_savings = unused_ips * 7.30  # $0.01/hour = $7.30/month
            savings += ip_savings
            self.recommendations.append({
                'service': 'Networking',
                'type': 'Unused Resources',
                'issue': f'{unused_ips} unused static external IP addresses',
                'recommendation': 'Release unused static IPs to avoid hourly charges',
                'potential_savings': ip_savings,
                'priority': 'high'
            })

        # Budget alerts
        if not self.resources.get('has_budget_alerts', False):
            self.recommendations.append({
                'service': 'Cloud Billing',
                'type': 'Cost Monitoring',
                'issue': 'No budget alerts configured',
                'recommendation': 'Set up Cloud Billing budgets with alerts at 50%, 80%, 100% of monthly budget',
                'potential_savings': 0,
                'priority': 'high'
            })

        # Recommender API
        if not self.resources.get('uses_recommender', False):
            self.recommendations.append({
                'service': 'Active Assist',
                'type': 'Visibility',
                'issue': 'GCP Recommender not reviewed',
                'recommendation': 'Review Active Assist recommendations for right-sizing, idle resources, and committed use discounts',
                'potential_savings': 0,
                'priority': 'medium'
            })

        return savings

    def _prioritize_recommendations(self) -> List[Dict[str, Any]]:
        """Get top priority recommendations."""
        high_priority = [r for r in self.recommendations if r['priority'] == 'high']
        high_priority.sort(key=lambda x: x.get('potential_savings', 0), reverse=True)
        return high_priority[:5]

    def generate_optimization_checklist(self) -> List[Dict[str, Any]]:
        """Generate actionable checklist for cost optimization."""
        return [
            {
                'category': 'Immediate Actions (Today)',
                'items': [
                    'Release unused static IPs',
                    'Delete unattached persistent disks',
                    'Stop idle Compute Engine instances',
                    'Set up billing budget alerts'
                ]
            },
            {
                'category': 'This Week',
                'items': [
                    'Add Cloud Storage lifecycle policies',
                    'Create log exclusion filters for verbose logs',
                    'Right-size Cloud SQL instances',
                    'Review Active Assist recommendations'
                ]
            },
            {
                'category': 'This Month',
                'items': [
                    'Evaluate committed use discounts',
                    'Migrate GKE Standard to Autopilot where applicable',
                    'Partition and cluster BigQuery tables',
                    'Enable Cloud CDN for high-egress services'
                ]
            },
            {
                'category': 'Ongoing',
                'items': [
                    'Review billing reports weekly',
                    'Label all resources for cost allocation',
                    'Monitor Active Assist recommendations monthly',
                    'Conduct quarterly cost optimization reviews'
                ]
            }
        ]


def main():
    parser = argparse.ArgumentParser(
        description='GCP Cost Optimizer - Analyzes GCP resources and recommends cost savings'
    )
    parser.add_argument(
        '--resources', '-r',
        type=str,
        help='Path to JSON file with current GCP resource inventory'
    )
    parser.add_argument(
        '--monthly-spend', '-s',
        type=float,
        default=1000,
        help='Current monthly GCP spend in USD (default: 1000)'
    )
    parser.add_argument(
        '--output', '-o',
        type=str,
        help='Path to write optimization report JSON'
    )
    parser.add_argument(
        '--json',
        action='store_true',
        help='Output as JSON format'
    )
    parser.add_argument(
        '--checklist',
        action='store_true',
        help='Generate optimization checklist'
    )

    args = parser.parse_args()

    if args.resources:
        try:
            with open(args.resources, 'r') as f:
                resources = json.load(f)
        except FileNotFoundError:
            print(f"Error: File '{args.resources}' not found.", file=sys.stderr)
            sys.exit(1)
        except json.JSONDecodeError:
            print(f"Error: File '{args.resources}' is not valid JSON.", file=sys.stderr)
            sys.exit(1)
    else:
        resources = {}

    optimizer = CostOptimizer(resources, args.monthly_spend)
    result = optimizer.analyze_and_optimize()

    if args.checklist:
        result['checklist'] = optimizer.generate_optimization_checklist()

    if args.output:
        with open(args.output, 'w') as f:
            json.dump(result, f, indent=2)
        print(f"Report written to {args.output}")
    elif args.json:
        print(json.dumps(result, indent=2))
    else:
        print(f"\nGCP Cost Optimization Report")
        print(f"{'=' * 40}")
        print(f"Current Monthly Spend: ${result['current_monthly_spend']:.2f}")
        print(f"Potential Savings:     ${result['potential_monthly_savings']:.2f}")
        print(f"Optimized Spend:       ${result['optimized_monthly_spend']:.2f}")
        print(f"Savings Percentage:    {result['savings_percentage']}%")
        print(f"\nTop Priority Actions:")
        for i, action in enumerate(result['priority_actions'], 1):
            print(f"  {i}. [{action['service']}] {action['recommendation']}")
            print(f"     Savings: ${action['potential_savings']:.2f}/month")
        print(f"\nTotal Recommendations: {len(result['recommendations'])}")


if __name__ == '__main__':
    main()
