from __future__ import annotations

from datetime import datetime
from typing import TYPE_CHECKING, Any

from ..ha_client import HAClient
from ..models import CheckResult, HAEventType, Severity
from ..storage import Storage
from .base import Check

if TYPE_CHECKING:
    from ..config import Settings

_MAX_RELEASE_NOTES = 2000


class UpdatesAvailableCheck(Check):
    """Detects available HA core/add-on updates via update.* entities.

    Runs daily. Emits one ha_update_available event per update entity whose
    7-day dedup window has expired. Falls back gracefully when HA is down.
    """

    name = "updates_available"

    def __init__(
        self,
        ha_client: HAClient,
        storage: Storage,
        settings: "Settings",
    ) -> None:
        self._client = ha_client
        self._storage = storage
        self._settings = settings

    async def run(self) -> list[CheckResult]:
        updates = await self._fetch_active_updates()
        if not updates:
            return []

        results: list[CheckResult] = []
        cooldown_s = self._settings.updates_cooldown_days * 86400

        for state in updates:
            eid = state["entity_id"]
            alert_key = f"update_available:{eid}"
            if await self._storage.was_alert_sent(alert_key, cooldown_s):
                continue
            attrs = state.get("attributes", {})
            results.append(CheckResult(
                healthy=False,
                event_type=HAEventType.ha_update_available,
                severity=Severity.info,
                message=(
                    f"Update available: {attrs.get('title', eid)} "
                    f"{attrs.get('installed_version', '?')} → "
                    f"{attrs.get('latest_version', '?')}"
                ),
                payload=_build_update_payload(eid, attrs),
            ))
            await self._storage.mark_alert_sent(alert_key)

        return results

    async def _fetch_active_updates(self) -> list[dict[str, Any]]:
        try:
            all_states = await self._client.get_states()
        except Exception:
            return []
        return [
            s for s in all_states
            if s["entity_id"].startswith("update.") and s["state"] == "on"
        ]


class UpdatesDigestCheck(UpdatesAvailableCheck):
    """Weekly Sunday digest: single event listing all pending updates.

    Deduped per ISO week (won't re-fire if triggered multiple times on the
    same Sunday, e.g. manual + scheduled).
    """

    name = "updates_digest"

    async def run(self) -> list[CheckResult]:
        updates = await self._fetch_active_updates()
        if not updates:
            return []

        week_key = datetime.now().strftime("%G-W%V")
        alert_key = f"update_digest:{week_key}"
        if await self._storage.was_alert_sent(alert_key, 6 * 86400):
            return []

        all_payloads = [
            _build_update_payload(s["entity_id"], s.get("attributes", {}))
            for s in updates
        ]
        await self._storage.mark_alert_sent(alert_key)
        return [CheckResult(
            healthy=False,
            event_type=HAEventType.ha_update_available,
            severity=Severity.info,
            message=f"Weekly digest: {len(all_payloads)} update(s) available",
            payload={"digest": True, "updates": all_payloads, "count": len(all_payloads)},
        )]


def _build_update_payload(entity_id: str, attrs: dict[str, Any]) -> dict[str, Any]:
    payload: dict[str, Any] = {
        "entity_id": entity_id,
        "title": attrs.get("title", entity_id),
        "installed_version": attrs.get("installed_version"),
        "latest_version": attrs.get("latest_version"),
        "in_progress": attrs.get("in_progress", False),
        "auto_update": attrs.get("auto_update", False),
    }
    if attrs.get("release_url"):
        payload["release_url"] = attrs["release_url"]
    summary = attrs.get("release_summary")
    if summary:
        payload["release_summary"] = summary[:_MAX_RELEASE_NOTES]
    return payload
