"""Manage cloud cloudhooks."""

from __future__ import annotations

import asyncio
from typing import TYPE_CHECKING, Any, TypedDict

from .api import ApiBase, CloudApiError, api_exception_handler
from .events.types import CloudhookCreatedEvent, CloudhookDeletedEvent

if TYPE_CHECKING:
    from . import Cloud, _ClientT


class CloudhookDetails(TypedDict):
    """Details of a cloudhook."""

    webhook_id: str
    cloudhook_id: str
    cloudhook_url: str
    managed: bool


class GeneratedCloudhookDetails(TypedDict):
    """Details of a generated cloudhook."""

    url: str
    cloudhook_id: str


class CloudhookApiError(CloudApiError):
    """Error raised when a cloudhook API call fails."""


class Cloudhooks(ApiBase):
    """Class to help manage cloudhooks."""

    def __init__(self, cloud: Cloud[_ClientT]) -> None:
        """Initialize cloudhooks."""
        super().__init__(cloud)
        cloud.iot.register_on_connect(self.async_publish_cloudhooks)

    async def async_publish_cloudhooks(self) -> None:
        """Inform the Relayer of the cloudhooks that we support."""
        if not self._cloud.is_connected:
            return

        cloudhooks = self._cloud.client.cloudhooks
        await self._cloud.iot.async_send_message(
            "webhook-register",
            {"cloudhook_ids": [info["cloudhook_id"] for info in cloudhooks.values()]},
            expect_answer=False,
        )

    async def async_create(self, webhook_id: str, managed: bool) -> CloudhookDetails:
        """Create a cloud webhook."""
        cloudhooks = self._cloud.client.cloudhooks

        if webhook_id in cloudhooks:
            raise ValueError("Hook is already enabled for the cloud.")

        if not self._cloud.iot.connected:
            raise ValueError("Cloud is not connected")

        # Create cloud hook
        async with asyncio.timeout(10):
            data = await self.generate()

        cloudhook_id = data["cloudhook_id"]
        cloudhook_url = data["url"]

        # Store hook
        cloudhooks_updated: dict[str, Any] = dict(cloudhooks)
        cloudhook: CloudhookDetails = {
            "webhook_id": webhook_id,
            "cloudhook_id": cloudhook_id,
            "cloudhook_url": cloudhook_url,
            "managed": managed,
        }
        cloudhooks_updated[webhook_id] = cloudhook
        await self._cloud.client.async_cloudhooks_update(cloudhooks_updated)

        await self.async_publish_cloudhooks()
        await self._cloud.events.publish(CloudhookCreatedEvent(cloudhook=cloudhook))
        return cloudhook

    async def async_delete(self, webhook_id: str) -> None:
        """Delete a cloud webhook."""
        cloudhooks = self._cloud.client.cloudhooks

        if webhook_id not in cloudhooks:
            raise ValueError("Hook is not enabled for the cloud.")

        # Remove hook
        cloudhooks_updated: dict[str, Any] = dict(cloudhooks)
        cloudhook: CloudhookDetails = cloudhooks_updated.pop(webhook_id)
        await self._cloud.client.async_cloudhooks_update(cloudhooks_updated)

        await self._cloud.events.publish(CloudhookDeletedEvent(cloudhook=cloudhook))
        await self.async_publish_cloudhooks()

    @api_exception_handler(CloudhookApiError)
    async def generate(self) -> GeneratedCloudhookDetails:
        """Get generated cloudhook details."""
        details: GeneratedCloudhookDetails = await self._call_cloud_api(
            action="webhook_generate",
        )
        return details
