import gzip
import io
import json
import random
from typing import AsyncGenerator, List, Optional

from maps_adv.adv_store.v2.lib.core.logbroker import LogbrokerClient, TopicWriter
from maps_adv.adv_store.api.schemas.enums import YesNoEnum
from maps_adv.common.helpers.enums import CampaignTypeEnum

from .schema import (
    DirectModerationDataZeroSpeedBanner,
    DirectModerationIncoming,
    DirectModerationMeta,
    DirectModerationOutgoing,
)

MAX_ORGANIZATION_TO_MODERATE = 5


class Client:
    __slots__ = (
        "_logbroker_client",
        "_topic_writer",
        "_out_topic",
        "_in_topic",
        "_consumer_id",
        "_avatar_mds_base_url",
    )

    _out_topic: str
    _in_topic: str
    _consumer_id: str
    _avatar_mds_base_url: str
    _logbroker_client: LogbrokerClient
    _topic_writer: TopicWriter

    def __init__(
        self,
        out_topic: str,
        in_topic: str,
        consumer_id: str,
        avatar_mds_base_url: str,
        logbroker_client: LogbrokerClient,
    ):
        self._out_topic = out_topic
        self._in_topic = in_topic
        self._consumer_id = consumer_id
        self._avatar_mds_base_url = avatar_mds_base_url

        self._logbroker_client = logbroker_client

    async def __aenter__(self) -> "Client":
        self._topic_writer = self._logbroker_client.create_writer(self._out_topic)
        await self._topic_writer.start()
        return self

    async def __aexit__(self, *args, **kwargs):
        await self._topic_writer.stop()

    async def send_campaign_moderation(
        self, campaign: dict, moderation: dict, geo_id: int
    ) -> None:

        message = None
        if campaign["campaign_type"] == CampaignTypeEnum.ZERO_SPEED_BANNER:
            message = self.create_moderation_message_for_zero_speed_banner(
                campaign, moderation
            )

        if message is not None:
            message.country_geo_id = geo_id
            await self._topic_writer.write_one(
                str.encode(json.dumps(message, default=lambda x: x.__dict__))
            )

    async def retrieve_direct_responses(
        self,
    ) -> AsyncGenerator[DirectModerationIncoming, None]:

        async with self._logbroker_client.create_reader(
            self._in_topic, self._consumer_id
        ) as reader:
            async for msg in reader.read_batch(1):

                decoded_msgs = self.decode_message(msg)

                for decoded_msg in decoded_msgs:
                    yield DirectModerationIncoming(
                        meta=DirectModerationMeta(
                            campaign_id=decoded_msg["meta"]["campaign_id"],
                            version_id=decoded_msg["meta"]["version_id"],
                        ),
                        verdict=YesNoEnum[decoded_msg["result"]["verdict"]],
                        reasons=decoded_msg["result"]["reasons"],
                    )
                reader.commit()
            await reader.finish_reading()

    @classmethod
    def decode_message(cls, msg):
        input = io.BytesIO()
        input.write(msg)
        input.seek(0)

        with gzip.GzipFile(fileobj=input, mode="rb") as fo:
            gunzipped_bytes_obj = fo.read()

        msgs = gunzipped_bytes_obj.decode().splitlines()

        return [json.loads(msg) for msg in msgs if msg]

    def create_moderation_message_for_zero_speed_banner(
        self, campaign: dict, moderation: dict
    ) -> Optional[DirectModerationOutgoing]:

        if "resolve_uri" in [action["type_"] for action in campaign["actions"]]:
            return None

        creative = next(
            (
                creative
                for creative in campaign["creatives"]
                if creative["type_"] == "banner"
            )
        )
        zero_speed_data = (
            self.create_zero_speed_banner_data(creative, campaign["actions"])
            if creative
            else None
        )

        return DirectModerationOutgoing(
            meta=DirectModerationMeta(
                campaign_id=campaign["id"], version_id=moderation["id"]
            ),
            type=campaign["campaign_type"],
            workflow=moderation["workflow"],
            country_geo_id=225,  # Russia by default
            verdictor_uid=moderation["reviewer_uid"],
            data=zero_speed_data,
        )

    def create_zero_speed_banner_data(
        self, creative: dict, actions: List[dict]
    ) -> DirectModerationDataZeroSpeedBanner:

        for action in actions:
            action["type"] = action.pop("type_")

            if (
                action.get("organizations") is not None
                and len(action["organizations"]) > MAX_ORGANIZATION_TO_MODERATE
            ):
                action["organizations"] = random.sample(
                    action["organizations"], MAX_ORGANIZATION_TO_MODERATE
                )

        return DirectModerationDataZeroSpeedBanner(
            image=self.build_image_url(creative["images"]),
            disclaimer=creative["disclaimer"],
            title=creative["title"],
            description=creative["description"],
            terms=creative["terms"],
            actions=actions,
        )

    def build_image_url(self, images: List[dict]) -> str:
        img = next((image for image in images if image["type"] == "source_image"))

        return (
            f"{self._avatar_mds_base_url}/get-geoadv-ext/"
            f'{img["group_id"]}/{img["image_name"]}/{img["alias_template"]}'
            if img
            else ""
        )
