"""Health, capability, and monitoring facades."""
from __future__ import annotations

from datetime import datetime, timezone
from typing import Any
from uuid import UUID

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app import __version__
from app.core.config import settings
from app.core.database import engine
from app.models.foundation import ProviderLog
from app.providers import list_registered_provider_kinds, supported_channels
from app.repositories.foundation import (
    MessageRepository,
    ProviderConfigRepository,
    QueueItemRepository,
)
from app.services.queue_engine import get_rate_limiter


class CapabilityService:
    def capabilities(self) -> dict[str, Any]:
        return {
            "service": settings.service_name,
            "version": __version__,
            "channels": supported_channels(),
            "features": [
                "provider_framework",
                "communication_router",
                "sms",
                "template_engine",
                "dynamic_contact_sources",
                "queue_engine",
                "delivery_tracking",
                "otp",
                "webhooks",
                "failover",
                "circuit_breaker",
                "rate_limiting",
                "idempotency",
                "tenant_isolation",
                "audit_logging",
                "metrics",
            ],
            "independent": True,
            "requires_other_modules": False,
            "registered_provider_kinds": list_registered_provider_kinds(),
            "contact_source_types": [
                "manual",
                "csv",
                "crm",
                "loyalty",
                "sports",
                "restaurant",
                "marketplace",
                "accounting",
                "website",
                "external_rest",
                "future",
            ],
        }


class MonitoringService:
    def __init__(self, session: AsyncSession) -> None:
        self.session = session
        self.messages = MessageRepository(session)
        self.queue = QueueItemRepository(session)
        self.providers = ProviderConfigRepository(session)

    async def stats(self, tenant_id: UUID) -> dict[str, Any]:
        providers = await self.providers.list_all_status(tenant_id)
        provider_rows = []
        for p in providers:
            healthy = p.circuit_state != "open" and p.status in ("active", "degraded")
            provider_rows.append(
                {
                    "id": p.id,
                    "name": p.name,
                    "provider_kind": p.provider_kind,
                    "channel": p.channel,
                    "status": p.status,
                    "circuit_state": p.circuit_state,
                    "consecutive_failures": p.consecutive_failures,
                    "balance": p.balance,
                    "rate_limit_per_minute": p.rate_limit_per_minute,
                    "last_health_at": p.last_health_at,
                    "last_error": p.last_error,
                    "healthy": healthy,
                }
            )
        return {
            "messages_by_status": await self.messages.count_by_status(tenant_id),
            "queue_by_status": await self.queue.count_by_status(tenant_id),
            "providers": provider_rows,
            "rate_limit_deferrals": get_rate_limiter().deferrals,
            "checked_at": datetime.now(timezone.utc).isoformat(),
        }

    async def providers_status(self, tenant_id: UUID) -> list[dict[str, Any]]:
        stats = await self.stats(tenant_id)
        return stats["providers"]

    async def metrics(self, tenant_id: UUID) -> dict[str, Any]:
        """Aggregated observability metrics for tenant."""
        stats = await self.stats(tenant_id)
        # SQLite-friendly aggregation
        stmt = (
            select(
                ProviderLog.provider_config_id,
                ProviderLog.success,
                ProviderLog.latency_ms,
            ).where(ProviderLog.tenant_id == tenant_id)
        )
        result = await self.session.execute(stmt)
        rows = result.all()
        by_provider: dict[str, dict[str, Any]] = {}
        for provider_id, success, latency in rows:
            key = str(provider_id)
            bucket = by_provider.setdefault(
                key,
                {"total": 0, "successes": 0, "failures": 0, "latencies": []},
            )
            bucket["total"] += 1
            if success:
                bucket["successes"] += 1
            else:
                bucket["failures"] += 1
            if latency is not None:
                bucket["latencies"].append(latency)

        provider_metrics = []
        for provider_id, bucket in by_provider.items():
            latencies = sorted(bucket["latencies"])
            avg_latency = (
                sum(latencies) / len(latencies) if latencies else None
            )
            p95 = None
            if latencies:
                idx = min(len(latencies) - 1, int(len(latencies) * 0.95))
                p95 = latencies[idx]
            total = bucket["total"] or 1
            provider_metrics.append(
                {
                    "provider_config_id": provider_id,
                    "total": bucket["total"],
                    "successes": bucket["successes"],
                    "failures": bucket["failures"],
                    "failure_rate": round(bucket["failures"] / total, 4),
                    "avg_latency_ms": round(avg_latency, 2) if avg_latency is not None else None,
                    "p95_latency_ms": p95,
                }
            )

        return {
            "service": settings.service_name,
            "version": __version__,
            "tenant_id": str(tenant_id),
            "messages_by_status": stats["messages_by_status"],
            "queue_by_status": stats["queue_by_status"],
            "rate_limit_deferrals": stats["rate_limit_deferrals"],
            "providers": stats["providers"],
            "provider_delivery": provider_metrics,
            "checked_at": datetime.now(timezone.utc).isoformat(),
        }

    @staticmethod
    async def database_ready() -> bool:
        try:
            async with engine.connect() as conn:
                await conn.execute(select(1))
            return True
        except Exception:  # noqa: BLE001
            return False
