"""
모니터링 SDK 메인 클래스
FastAPI 애플리케이션에 간편한 통합을 제공함

역할: SDK의 진입점으로, 모든 collector를 초기화하고 설정을 관리함
사용: main.py에서 MonitoringSDK.init(app, config) 호출로 전체 SDK 활성화
"""

import logging
import atexit
import asyncio
from typing import Optional
from .types.interfaces import MonitoringConfig
from .transport.batch_sender import BatchSender
from .tracing.http_tracer import HttpTracerMiddleware
from .collectors.log_collector import LogCollectorHandler
from .collectors.http_client_collector import HttpClientCollector
from .collectors.bedrock_collector import BedrockCollector


class MonitoringSDK:
    """
    모니터링 SDK 메인 클래스
    """

    _instance: Optional['MonitoringSDK'] = None

    def __init__(self, config: MonitoringConfig):
        """
        MonitoringSDK 초기화 (내부용)

        Args:
            config: 모니터링 설정
        """
        self.config = self._validate_and_normalize_config(config)
        self.batch_sender = BatchSender(self.config)

    @classmethod
    def init(cls, app, config: MonitoringConfig) -> 'MonitoringSDK':
        """
        모니터링 SDK를 초기화함

        역할:
        1. SDK 인스턴스를 생성함 (싱글톤)
        2. BatchSender 시작 (배치 전송 타이머 시작)
        3. 모든 collector 자동 등록 (HTTP, Log, HTTP Client, Bedrock)
        4. Shutdown hook 설정

        사용: main.py에서 단 한 번만 호출함

        Args:
            app: FastAPI 애플리케이션 인스턴스
            config: 모니터링 설정

        Returns:
            MonitoringSDK 인스턴스
        """
        if cls._instance:
            print('[MonitoringSDK] SDK already initialized. Using existing instance.')
            return cls._instance

        sdk = cls(config)
        cls._instance = sdk

        sdk._setup_monitoring(app)

        print(f"[MonitoringSDK] Initialized for service: {config['service_name']}")
        print(f"[MonitoringSDK] Environment: {config.get('environment', 'development')}")
        print(f"[MonitoringSDK] Log Endpoint: {sdk.config.get('log_endpoint')}")
        print(f"[MonitoringSDK] Trace Endpoint: {sdk.config.get('trace_endpoint')}")

        return sdk

    def _setup_monitoring(self, app) -> None:
        """
        모든 모니터링 기능을 설정함

        역할: 설정에 따라 각 collector를 활성화함

        Args:
            app: FastAPI 애플리케이션
        """
        service_name = self.config['service_name']
        environment = self.config.get('environment', 'development')

        if self.config.get('enable_http_tracking', True):
            self._setup_http_tracing(app, service_name, environment)

        if self.config.get('enable_log_tracking', True):
            self._setup_log_collection(service_name, environment)

        if self.config.get('enable_http_client_tracking', True):
            self._setup_http_client_tracking(service_name, environment)

        if self.config.get('enable_bedrock_tracking', True):
            self._setup_bedrock_tracking(service_name, environment)

        self._setup_shutdown_hooks()

    def _setup_http_tracing(self, app, service_name: str, environment: str) -> None:
        """
        HTTP 요청 추적을 설정함

        역할: HttpTracerMiddleware를 FastAPI에 추가하여 모든 HTTP 요청을 추적함

        Args:
            app: FastAPI 애플리케이션
            service_name: 서비스 이름
            environment: 환경 설정
        """
        middleware = HttpTracerMiddleware(
            app=app,
            batch_sender=self.batch_sender,
            service_name=service_name,
            environment=environment
        )

        app.add_middleware(HttpTracerMiddleware.__bases__[0], dispatch=middleware.dispatch)
        print('[MonitoringSDK] HTTP tracing enabled')

    def _setup_log_collection(self, service_name: str, environment: str) -> None:
        """
        로그 수집을 설정함 (Python logging handler)

        역할: Python root logger에 LogCollectorHandler를 추가함

        Args:
            service_name: 서비스 이름
            environment: 환경 설정
        """
        try:
            handler = LogCollectorHandler(
                batch_sender=self.batch_sender,
                service_name=service_name,
                environment=environment
            )

            # Root logger에 handler 추가
            root_logger = logging.getLogger()
            root_logger.addHandler(handler)

            print('[MonitoringSDK] Log collection enabled')
        except Exception as e:
            print(f'[MonitoringSDK] Failed to setup log collection: {e}')

    def _setup_http_client_tracking(self, service_name: str, environment: str) -> None:
        """
        HTTP 클라이언트 추적을 설정함 (httpx, requests)

        역할: httpx와 requests에 instrumentation을 설정하여 외부 API 호출을 추적함

        Args:
            service_name: 서비스 이름
            environment: 환경 설정
        """
        collector = HttpClientCollector(
            batch_sender=self.batch_sender,
            service_name=service_name,
            environment=environment
        )

        collector.setup_httpx_instrumentation()
        collector.setup_requests_instrumentation()

        print('[MonitoringSDK] HTTP client tracking enabled')

    def _setup_bedrock_tracking(self, service_name: str, environment: str) -> None:
        """
        Bedrock API 호출 추적을 설정함 (boto3)

        역할: boto3 bedrock-runtime에 instrumentation을 설정하여 Bedrock 호출을 추적함

        Args:
            service_name: 서비스 이름
            environment: 환경 설정
        """
        collector = BedrockCollector(
            batch_sender=self.batch_sender,
            service_name=service_name,
            environment=environment
        )

        collector.setup_bedrock_instrumentation()

        print('[MonitoringSDK] Bedrock tracking enabled')

    def _setup_shutdown_hooks(self) -> None:
        """
        우아한 종료 hook을 설정함

        역할: 앱 종료 시 버퍼에 남은 데이터를 모두 전송함
        """
        def shutdown_handler():
            print('[MonitoringSDK] Shutting down, flushing data...')
            # asyncio 이벤트 루프에서 shutdown 실행
            try:
                loop = asyncio.get_event_loop()
                if loop.is_running():
                    loop.create_task(self.batch_sender.shutdown())
                else:
                    loop.run_until_complete(self.batch_sender.shutdown())
            except Exception as e:
                print(f'[MonitoringSDK] Error during shutdown: {e}')
            print('[MonitoringSDK] Shutdown complete')

        atexit.register(shutdown_handler)

    def _validate_and_normalize_config(self, config: MonitoringConfig) -> MonitoringConfig:
        """
        설정을 검증하고 정규화함

        역할: 필수 값 검증 및 기본값 적용

        Args:
            config: 사용자 제공 설정

        Returns:
            검증되고 기본값이 적용된 설정

        Raises:
            ValueError: 필수 설정이 없는 경우
        """
        if not config.get('api_key'):
            raise ValueError('[MonitoringSDK] api_key is required')

        if not config.get('service_name'):
            raise ValueError('[MonitoringSDK] service_name is required')

        # endpoint가 제공되면 log_endpoint와 trace_endpoint의 기본값으로 사용
        # 개별 엔드포인트가 명시되면 그것을 우선 사용
        log_endpoint = config.get('log_endpoint')
        trace_endpoint = config.get('trace_endpoint')

        if config.get('endpoint'):
            # 하위 호환성: 기존 endpoint가 제공되면 /logs, /traces 경로를 추가
            log_endpoint = log_endpoint or f"{config['endpoint']}/sdk/logs"
            trace_endpoint = trace_endpoint or f"{config['endpoint']}/sdk/traces"

        if not log_endpoint:
            raise ValueError('[MonitoringSDK] log_endpoint is required (or provide endpoint)')

        if not trace_endpoint:
            raise ValueError('[MonitoringSDK] trace_endpoint is required (or provide endpoint)')

        # 정규화된 설정 반환
        normalized_config: MonitoringConfig = {
            **config,
            'log_endpoint': log_endpoint,
            'trace_endpoint': trace_endpoint,
            'environment': config.get('environment', 'development'),
            'batch_size': config.get('batch_size', 100),
            'flush_interval': config.get('flush_interval', 5),
            'enable_log_tracking': config.get('enable_log_tracking', True),
            'enable_http_tracking': config.get('enable_http_tracking', True),
            'enable_http_client_tracking': config.get('enable_http_client_tracking', True),
            'enable_bedrock_tracking': config.get('enable_bedrock_tracking', True)
        }

        return normalized_config

    async def flush(self) -> None:
        """
        데이터를 수동으로 flush함

        역할: 테스트나 특수한 경우 즉시 데이터를 전송함
        사용: await sdk.flush()
        """
        await self.batch_sender.flush()

    def get_buffer_size(self) -> int:
        """
        현재 버퍼 크기를 조회함

        역할: 모니터링이나 디버깅 목적으로 버퍼 상태 확인

        Returns:
            버퍼에 쌓인 데이터 개수
        """
        return self.batch_sender.get_buffer_size()
