import json
from typing import Any, Dict, Optional
from jazzmine.logging.sinks.base_sink import BaseSink
from jazzmine.logging.exceptions import LoggerDependencyError


class CloudWatchLogger(BaseSink):
    """AWS CloudWatch Logs sink"""
    
    def initialize(self) -> None:
        try:
            import boto3
            self.boto3 = boto3
        except ImportError:
            raise LoggerDependencyError(
                "boto3 is required for CloudWatch sink. Install: pip install boto3"
            )
        
        self.log_group = self.sink_config.get('log_group', f'/logs/{self.logger_name}')
        self.log_stream = self.sink_config.get('log_stream', 'default')
        self.region = self.sink_config.get('region', 'us-east-1')
        
        self.client = self.boto3.client('logs', region_name=self.region)
        self.sequence_token: Optional[str] = None
        
        # Ensure log group and stream exist
        try:
            self.client.create_log_group(logGroupName=self.log_group)
        except self.client.exceptions.ResourceAlreadyExistsException:
            pass
        
        try:
            self.client.create_log_stream(
                logGroupName=self.log_group,
                logStreamName=self.log_stream
            )
        except self.client.exceptions.ResourceAlreadyExistsException:
            pass
        
        self._initialized = True
    
    def shutdown(self) -> None:
        pass
    
    def _send_log(self, level: str, msg: str, **kwargs) -> None:
        import time
        
        log_event = {
            'timestamp': int(time.time() * 1000),
            'message': json.dumps({
                'level': level,
                'message': msg,
                'logger': self.logger_name,
                **kwargs
            })
        }
        
        try:
            params = {
                'logGroupName': self.log_group,
                'logStreamName': self.log_stream,
                'logEvents': [log_event]
            }
            
            if self.sequence_token:
                params['sequenceToken'] = self.sequence_token
            
            response = self.client.put_log_events(**params)
            self.sequence_token = response.get('nextSequenceToken')
        except Exception:
            pass
    
    def log(self, level: str, msg: str, **kwargs) -> None:
        if not self._initialized:
            return
        self._send_log(level, msg, **kwargs)
    
    async def alog(self, level: str, msg: str, **kwargs) -> None:
        # CloudWatch SDK is sync, use it directly
        self.log(level, msg, **kwargs)


class PubSubLogger(BaseSink):
    """Google Cloud Pub/Sub sink"""
    
    def initialize(self) -> None:
        try:
            from google.cloud import pubsub_v1
            self.pubsub = pubsub_v1
        except ImportError:
            raise LoggerDependencyError(
                "google-cloud-pubsub is required for Pub/Sub sink. Install: pip install google-cloud-pubsub"
            )
        
        self.project_id = self.sink_config.get('project_id')
        self.topic_name = self.sink_config.get('topic', f'logs-{self.logger_name}')
        
        self.publisher = self.pubsub.PublisherClient()
        self.topic_path = self.publisher.topic_path(self.project_id, self.topic_name)
        
        self._initialized = True
    
    def shutdown(self) -> None:
        if self._initialized:
            self.publisher.stop()
    
    def _format_message(self, level: str, msg: str, **kwargs) -> bytes:
        import time
        payload = {
            'timestamp': time.time(),
            'level': level,
            'logger': self.logger_name,
            'message': msg,
            **kwargs
        }
        return json.dumps(payload).encode('utf-8')
    
    def log(self, level: str, msg: str, **kwargs) -> None:
        if not self._initialized:
            return
        
        data = self._format_message(level, msg, **kwargs)
        
        try:
            self.publisher.publish(self.topic_path, data)
        except Exception:
            pass
    
    async def alog(self, level: str, msg: str, **kwargs) -> None:
        # Pub/Sub client handles async internally
        self.log(level, msg, **kwargs)
