"""HTTP health check collector."""

from __future__ import annotations

import asyncio
from datetime import UTC, datetime
from typing import TYPE_CHECKING

import httpx
import structlog

from curadise_agent.collectors.base import DomainCollector
from curadise_agent.models.domain.metric import DomainMetric, MetricType

if TYPE_CHECKING:
    from curadise_agent.config.schema import CollectorConfig, HTTPCheckConfig

log = structlog.get_logger(__name__)


class HTTPCollector(DomainCollector):
    """
    Performs HTTP health checks on configured endpoints.

    Metrics include:
    - Response time
    - Status code
    - Success/failure status
    - SSL certificate expiry (if HTTPS)
    """

    def __init__(
        self,
        name: str = "http",
        config: CollectorConfig | None = None,
        checks: list[HTTPCheckConfig] | None = None,
        tags: dict[str, str] | None = None,
    ) -> None:
        """
        Initialize HTTP collector.

        Args:
            name: Collector name
            config: Collector configuration
            checks: List of HTTP checks to perform
            tags: Default tags
        """
        super().__init__(name=name, config=config, tags=tags)
        self._checks = checks or []
        self._client: httpx.AsyncClient | None = None

    async def initialize(self) -> None:
        """Initialize HTTP client."""
        self._client = httpx.AsyncClient(
            timeout=httpx.Timeout(30.0),
            follow_redirects=True,
        )

    async def shutdown(self) -> None:
        """Close HTTP client."""
        if self._client:
            await self._client.aclose()
            self._client = None

    async def collect(self) -> list[DomainMetric]:
        """Perform all configured HTTP checks."""
        if not self._checks:
            return []

        metrics: list[DomainMetric] = []

        # Run checks concurrently
        tasks = [self._check_endpoint(check) for check in self._checks]
        results = await asyncio.gather(*tasks, return_exceptions=True)

        for check, result in zip(self._checks, results, strict=False):
            if isinstance(result, BaseException):
                self._log.error(
                    "http_check_exception",
                    url=check.url,
                    error=str(result),
                )
                metrics.extend(self._create_failure_metrics(check, str(result)))
            else:
                metrics.extend(result)

        return metrics

    async def _check_endpoint(self, check: HTTPCheckConfig) -> list[DomainMetric]:
        """
        Check a single HTTP endpoint.

        Args:
            check: HTTP check configuration

        Returns:
            List of metrics from this check
        """
        if self._client is None:
            await self.initialize()
            assert self._client is not None

        metrics: list[DomainMetric] = []
        tags = {
            "url": check.url,
            "method": check.method,
        }

        start_time = datetime.now(UTC)

        try:
            response = await self._client.request(
                method=check.method,
                url=check.url,
                headers=check.headers,
                content=check.body.encode() if check.body else None,
                timeout=check.timeout,
            )

            duration_ms = (datetime.now(UTC) - start_time).total_seconds() * 1000

            # Response time metric
            metrics.append(
                DomainMetric(
                    name="http.response_time_ms",
                    value=duration_ms,
                    metric_type=MetricType.GAUGE,
                    unit="milliseconds",
                    tags=tags,
                )
            )

            # Status code metric
            metrics.append(
                DomainMetric(
                    name="http.status_code",
                    value=float(response.status_code),
                    metric_type=MetricType.GAUGE,
                    tags=tags,
                )
            )

            # Success metric (1 = success, 0 = failure)
            is_success = response.status_code == check.expected_status
            metrics.append(
                DomainMetric(
                    name="http.success",
                    value=1.0 if is_success else 0.0,
                    metric_type=MetricType.GAUGE,
                    tags=tags,
                    attributes={
                        "expected_status": check.expected_status,
                        "actual_status": response.status_code,
                    },
                )
            )

            # Response size
            content_length = len(response.content)
            metrics.append(
                DomainMetric(
                    name="http.response_size_bytes",
                    value=float(content_length),
                    metric_type=MetricType.GAUGE,
                    unit="bytes",
                    tags=tags,
                )
            )

            # SSL certificate info for HTTPS
            if check.url.startswith("https://"):
                ssl_metrics = await self._check_ssl(check.url, tags)
                metrics.extend(ssl_metrics)

            self._log.debug(
                "http_check_complete",
                url=check.url,
                status_code=response.status_code,
                duration_ms=duration_ms,
                success=is_success,
            )

        except httpx.TimeoutException:
            duration_ms = (datetime.now(UTC) - start_time).total_seconds() * 1000
            metrics.extend(self._create_failure_metrics(check, "timeout", duration_ms, tags))

        except httpx.ConnectError as e:
            duration_ms = (datetime.now(UTC) - start_time).total_seconds() * 1000
            metrics.extend(
                self._create_failure_metrics(check, f"connection_error: {e}", duration_ms, tags)
            )

        except Exception as e:
            duration_ms = (datetime.now(UTC) - start_time).total_seconds() * 1000
            metrics.extend(self._create_failure_metrics(check, str(e), duration_ms, tags))

        return metrics

    def _create_failure_metrics(
        self,
        check: HTTPCheckConfig,
        error: str,
        duration_ms: float = 0.0,
        tags: dict[str, str] | None = None,
    ) -> list[DomainMetric]:
        """Create metrics for a failed check."""
        if tags is None:
            tags = {
                "url": check.url,
                "method": check.method,
            }

        return [
            DomainMetric(
                name="http.response_time_ms",
                value=duration_ms,
                metric_type=MetricType.GAUGE,
                unit="milliseconds",
                tags=tags,
            ),
            DomainMetric(
                name="http.success",
                value=0.0,
                metric_type=MetricType.GAUGE,
                tags=tags,
                attributes={"error": error},
            ),
        ]

    async def _check_ssl(
        self,
        url: str,
        tags: dict[str, str],
    ) -> list[DomainMetric]:
        """
        Check SSL certificate for a URL.

        Args:
            url: HTTPS URL
            tags: Base tags

        Returns:
            SSL-related metrics
        """
        metrics: list[DomainMetric] = []

        try:
            import ssl
            from urllib.parse import urlparse

            parsed = urlparse(url)
            hostname = parsed.hostname or ""
            port = parsed.port or 443

            # Get certificate
            context = ssl.create_default_context()
            with context.wrap_socket(
                __import__("socket").create_connection((hostname, port), timeout=10),
                server_hostname=hostname,
            ) as sock:
                cert = sock.getpeercert()

            if cert:
                # Parse expiry date
                not_after = cert.get("notAfter", "")
                if not_after and isinstance(not_after, str):
                    from datetime import datetime as dt

                    # Parse SSL date format
                    expiry = dt.strptime(not_after, "%b %d %H:%M:%S %Y %Z")
                    expiry = expiry.replace(tzinfo=UTC)
                    days_until_expiry = (expiry - datetime.now(UTC)).days

                    metrics.append(
                        DomainMetric(
                            name="http.ssl_days_until_expiry",
                            value=float(days_until_expiry),
                            metric_type=MetricType.GAUGE,
                            unit="days",
                            tags=tags,
                        )
                    )

                    # Valid metric (1 if not expired)
                    metrics.append(
                        DomainMetric(
                            name="http.ssl_valid",
                            value=1.0 if days_until_expiry > 0 else 0.0,
                            metric_type=MetricType.GAUGE,
                            tags=tags,
                        )
                    )

        except Exception as e:
            self._log.debug("ssl_check_failed", url=url, error=str(e))
            metrics.append(
                DomainMetric(
                    name="http.ssl_valid",
                    value=0.0,
                    metric_type=MetricType.GAUGE,
                    tags=tags,
                    attributes={"error": str(e)},
                )
            )

        return metrics
