import json
import asyncio
from typing import Any, Dict
import aiohttp
import requests
from jazzmine.logging.sinks.base_sink import BaseSink
from jazzmine.logging.exceptions import LoggerDependencyError


class DatadogLogger(BaseSink):
    """Datadog HTTP API sink"""
    
    def initialize(self) -> None:
        self.api_key = self.sink_config.get('api_key')
        self.site = self.sink_config.get('site', 'datadoghq.com')
        self.service = self.sink_config.get('service', self.logger_name)
        # Allow URL override for testing, otherwise build from site
        self.url = self.sink_config.get('url') or f"https://http-intake.logs.{self.site}/api/v2/logs"
        self.session = None
        self._initialized = True
    
    def shutdown(self) -> None:
        if self.session:
            asyncio.create_task(self.session.close())
    
    def log(self, level: str, msg: str, **kwargs) -> None:
        if not self._initialized:
            return
        
        payload = {
            'ddsource': 'python',
            'service': self.service,
            'message': msg,
            'level': level.upper(),
            **kwargs
        }
        
        headers = {
            'DD-API-KEY': self.api_key,
            'Content-Type': 'application/json'
        }
        
        try:
            requests.post(self.url, json=payload, headers=headers, timeout=5)
        except Exception:
            pass
    
    async def alog(self, level: str, msg: str, **kwargs) -> None:
        if not self._initialized:
            return
        
        if not self.session:
            self.session = aiohttp.ClientSession()
        
        payload = {
            'ddsource': 'python',
            'service': self.service,
            'message': msg,
            'level': level.upper(),
            **kwargs
        }
        
        headers = {
            'DD-API-KEY': self.api_key,
            'Content-Type': 'application/json'
        }
        
        try:
            await self.session.post(self.url, json=payload, headers=headers, timeout=aiohttp.ClientTimeout(total=5))
        except Exception:
            pass


class LokiLogger(BaseSink):
    """Grafana Loki sink"""
    
    def initialize(self) -> None:
        self.url = self.sink_config.get('url', 'http://localhost:3100/loki/api/v1/push')
        self.labels = self.sink_config.get('labels', {'job': self.logger_name})
        self.session = None
        self._initialized = True
    
    def shutdown(self) -> None:
        if self.session:
            asyncio.create_task(self.session.close())
    
    def _format_payload(self, level: str, msg: str, **kwargs) -> Dict[str, Any]:
        import time
        labels = {**self.labels, 'level': level}
        label_str = ','.join(f'{k}="{v}"' for k, v in labels.items())
        
        log_line = msg
        if kwargs:
            log_line = f"{msg} {json.dumps(kwargs)}"
        
        return {
            'streams': [{
                'stream': labels,
                'values': [[str(int(time.time() * 1e9)), log_line]]
            }]
        }
    
    def log(self, level: str, msg: str, **kwargs) -> None:
        if not self._initialized:
            return
        
        payload = self._format_payload(level, msg, **kwargs)
        
        try:
            requests.post(self.url, json=payload, timeout=5)
        except Exception:
            pass
    
    async def alog(self, level: str, msg: str, **kwargs) -> None:
        if not self._initialized:
            return
        
        if not self.session:
            self.session = aiohttp.ClientSession()
        
        payload = self._format_payload(level, msg, **kwargs)
        
        try:
            await self.session.post(self.url, json=payload, timeout=aiohttp.ClientTimeout(total=5))
        except Exception:
            pass


class NewRelicLogger(BaseSink):
    """New Relic Logs API sink"""
    
    def initialize(self) -> None:
        self.license_key = self.sink_config.get('license_key')
        self.region = self.sink_config.get('region', 'us')
        # Allow URL override for testing, otherwise build from region
        if 'url' in self.sink_config:
            self.url = self.sink_config['url']
        else:
            base_url = 'https://log-api.newrelic.com' if self.region == 'us' else 'https://log-api.eu.newrelic.com'
            self.url = f"{base_url}/log/v1"
        self.session = None
        self._initialized = True
    
    def shutdown(self) -> None:
        if self.session:
            asyncio.create_task(self.session.close())
    
    def _format_payload(self, level: str, msg: str, **kwargs) -> Dict[str, Any]:
        return [{
            'message': msg,
            'level': level,
            'service': self.logger_name,
            **kwargs
        }]
    
    def log(self, level: str, msg: str, **kwargs) -> None:
        if not self._initialized:
            return
        
        payload = self._format_payload(level, msg, **kwargs)
        headers = {
            'X-License-Key': self.license_key,
            'Content-Type': 'application/json'
        }
        
        try:
            requests.post(self.url, json=payload, headers=headers, timeout=5)
        except Exception:
            pass
    
    async def alog(self, level: str, msg: str, **kwargs) -> None:
        if not self._initialized:
            return
        
        if not self.session:
            self.session = aiohttp.ClientSession()
        
        payload = self._format_payload(level, msg, **kwargs)
        headers = {
            'X-License-Key': self.license_key,
            'Content-Type': 'application/json'
        }
        
        try:
            await self.session.post(self.url, json=payload, headers=headers, timeout=aiohttp.ClientTimeout(total=5))
        except Exception:
            pass


class LogstashLogger(BaseSink):
    """Logstash HTTP input sink"""
    
    def initialize(self) -> None:
        self.url = self.sink_config.get('url', 'http://localhost:8080')
        self.session = None
        self._initialized = True
    
    def shutdown(self) -> None:
        if self.session:
            asyncio.create_task(self.session.close())
    
    def _format_payload(self, level: str, msg: str, **kwargs) -> Dict[str, Any]:
        import time
        return {
            '@timestamp': time.strftime('%Y-%m-%dT%H:%M:%S.000Z', time.gmtime()),
            'message': msg,
            'level': level,
            'logger': self.logger_name,
            **kwargs
        }
    
    def log(self, level: str, msg: str, **kwargs) -> None:
        if not self._initialized:
            return
        
        payload = self._format_payload(level, msg, **kwargs)
        
        try:
            requests.post(self.url, json=payload, timeout=5)
        except Exception:
            pass
    
    async def alog(self, level: str, msg: str, **kwargs) -> None:
        if not self._initialized:
            return
        
        if not self.session:
            self.session = aiohttp.ClientSession()
        
        payload = self._format_payload(level, msg, **kwargs)
        
        try:
            await self.session.post(self.url, json=payload, timeout=aiohttp.ClientTimeout(total=5))
        except Exception:
            pass
