from dataclasses import asdict, dataclass
from logging import Logger
import aiohttp
from aiokafka import AIOKafkaProducer
from async_tools import AsyncInitable, AsyncDeinitable
from notification_server_connector.utils.exceptions import NotificationsServerError
from notification_server_connector.utils.abstract_encoder import AbstractEncoder
from notification_server_connector.models.notification import Notification, SourceTypes


logger = Logger(__name__)


class NotificationConnector(AsyncInitable, AsyncDeinitable):
    @dataclass
    class Config:
        url: str
        address: str
        max_request_size: int
        topic: str

    @dataclass
    class Context:
        encoder: AbstractEncoder

    _config: Config
    _producer: AIOKafkaProducer
    _context: Context

    def __init__(self, config: Config, context: Context) -> None:
        self._config = config
        self._context = context
        self._http_client = aiohttp.ClientSession()

        AsyncInitable.__init__(self)
        AsyncDeinitable.__init__(self)

    async def _async_init(self) -> None:
        self._producer = AIOKafkaProducer(
            bootstrap_servers=self._config.address,
            max_request_size=self._config.max_request_size,
        )
        await self._producer.start()

    async def _async_deinit(self) -> None:
        await self._producer.stop()

    async def send(self, notification: Notification) -> bool:
        logger.debug(f'write to kafka channel "{self._config.topic}": {notification}')
        payload = self._context.encoder.encode(asdict(notification))
        try:
            result = await self._producer.send(self._config.topic, value=payload)
            logger.debug(f'write to kafka channel "{self._config.topic}" done, result {result}')
            return True
        except Exception as exc:
            logger.error(f'write to kafka channel "{self._config.topic}" failed, error: {exc}')
            return False

    async def mark_notifications_as_read_internal(
        self,
        user_id: int,
        source_type: SourceTypes,
        source_ids: list[int],
        server_name: str = 'unknown',
        timeout: int = 5
    ) -> list[int]:
        logger.debug(
            f'mark notification for user({user_id}) as read for sources({source_type}): {source_ids}'
        )
        async with self._http_client.post(
            f'{self._config.url}/v1/mark_notifications_as_read_internal',
            json={
                'user_id': user_id,
                'source_type': source_type,
                'source_ids': source_ids,
            },
            headers={
                'server_name': server_name,
            },
            timeout=timeout,
        ) as response:
            logger.debug(
                f'mark notification as read done, status: {response.status}'
            )

            answer = await response.json()
            logger.debug(
                f'mark notification as read answer: {answer}'
            )
            done = answer.get('done', False)
            result = answer.get('result', False)
            error = answer.get('error', None)
            if not done:
                logger.error(f'mark notification as read failed, error: {error}')
                raise NotificationsServerError(f'mark notification as read failed, error: {error}')
            return result
