"""Generic fixed-schema text webhook channel adapter."""

from __future__ import annotations

import asyncio
from typing import Any

from beaver.foundation.events import ChannelIdentity, InboundMessage, OutboundMessage
from beaver.interfaces.channels.base import ChannelInboundSink


class GenericWebhookAdapter:
    def __init__(
        self,
        *,
        channel_id: str,
        kind: str,
        mode: str,
        account_id: str,
        display_name: str = "",
        inbound_sink: ChannelInboundSink,
        response_timeout_seconds: float = 1800,
    ) -> None:
        self.channel_id = channel_id
        self.kind = kind
        self.mode = mode
        self.account_id = account_id
        self.display_name = display_name or channel_id
        self.inbound_sink = inbound_sink
        self.response_timeout_seconds = max(1.0, float(response_timeout_seconds))
        self.started = False
        self._pending: dict[str, asyncio.Future[OutboundMessage]] = {}

    async def start(self) -> None:
        self.started = True

    async def stop(self) -> None:
        self.started = False
        for future in list(self._pending.values()):
            if not future.done():
                future.cancel()
        self._pending.clear()

    async def handle_webhook_payload(self, payload: dict[str, Any]) -> dict[str, Any]:
        text = str(payload.get("text") or "").strip()
        peer_id = str(payload.get("peer_id") or "").strip()
        message_id = str(payload.get("message_id") or "").strip()
        thread_id = str(payload.get("thread_id") or "").strip() or None
        peer_type = str(payload.get("peer_type") or "unknown").strip() or "unknown"
        user_id = str(payload.get("user_id") or "").strip() or None
        if not text:
            return {"ok": False, "error": "text is required"}
        if not peer_id:
            return {"ok": False, "error": "peer_id is required"}
        if not message_id:
            return {"ok": False, "error": "message_id is required"}

        identity = ChannelIdentity(
            channel_id=self.channel_id,
            kind=self.kind,
            account_id=self.account_id,
            peer_id=peer_id,
            thread_id=thread_id,
            peer_type=peer_type,
            user_id=user_id,
            message_id=message_id,
        )
        inbound = InboundMessage(
            channel=self.channel_id,
            content=text,
            user_id=user_id,
            channel_identity=identity,
            metadata={"webhook": {"peer_type": peer_type}},
        )
        future = asyncio.get_running_loop().create_future()
        self._pending[inbound.message_id] = future
        accept = await self.inbound_sink.accept_inbound(inbound)
        if not accept.accepted:
            self._pending.pop(inbound.message_id, None)
            record = accept.record or {}
            return {
                "ok": accept.error is None,
                "duplicate": accept.duplicate,
                "pending": accept.pending,
                "session_id": accept.session_id,
                "status": record.get("status"),
                "run_id": record.get("run_id"),
                "reply": record.get("reply"),
                "error": accept.error or record.get("error"),
            }
        try:
            outbound = await asyncio.wait_for(future, timeout=self.response_timeout_seconds)
        except asyncio.TimeoutError:
            self._pending.pop(inbound.message_id, None)
            return {
                "ok": True,
                "duplicate": False,
                "pending": True,
                "session_id": accept.session_id,
            }
        return {
            "ok": outbound.finish_reason != "error",
            "duplicate": False,
            "pending": False,
            "session_id": outbound.session_id,
            "run_id": outbound.run_id,
            "reply": outbound.content,
            "error": outbound.metadata.get("error"),
        }

    async def send(self, message: OutboundMessage) -> None:
        future = self._pending.pop(message.message_id, None)
        if future is None or future.done():
            message.metadata["delivery_status"] = "unclaimed"
            return
        future.set_result(message)
