"""
Deployment Monitor - Real-time CloudFormation deployment status tracking

This module provides detailed deployment status monitoring similar to AWS CDK,
showing real-time progress of CloudFormation stack operations with granular
event tracking and progress indicators.
"""

import time
import threading
from typing import Dict, List, Optional, Callable, Any
from datetime import datetime
from dataclasses import dataclass
from enum import Enum
import click

try:
    from mypy_boto3_cloudformation import CloudFormationClient
except ImportError:
    CloudFormationClient = Any


class EventStatus(Enum):
    """CloudFormation event status types"""
    CREATE_IN_PROGRESS = "CREATE_IN_PROGRESS"
    CREATE_COMPLETE = "CREATE_COMPLETE"
    CREATE_FAILED = "CREATE_FAILED"
    UPDATE_IN_PROGRESS = "UPDATE_IN_PROGRESS"
    UPDATE_COMPLETE = "UPDATE_COMPLETE"
    UPDATE_FAILED = "UPDATE_FAILED"
    DELETE_IN_PROGRESS = "DELETE_IN_PROGRESS"
    DELETE_COMPLETE = "DELETE_COMPLETE"
    DELETE_FAILED = "DELETE_FAILED"
    ROLLBACK_IN_PROGRESS = "ROLLBACK_IN_PROGRESS"
    ROLLBACK_COMPLETE = "ROLLBACK_COMPLETE"
    ROLLBACK_FAILED = "ROLLBACK_FAILED"


@dataclass
class DeploymentEvent:
    """Represents a CloudFormation deployment event"""
    timestamp: datetime
    resource_type: str
    logical_id: str
    status: EventStatus
    status_reason: Optional[str] = None
    resource_status_reason: Optional[str] = None
    
    @property
    def is_success(self) -> bool:
        """Check if this event represents a successful operation"""
        return self.status in [
            EventStatus.CREATE_COMPLETE,
            EventStatus.UPDATE_COMPLETE,
            EventStatus.DELETE_COMPLETE
        ]
    
    @property
    def is_failure(self) -> bool:
        """Check if this event represents a failed operation"""
        return self.status in [
            EventStatus.CREATE_FAILED,
            EventStatus.UPDATE_FAILED,
            EventStatus.DELETE_FAILED,
            EventStatus.ROLLBACK_FAILED
        ]
    
    @property
    def is_in_progress(self) -> bool:
        """Check if this event represents an in-progress operation"""
        return self.status in [
            EventStatus.CREATE_IN_PROGRESS,
            EventStatus.UPDATE_IN_PROGRESS,
            EventStatus.DELETE_IN_PROGRESS,
            EventStatus.ROLLBACK_IN_PROGRESS
        ]


