import logging
from typing import Optional, List, AsyncGenerator, Tuple, Any, Dict
from royalnet.bard.discord import YtdlDiscord
from royalnet.serf.discord import Playable
import discord
import random


log = logging.getLogger(__name__)


class RoyalPool(Playable):
    """A pool of :class:`YtdlDiscord` that will be played in a loop."""
    def __init__(self, start_with: Optional[List[YtdlDiscord]] = None):
        super().__init__()
        self.full_pool: List[YtdlDiscord] = []
        self.remaining_pool: List[YtdlDiscord] = []
        self.now_playing: Optional[YtdlDiscord] = None
        if start_with is not None:
            self.full_pool = [*self.full_pool, *start_with]
        log.debug(f"Created new {self.__class__.__qualname__} containing: {self.full_pool}")

    async def _generator(self) \
            -> AsyncGenerator[Optional["discord.AudioSource"], Tuple[Tuple[Any, ...], Dict[str, Any]]]:
        yield
        while True:
            if len(self.remaining_pool) == 0:
                if len(self.full_pool) == 0:
                    log.debug(f"Nothing in the pool, yielding None...")
                    yield None
                    continue
                else:
                    self.remaining_pool = self.full_pool.copy()
                    random.shuffle(self.remaining_pool)
            else:
                log.debug(f"Dequeuing an item...")
                # Get the first YtdlDiscord of the queue
                self.now_playing: YtdlDiscord = self.remaining_pool.pop(0)
                log.debug(f"Yielding FileAudioSource from: {self.now_playing}")
                # Create a FileAudioSource from the YtdlDiscord
                # If the file hasn't been fetched / downloaded / converted yet, it will do so before yielding
                async with self.now_playing.spawn_audiosource() as fas:
                    # Yield the resulting AudioSource
                    yield fas

    async def destroy(self):
        for file in self.full_pool:
            log.debug(f"Deleting: {file}")
            await file.delete_asap()
            log.debug(f"Deleting: {file.ytdl_file}")
            await file.ytdl_file.delete_asap()
            log.debug(f"Deleted successfully!")
        self.full_pool = []
        self.remaining_pool = []
        self.now_playing = None
