import itertools
import locale
import logging
import os

import uritools
from mopidy import backend, models

from . import Extension

logger = logging.getLogger(__name__)


def strerror(error):
    if isinstance(error.strerror, bytes):
        return error.strerror.decode(locale.getpreferredencoding())
    else:
        return error.strerror


def get_config_dir(config):
    try:
        return Extension.get_config_dir(config)
    except OSError as e:
        logger.warning(
            "Cannot access %s config directory: %s",
            Extension.dist_name,
            strerror(e),
        )
    except Exception as e:
        logger.warning(
            "Cannot access %s config directory: %s", Extension.dist_name, e
        )
    return None


class PodcastLibraryProvider(backend.LibraryProvider):
    def __init__(self, config, backend):
        super().__init__(backend)
        self.__config_dir = get_config_dir(config)
        self.__browse_root = config[Extension.ext_name]["browse_root"]
        self.__browse_order = config[Extension.ext_name]["browse_order"]
        self.__lookup_order = config[Extension.ext_name]["lookup_order"]
        self.__tracks = {}  # cache tracks for faster lookup

    @property
    def root_directory(self):
        root = self.__browse_root
        if not root:
            return None
        elif root.startswith(("file:", "http:", "https:")):
            uri = uritools.uridefrag("podcast+" + root).uri
            return models.Ref.directory(name="Podcasts", uri=uri)
        elif os.path.isabs(root):
            uri = uritools.uricompose("podcast+file", "", root)
            return models.Ref.directory(name="Podcasts", uri=uri)
        elif self.__config_dir:
            path = os.path.join(self.__config_dir, root)
            uri = uritools.uricompose("podcast+file", "", path)
            return models.Ref.directory(name="Podcasts", uri=uri)
        else:
            logger.error("Cannot retrieve Podcast root directory")
            return None

    def browse(self, uri):
        try:
            feed = self.backend.feeds[uri]
        except Exception as e:
            logger.error("Error retrieving %s: %s", uri, e)  # TODO: raise?
        else:
            return list(feed.items(self.__browse_order == "desc"))
        return []  # FIXME: hide errors from clients

    def get_images(self, uris):
        def key(uri):
            return uritools.uridefrag(uri).uri

        result = {}
        for feeduri, urls in itertools.groupby(sorted(uris, key=key), key=key):
            try:
                images = dict(self.backend.feeds[feeduri].images())
            except Exception as e:
                logger.error("Error retrieving images for %s: %s", feeduri, e)
            else:
                result.update((url, images.get(url, [])) for url in urls)
        return result

    def lookup(self, uri):
        # pop from __tracks since cached tracks shouldn't live too long
        try:
            track = self.__tracks.pop(uri)
        except KeyError:
            logger.debug("Lookup cache miss: %s", uri)
        else:
            return [track]
        try:
            feed = self.backend.feeds[uritools.uridefrag(uri).uri]
        except Exception as e:
            logger.error("Error retrieving %s: %s", uri, e)  # TODO: raise?
        else:
            return self.__lookup(feed, uri)
        return []  # FIXME: hide errors from clients

    def refresh(self, uri=None):
        if uri:
            self.backend.feeds.pop(uritools.uridefrag(uri).uri, None)
        else:
            self.backend.feeds.clear()
        self.__tracks.clear()

    def __lookup(self, feed, uri):
        if uri == feed.uri:
            return list(feed.tracks(self.__lookup_order == "desc"))
        else:
            self.__tracks = tracks = {t.uri: t for t in feed.tracks()}
            try:
                track = tracks.pop(uri)
            except KeyError:
                logger.warning("No such track: %s", uri)  # TODO: raise?
            else:
                return [track]
