# coding=utf-8

from __future__ import absolute_import

import functools
import importlib
import os
import logging
from subliminal.providers import Provider as _Provider
from subliminal.subtitle import Subtitle as _Subtitle
from subliminal_patch.extensions import provider_registry
from subliminal_patch.http import RetryingSession
from subliminal_patch.subtitle import Subtitle, guess_matches


logger = logging.getLogger(__name__)


class Provider(_Provider):
    hash_verifiable = False
    hearing_impaired_verifiable = False
    skip_wrong_fps = True

    def ping(self):
        """Check if the provider is alive."""
        return True


def reinitialize_on_error(exceptions: tuple, attempts=1):
    """Method decorator for Provider class. It will reinitialize the instance
    and re-run the method in case of exceptions.

    :param exceptions: tuple of expected exceptions
    :param attempts: number of attempts to call the method
    """

    def real_decorator(method):
        @functools.wraps(method)
        def wrapper(self, *args, **kwargs):
            inc = 1
            while True:
                try:
                    return method(self, *args, **kwargs)
                except exceptions as error:
                    if inc > attempts:
                        raise

                    logger.exception(error)
                    logger.debug("Reinitializing %s instance (%s attempt)", self, inc)

                    self.terminate()
                    self.initialize()

                    inc += 1

        return wrapper

    return real_decorator


# register providers
# fixme: this is bad
for name in os.listdir(os.path.dirname(__file__)):
    if name in ("__init__.py", "mixins.py", "utils.py") or not name.endswith(".py"):
        continue

    module_name = os.path.splitext(name)[0]
    mod = importlib.import_module("subliminal_patch.providers.%s" % module_name.lower())
    for item in dir(mod):
        cls = getattr(mod, item)
        if item != "Provider" and item.endswith("Provider") and not item.startswith("_"):
            is_sz_provider = issubclass(cls, Provider)
            is_provider = issubclass(cls, _Provider)

            if not is_provider:
                continue

            if not is_sz_provider:
                # patch provider bases
                new_bases = []

                for base in cls.__bases__:
                    if base == _Provider:
                        base = Provider
                    else:
                        if _Provider in base.__bases__:
                            base.__bases__ = (Provider,)
                    new_bases.append(base)

                cls.__bases__ = tuple(new_bases)

                # patch subtitle bases
                new_bases = []
                for base in cls.subtitle_class.__bases__:
                    if base == _Subtitle:
                        base = Subtitle
                    else:
                        if _Subtitle in base.__bases__:
                            base.__bases__ = (Subtitle,)
                    new_bases.append(base)

                cls.subtitle_class.__bases__ = tuple(new_bases)

            # inject our requests.Session wrapper for automatic retry but not for specific providers that are already
            # struggling and that we don't want to hurt more
            mod.Session = RetryingSession

            # inject our guess_matches function
            mod.guess_matches = guess_matches

            provider_registry.register(module_name, cls)

    # try patching the correspondent subliminal provider
    try:
        subliminal_mod = importlib.import_module("subliminal.providers.%s" % module_name.lower())
    except ImportError:
        pass
    else:
        # inject our requests.Session wrapper for automatic retry but not for specific providers that are already
        # struggling and that we don't want to hurt more
        subliminal_mod.Session = RetryingSession
        subliminal_mod.guess_matches = guess_matches

