from datetime import datetime, timedelta
from random import randrange

from modules.common.module import BotModule


class PollingService(BotModule):
    def __init__(self, name):
        super().__init__(name)
        self.known_ids = set()
        self.account_rooms = dict()  # Roomid -> [account, account..]
        self.next_poll_time = dict()  # Roomid -> datetime, None = not polled yet
        self.service_name = "Service"
        self.poll_interval_min = 30  # TODO: Configurable
        self.poll_interval_random = 30
        self.owner_only = False # Set to true if service can be run only by bot owner
        self.send_all = False # Set to true to send all received items, even on first sync

    async def matrix_poll(self, bot, pollcount):
        if self.enabled and len(self.account_rooms):
            await self.poll_all_accounts(bot)

    async def poll_all_accounts(self, bot):
        now = datetime.now()
        delete_rooms = []
        for roomid in self.account_rooms:
            if roomid in bot.client.rooms:
                send_messages = True
                # First poll
                if not self.next_poll_time.get(roomid, None):
                    self.next_poll_time[roomid] = now + timedelta(hours=-1)
                    if not self.send_all:
                        send_messages = False
                        self.logger.debug(f'Polling all accounts for room {roomid} - but this is first sync so I wont send messages')
                if now >= self.next_poll_time.get(roomid):
                    accounts = self.account_rooms[roomid]
                    for account in accounts:
                        await self.poll_account(bot, account, roomid, send_messages)
            else:
                self.logger.warning(f'Bot is no longer in room {roomid} - deleting it from {self.service_name} room list')
                delete_rooms.append(roomid)

        if len(delete_rooms):
            for roomid in delete_rooms:
                self.account_rooms.pop(roomid, None)
            bot.save_settings()

        self.first_run = False

    async def poll_implementation(self, bot, account, roomid, send_messages):
        pass

    async def poll_account(self, bot, account, roomid, send_messages):
        polldelay = timedelta(minutes=self.poll_interval_min + randrange(self.poll_interval_random))
        self.next_poll_time[roomid] = datetime.now() + polldelay

        await self.poll_implementation(bot, account, roomid, send_messages)

    async def matrix_message(self, bot, room, event):
        if self.owner_only:
            bot.must_be_owner(event)

        args = event.body.split()

        if len(args) == 2:
            if args[1] == 'list':
                await bot.send_text(room,
                                    f'{self.service_name} accounts in this room: {self.account_rooms.get(room.room_id) or []}')
            elif args[1] == 'debug':
                await bot.send_text(room,
                                    f"{self.service_name} accounts: {self.account_rooms.get(room.room_id) or []} - known ids: {self.known_ids}\n" \
                                    f"Next poll in this room at {self.next_poll_time.get(room.room_id)} - in {self.next_poll_time.get(room.room_id) - datetime.now()}")
            elif args[1] == 'poll':
                bot.must_be_owner(event)
                self.logger.info(f'{self.service_name} force polling requested by {event.sender}')
                # Faking next poll times to force poll
                for roomid in self.account_rooms:
                    self.next_poll_time[roomid] = datetime.now() - timedelta(hours=1)
                await self.poll_all_accounts(bot)
            elif args[1] == 'clear':
                bot.must_be_admin(room, event)
                self.account_rooms[room.room_id] = []
                bot.save_settings()
                await bot.send_text(room, f'Cleared all {self.service_name} accounts from this room')
        if len(args) == 3:
            if args[1] == 'add':
                bot.must_be_admin(room, event)

                account = args[2]
                self.logger.info(f'Adding {self.service_name} account {account} to room id {room.room_id}')

                if self.account_rooms.get(room.room_id):
                    if account not in self.account_rooms[room.room_id]:
                        self.account_rooms[room.room_id].append(account)
                    else:
                        await bot.send_text(room, 'This account already added in this room!')
                        return
                else:
                    self.account_rooms[room.room_id] = [account]
                bot.save_settings()
                await bot.send_text(room, f'Added {self.service_name} account {account} to this room.')

            elif args[1] == 'del':
                bot.must_be_admin(room, event)

                account = args[2]
                self.logger.info(f'Removing {self.service_name} account {account} from room id {room.room_id}')

                if self.account_rooms.get(room.room_id):
                    self.account_rooms[room.room_id].remove(account)

                self.logger.info(f'{self.service_name} accounts now for this room {self.account_rooms.get(room.room_id)}')

                bot.save_settings()
                await bot.send_text(room, f'Removed {self.service_name} account from this room')

    def get_settings(self):
        data = super().get_settings()
        data['account_rooms'] = self.account_rooms
        return data

    def set_settings(self, data):
        super().set_settings(data)
        if data.get('account_rooms'):
            self.account_rooms = data['account_rooms']

    def help(self):
        return f'{self.service_name} polling'