class DeploymentMonitor:
    """Real-time CloudFormation deployment monitoring with detailed status updates"""
    
    def __init__(self, cloudformation_client: CloudFormationClient, stack_name: str):
        self.cf_client = cloudformation_client
        self.stack_name = stack_name
        self.events: List[DeploymentEvent] = []
        self.seen_event_ids: set = set()
        self.start_time = datetime.now()
        self.monitoring = False
        self.monitor_thread: Optional[threading.Thread] = None
        
    def start_monitoring(self, poll_interval: int = 5) -> None:
        """Start monitoring CloudFormation events in a separate thread"""
        if self.monitoring:
            return
            
        self.monitoring = True
        self.monitor_thread = threading.Thread(
            target=self._monitor_events,
            args=(poll_interval,),
            daemon=True
        )
        self.monitor_thread.start()
        
    def stop_monitoring(self) -> None:
        """Stop monitoring CloudFormation events"""
        self.monitoring = False
        if self.monitor_thread:
            self.monitor_thread.join(timeout=10)
    
    def _monitor_events(self, poll_interval: int) -> None:
        """Monitor CloudFormation events in a loop"""
        while self.monitoring:
            try:
                self._fetch_and_process_events()
                time.sleep(poll_interval)
            except Exception as e:
                click.echo(f"Error monitoring events: {e}", err=True)
                break
    
    def _fetch_and_process_events(self) -> None:
        """Fetch new CloudFormation events and process them"""
        try:
            response = self.cf_client.describe_stack_events(StackName=self.stack_name)
            new_events = []
            
            for event_data in response['StackEvents']:
                event_id = event_data['EventId']
                if event_id not in self.seen_event_ids:
                    self.seen_event_ids.add(event_id)
                    new_events.append(event_data)
            
            # Process events in chronological order (oldest first)
            for event_data in reversed(new_events):
                event = self._parse_event(event_data)
                if event:
                    self.events.append(event)
                    self._display_event(event)
                    
        except Exception as e:
            click.echo(f"Error fetching events: {e}", err=True)
    
    def _parse_event(self, event_data: Dict) -> Optional[DeploymentEvent]:
        """Parse CloudFormation event data into DeploymentEvent"""
        try:
            timestamp = event_data['Timestamp']
            resource_type = event_data.get('ResourceType', '')
            logical_id = event_data.get('LogicalResourceId', '')
            status_str = event_data.get('ResourceStatus', '')
            status_reason = event_data.get('StatusReason')
            resource_status_reason = event_data.get('ResourceStatusReason')
            
            # Parse status
            try:
                status = EventStatus(status_str)
            except ValueError:
                return None  # Skip unknown statuses
            
            return DeploymentEvent(
                timestamp=timestamp,
                resource_type=resource_type,
                logical_id=logical_id,
                status=status,
                status_reason=status_reason,
                resource_status_reason=resource_status_reason
            )
        except Exception:
            return None
    
    def _display_event(self, event: DeploymentEvent) -> None:
        """Display a deployment event with appropriate formatting"""
        # Skip stack-level events that aren't very useful
        if event.resource_type == 'AWS::CloudFormation::Stack':
            return
            
        # Format timestamp
        timestamp_str = event.timestamp.strftime("%H:%M:%S")
        
        # Determine color and symbol based on status
        if event.is_success:
            color = "green"
            symbol = "✅"
        elif event.is_failure:
            color = "red"
            symbol = "❌"
        elif event.is_in_progress:
            color = "yellow"
            symbol = "🔄"
        else:
            color = "white"
            symbol = "ℹ️"
        
        # Format the event message
        message = f"{symbol} {timestamp_str} {event.logical_id} ({event.resource_type})"
        
        if event.status_reason:
            message += f" - {event.status_reason}"
        elif event.resource_status_reason:
            message += f" - {event.resource_status_reason}"
        
        # Display with color
        click.echo(click.style(message, fg=color))
    
    def wait_for_completion(self, timeout_minutes: int = 30) -> bool:
        """Wait for stack operation to complete with detailed monitoring"""
        self.start_monitoring()
        
        try:
            # Use CloudFormation waiter but with our custom monitoring
            if self._is_update_operation():
                waiter = self.cf_client.get_waiter('stack_update_complete')
            else:
                waiter = self.cf_client.get_waiter('stack_create_complete')
            
            waiter.wait(
                StackName=self.stack_name,
                WaiterConfig={
                    'Delay': 10,
                    'MaxAttempts': timeout_minutes * 6  # 10 second intervals
                }
            )
            
            self._display_completion_summary()
            return True
            
        except Exception as e:
            click.echo(f"Deployment failed: {e}", err=True)
            self._display_failure_summary()
            return False
        finally:
            self.stop_monitoring()
    
    def _is_update_operation(self) -> bool:
        """Determine if this is an update operation"""
        try:
            response = self.cf_client.describe_stacks(StackName=self.stack_name)
            stack_status = response['Stacks'][0]['StackStatus']
            return 'UPDATE' in stack_status
        except:
            return False
    
    def _display_completion_summary(self) -> None:
        """Display a summary of the deployment completion"""
        duration = datetime.now() - self.start_time
        duration_str = str(duration).split('.')[0]  # Remove microseconds
        
        # Count events by status
        success_count = len([e for e in self.events if e.is_success])
        failure_count = len([e for e in self.events if e.is_failure])
        
        click.echo(f"\n🎉 Deployment completed in {duration_str}")
        click.echo(f"   ✅ {success_count} resources created/updated successfully")
        
        if failure_count > 0:
            click.echo(f"   ❌ {failure_count} resources failed")
    
    def _display_failure_summary(self) -> None:
        """Display a summary of deployment failures"""
        failure_events = [e for e in self.events if e.is_failure]
        
        if failure_events:
            click.echo(f"\n❌ Deployment failed with {len(failure_events)} resource failures:")
            for event in failure_events[-5:]:  # Show last 5 failures
                click.echo(f"   • {event.logical_id}: {event.status_reason or event.resource_status_reason}")


def monitor_deployment(cloudformation_client: CloudFormationClient, 
                      stack_name: str, 
                      timeout_minutes: int = 30) -> bool:
    """
    Monitor a CloudFormation deployment with detailed real-time status updates.
    
    Args:
        cloudformation_client: Boto3 CloudFormation client
        stack_name: Name of the CloudFormation stack
        timeout_minutes: Maximum time to wait for completion
        
    Returns:
        True if deployment succeeded, False otherwise
    """
    monitor = DeploymentMonitor(cloudformation_client, stack_name)
    return monitor.wait_for_completion(timeout_minutes)
