"""
HTTP 클라이언트 수집기
httpx와 requests 요청을 가로채서 외부 API 호출을 추적함

역할: httpx와 requests를 통한 모든 외부 API 호출을 자동으로 추적함
사용: MonitoringSDK.init()에서 자동 등록됨
"""

import time
from datetime import datetime
from typing import Optional
from ..types.interfaces import SpanEntry
from ..transport.batch_sender import BatchSender
from ..tracing.trace_context import get_trace_context
from ..tracing.id_generator import generate_span_id


class HttpClientCollector:
    """
    HTTP 클라이언트 호출을 추적하는 수집기
    """

    def __init__(
        self,
        batch_sender: BatchSender,
        service_name: str,
        environment: str
    ):
        """
        HttpClientCollector 초기화

        Args:
            batch_sender: 배치 전송자
            service_name: 서비스 이름
            environment: 환경 설정
        """
        self.batch_sender = batch_sender
        self.service_name = service_name
        self.environment = environment

    def setup_httpx_instrumentation(self) -> None:
        """
        httpx 라이브러리에 instrumentation을 설정함

        역할: httpx.AsyncClient 사용 시 자동으로 추적하도록 설정
        """
        try:
            import httpx

            # httpx event hooks를 사용하여 추적
            original_init = httpx.AsyncClient.__init__

            def patched_init(client_self, *args, **kwargs):
                # 기존 event_hooks 가져오기
                event_hooks = kwargs.get('event_hooks', {})

                # 요청/응답 훅 추가
                request_hooks = event_hooks.get('request', [])
                response_hooks = event_hooks.get('response', [])

                request_hooks.append(self._httpx_request_hook)
                response_hooks.append(self._httpx_response_hook)

                event_hooks['request'] = request_hooks
                event_hooks['response'] = response_hooks
                kwargs['event_hooks'] = event_hooks

                original_init(client_self, *args, **kwargs)

            httpx.AsyncClient.__init__ = patched_init

        except ImportError:
            # httpx가 설치되지 않은 경우 무시
            pass

    async def _httpx_request_hook(self, request) -> None:
        """httpx 요청 시작 훅"""
        request._monitoring_start_time = time.time()

    async def _httpx_response_hook(self, response) -> None:
        """httpx 응답 훅"""
        trace_context = get_trace_context()
        if not trace_context:
            return

        request = response.request
        if not hasattr(request, '_monitoring_start_time'):
            return

        duration = (time.time() - request._monitoring_start_time) * 1000
        status = 'OK' if response.status_code < 400 else 'ERROR'

        self._record_http_client_span(
            str(request.method),
            str(request.url),
            response.status_code,
            duration,
            status,
            trace_context.trace_id,
            trace_context.span_id
        )

    def setup_requests_instrumentation(self) -> None:
        """
        requests 라이브러리에 instrumentation을 설정함

        역할: requests.request 사용 시 자동으로 추적하도록 설정
        """
        try:
            import requests

            original_request = requests.request

            def patched_request(method, url, **kwargs):
                trace_context = get_trace_context()

                if not trace_context:
                    return original_request(method, url, **kwargs)

                start_time = time.time()
                status = 'ERROR'
                status_code = 0

                try:
                    response = original_request(method, url, **kwargs)
                    status_code = response.status_code
                    status = 'OK' if status_code < 400 else 'ERROR'
                    return response
                except Exception as e:
                    status = 'ERROR'
                    raise
                finally:
                    duration = (time.time() - start_time) * 1000
                    self._record_http_client_span(
                        method.upper(),
                        url,
                        status_code,
                        duration,
                        status,
                        trace_context.trace_id,
                        trace_context.span_id
                    )

            requests.request = patched_request

        except ImportError:
            # requests가 설치되지 않은 경우 무시
            pass

    def _record_http_client_span(
        self,
        method: str,
        url: str,
        status_code: int,
        duration: float,
        status: str,
        trace_id: str,
        parent_span_id: str
    ) -> None:
        """
        HTTP 클라이언트 span을 기록함

        Args:
            method: HTTP 메서드
            url: 요청 URL
            status_code: HTTP 상태 코드
            duration: 소요 시간 (밀리초)
            status: 상태 (OK 또는 ERROR)
            trace_id: Trace ID
            parent_span_id: Parent Span ID
        """
        span_id = generate_span_id()
        timestamp = datetime.utcnow().isoformat() + 'Z'

        span: SpanEntry = {
            'type': 'span',
            'timestamp': timestamp,
            'service_name': self.service_name,
            'environment': self.environment,
            'trace_id': trace_id,
            'span_id': span_id,
            'parent_span_id': parent_span_id,
            'name': f'{method} {url}',
            'kind': 'CLIENT',
            'duration_ms': duration,
            'status': status,  # type: ignore
            'http_method': method,
            'http_url': url,
            'http_status_code': status_code if status_code > 0 else None
        }

        self.batch_sender.add(span)
