"""
CloudZero API client for billing connection management.

Based on patterns from cz-demo-tools with enhanced error handling
and integration with czbillgen architecture.
"""

import os
import requests
from typing import Dict, List, Optional, Any
from dataclasses import dataclass
import typer
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type


@dataclass
class CloudZeroConnection:
    """Represents a CloudZero billing connection"""
    name: str
    cloud_provider: str
    s3_bucket_name: str
    s3_bucket_path: str
    role_arn: str
    external_id: str
    is_enabled: bool = True
    connection_id: Optional[str] = None


class CloudZeroAPIError(Exception):
    """Custom exception for CloudZero API errors"""
    pass


class CloudZeroAPI:
    """
    CloudZero API client for billing connection management.
    
    Handles creation, retrieval, and deletion of billing connections
    for S3-based data ingestion, following patterns from cz-demo-tools.
    """
    
    def __init__(self, api_key: str, base_url: str = "https://api.cloudzero.com"):
        """
        Initialize CloudZero API client.
        
        Args:
            api_key: CloudZero organization API key
            base_url: CloudZero API base URL
        """
        self.api_key = api_key
        self.base_url = base_url
        self.session = requests.Session()
        self.session.headers.update({
            "accept": "application/json",
            "content-type": "application/json",
            "Authorization": api_key
        })
    
    def _make_request(self, method: str, endpoint: str, **kwargs) -> requests.Response:
        """
        Make HTTP request to CloudZero API.
        
        Args:
            method: HTTP method (GET, POST, DELETE, etc.)
            endpoint: API endpoint path
            **kwargs: Additional arguments for requests
            
        Returns:
            Response object
            
        Raises:
            CloudZeroAPIError: If request fails
        """
        url = f"{self.base_url}{endpoint}"
        
        try:
            response = self.session.request(method, url, **kwargs)
            response.raise_for_status()
            return response
        except requests.exceptions.RequestException as e:
            if hasattr(e, 'response') and e.response is not None:
                error_msg = f"CloudZero API error: {e.response.status_code} - {e.response.text}"
            else:
                error_msg = f"CloudZero API request failed: {str(e)}"
            raise CloudZeroAPIError(error_msg)
    
    @retry(
        stop=stop_after_attempt(3),
        wait=wait_exponential(multiplier=1, min=4, max=10),
        retry=retry_if_exception_type(requests.exceptions.RequestException),
        reraise=True
    )
    def create_billing_connection(self, connection: CloudZeroConnection) -> Dict[str, Any]:
        """
        Create a new billing connection in CloudZero.
        
        Args:
            connection: CloudZeroConnection object with connection details
            
        Returns:
            Dictionary containing the created connection details
        """
        payload = {
            "s3_bucket_info": {
                "bucket_name": connection.s3_bucket_name,
                "bucket_path": connection.s3_bucket_path,
                "role_arn": connection.role_arn,
                "external_id": connection.external_id
            },
            "is_enabled": connection.is_enabled,
            "name": connection.name,
            "cloud_provider": connection.cloud_provider
        }
        
        typer.echo(f"    🔗 Creating billing connection: {connection.name}")
        
        response = self._make_request("POST", "/v2/connections/billing", json=payload)
        result = response.json()
        
        typer.echo(f"    ✅ Connection created: {connection.name}")
        return result
    
    def get_billing_connection(self, connection_name: str) -> Optional[Dict[str, Any]]:
        """
        Get a single billing connection by name.
        
        Args:
            connection_name: Name of the connection to retrieve
            
        Returns:
            Dictionary containing connection details, or None if not found
        """
        try:
            connections = self.list_billing_connections()
            for connection in connections:
                if connection.get("name") == connection_name:
                    return connection
            return None
        except CloudZeroAPIError:
            return None
    
    def list_billing_connections(self) -> List[Dict[str, Any]]:
        """
        List all billing connections.
        
        Returns:
            List of dictionaries containing connection details
        """
        response = self._make_request("GET", "/v2/connections/billing")
        result = response.json()
        
        # Handle different response formats
        if isinstance(result, list):
            return result
        elif isinstance(result, dict) and "connections" in result:
            return result["connections"]
        else:
            return []
    
    def delete_billing_connection(self, connection_id: str) -> bool:
        """
        Delete a billing connection.
        
        For tier1 connections, this disables the connection (sets is_enabled=False)
        since actual deletion is not supported by the CloudZero API.
        
        Args:
            connection_id: ID of the connection to delete/disable
            
        Returns:
            True if deletion/disabling was successful
        """
        typer.echo(f"    🗑️  Deleting billing connection: {connection_id}")
        
        # Try DELETE request first (for non-tier1 connections)
        try:
            response = self._make_request("DELETE", f"/v2/connections/billing/{connection_id}")
            typer.echo(f"    ✅ Connection deleted: {connection_id}")
            return response.status_code == 204 or response.status_code == 200
        except CloudZeroAPIError as e:
            # If DELETE fails due to tier1 connection, try PATCH to disable
            if "Invalid fields for tier1 connection" in str(e):
                typer.echo(f"    ⚠️  Tier1 connection detected, disabling instead of deleting")
                try:
                    payload = {"is_enabled": False}
                    response = self._make_request("PATCH", f"/v2/connections/billing/{connection_id}", json=payload)
                    typer.echo(f"    ✅ Connection disabled: {connection_id}")
                    return response.status_code == 200
                except CloudZeroAPIError as patch_error:
                    typer.echo(f"    ❌ Failed to disable connection: {patch_error}")
                    raise patch_error
            else:
                typer.echo(f"    ❌ Failed to delete connection: {e}")
                raise e
    
    def get_organization_info(self) -> Dict[str, Any]:
        """
        Get organization information from the API.
        
        Returns:
            Dictionary containing organization context
        """
        connections = self.list_billing_connections()
        
        if not connections:
            return {
                "org_id": "Unknown",
                "connection_count": 0,
                "enabled_count": 0,
                "providers": {},
                "api_key_masked": f"{self.api_key[:8]}...{self.api_key[-4:]}" if len(self.api_key) > 12 else "***"
            }
        
        # Extract organization ID from connection capabilities path or S3 bucket path
        org_id = "Unknown"
        try:
            # Try extracting from capabilities path first
            path = connections[0]["capabilities"]["owner"]["path"]
            org_id = path.split("organizations/")[1].split("/")[0]
        except (KeyError, IndexError):
            # Fallback: try extracting from S3 bucket path (e.g., "piedpiper-custom-billing/DemoTest/AWS")
            try:
                s3_path = connections[0]["s3_bucket_info"]["bucket_path"]
                path_parts = s3_path.split("/")
                if len(path_parts) >= 2:
                    org_id = path_parts[1]  # Second part is typically the organization name
            except (KeyError, IndexError):
                org_id = "Unknown"
        
        # Count connections by provider
        provider_counts = {}
        enabled_count = 0
        
        for conn in connections:
            provider = conn.get("cloud_provider", "Unknown")
            provider_counts[provider] = provider_counts.get(provider, 0) + 1
            if conn.get("is_enabled", True):
                enabled_count += 1
        
        return {
            "org_id": org_id,
            "connection_count": len(connections),
            "enabled_count": enabled_count,
            "providers": provider_counts,
            "api_key_masked": f"{self.api_key[:8]}...{self.api_key[-4:]}" if len(self.api_key) > 12 else "***"
        }
    
    def print_organization_context(self) -> None:
        """
        Print organization context information to the user.
        """
        info = self.get_organization_info()
        
        typer.echo(f"🏢 CloudZero Organization: {info['org_id']}")
        typer.echo(f"🔑 API Key: {info['api_key_masked']}")
        typer.echo(f"📊 Total connections: {info['connection_count']} ({info['enabled_count']} enabled)")
        
        if info['providers']:
            typer.echo("📋 Provider breakdown:")
            for provider, count in sorted(info['providers'].items()):
                typer.echo(f"   • {provider}: {count} connections")

    def find_enabled_bucket_connection(self, provider: str) -> Optional[Dict[str, Any]]:
        """
        Find an enabled bucket connection for a provider.
        
        Args:
            provider: Provider name (AWS, Azure, GCP)
            
        Returns:
            Connection dictionary if found, None otherwise
        """
        from .connection_manager import generate_provider_connection_name
        
        connection_name = generate_provider_connection_name(provider)
        connections = self.list_billing_connections()
        
        for conn in connections:
            if (conn.get("name") == connection_name and 
                conn.get("is_enabled", True)):
                return conn
        
        return None
    
    def ensure_provider_bucket_connections(self, providers: List[str], 
                                         s3_bucket_name: str,
                                         s3_root_folder: str,
                                         s3_org_folder: str,
                                         role_arn: str,
                                         external_id: str) -> Dict[str, Dict[str, Any]]:
        """
        Ensure bucket connections exist for all specified providers.
        
        Args:
            providers: List of provider names (AWS, Azure, GCP)
            s3_bucket_name: S3 bucket name
            s3_root_folder: S3 root folder
            s3_org_folder: S3 organization folder
            role_arn: AWS IAM role ARN
            external_id: AWS IAM external ID
            
        Returns:
            Dictionary mapping provider names to their connection details
        """
        from .connection_manager import create_cloudzero_connection
        
        results = {}
        
        for provider in providers:
            typer.echo(f"🔍 Checking {provider} bucket connection...")
            
            # Check if enabled connection exists
            existing_connection = self.find_enabled_bucket_connection(provider)
            
            if existing_connection:
                typer.echo(f"    ✅ {provider} connection already exists: {existing_connection['name']}")
                results[provider] = existing_connection
            else:
                typer.echo(f"    🔗 Creating {provider} bucket connection...")
                
                # Create new connection
                connection = create_cloudzero_connection(
                    provider=provider,
                    s3_bucket_name=s3_bucket_name,
                    s3_root_folder=s3_root_folder,
                    s3_org_folder=s3_org_folder,
                    role_arn=role_arn,
                    external_id=external_id
                )
                
                result = self.create_billing_connection(connection)
                results[provider] = result
                typer.echo(f"    ✅ {provider} connection created: {connection.name}")
        
        return results

    def ensure_billing_connection(self, connection: CloudZeroConnection) -> Dict[str, Any]:
        """
        Ensure billing connection exists, creating it if necessary.
        
        Args:
            connection: CloudZeroConnection object with connection details
            
        Returns:
            Dictionary containing the connection details
        """
        # Check if connection already exists
        existing_connection = self.get_billing_connection(connection.name)
        
        if existing_connection:
            typer.echo(f"    ✅ Connection already exists: {connection.name}")
            return existing_connection
        
        # Create new connection
        return self.create_billing_connection(connection)


def test_cloudzero_connection(api_key: str) -> bool:
    """
    Test CloudZero API connection.
    
    Args:
        api_key: CloudZero API key
        
    Returns:
        True if connection is successful
    """
    try:
        api = CloudZeroAPI(api_key)
        connections = api.list_billing_connections()
        typer.echo(f"✅ CloudZero API connection successful ({len(connections)} connections found)")
        return True
    except CloudZeroAPIError as e:
        typer.echo(f"❌ CloudZero API connection failed: {e}")
        return False