import asyncio
import logging
import string

from aiokit import AioThing
from library.telegram.base import BaseTelegramClient
from telethon.tl.functions.channels import GetParticipantsRequest
from telethon.tl.types import ChannelParticipantsSearch


class AdminLogReader(AioThing):
    def __init__(self, telegram_config):
        super().__init__()
        self.subscriptors = set()
        self.loading = False
        self.telegram_client = BaseTelegramClient(
            app_id=telegram_config['app_id'],
            app_hash=telegram_config['app_hash'],
            database=telegram_config['database'],
            flood_sleep_threshold=25,
        )
        self.channel_name = telegram_config['related_channel']
        self.last_max_id = 0

    def statbox(self, **kwargs):
        logging.getLogger('statbox').info({'mode': 'admin_log_reader', **kwargs})

    async def skip_admin_log(self):
        async for event in self.telegram_client.iter_admin_log(self.channel_name, limit=1):
            self.last_max_id = event.id
        self.statbox(action='skipped_admin_log', max_id=self.last_max_id)

    async def process_admin_log(self, sleep=1.0):
        self.loading = True
        try:
            while 1:
                async for event in self.telegram_client.iter_admin_log(
                    self.channel_name, join=True, invite=True, leave=True, min_id=self.last_max_id
                ):
                    is_subscribed = event.joined or event.joined_invite
                    if is_subscribed:
                        self.subscriptors.add(event.user_id)
                    elif event.user_id in self.subscriptors:
                        self.subscriptors.remove(event.user_id)
                    self.last_max_id = event.id
                await asyncio.sleep(sleep)
        except asyncio.CancelledError:
            pass
        finally:
            self.loading = False

    async def _fetch_users(self, query):
        max_batch_size = 200

        participants = await self.telegram_client(
            GetParticipantsRequest(
                channel=self.channel_name,
                filter=ChannelParticipantsSearch(query),
                offset=0,
                limit=max_batch_size,
                hash=0,
            )
        )

        for user in participants.users:
            self.subscriptors.add(user.id)

        # There is a possibility that more users exist if we hit maximum count of users
        # So, we are making a recursion to reveal it
        if len(participants.users) == max_batch_size:
            for new_letter in string.ascii_lowercase + string.digits:
                await self._fetch_users(query + new_letter)

    async def load_channel_users(self):
        self.statbox(action='load_channel_users')
        await self._fetch_users('')
        self.statbox(action='loaded_channel_users', subscriptors=len(self.subscriptors))

    async def start(self):
        await self.telegram_client.start_and_wait()
        await self.skip_admin_log()
        await self.load_channel_users()
        asyncio.create_task(self.process_admin_log())

    def is_subscribed(self, user_id):
        return not self.loading or user_id in self.subscriptors
