# coding=utf-8

from __future__ import absolute_import
import logging

from subliminal.video import Episode, Movie
from subliminal.score import get_scores

logger = logging.getLogger(__name__)


FPS_EQUALITY = ((23.976, 23.98, 24.0),)


def framerate_equal(source, check):
    if source == check:
        return True

    source = float(source)
    check = float(check)
    if source == check:
        return True

    for equal_fps_tuple in FPS_EQUALITY:
        if check in equal_fps_tuple and source in equal_fps_tuple:
            return True

    return False


DEFAULT_SCORES = {
    "episode": {
        "hash": 359,
        "series": 160,
        "year": 90,
        "season": 30,
        "episode": 30,
        "source": 25,
        "release_group": 20,
        "audio_codec": 1,
        "resolution": 1,
        "video_codec": 1,
        "hearing_impaired": 1,
        "streaming_service": 1,
    },
    "movie": {
        "hash": 179,
        "title": 60,
        "year": 40,
        "source": 30,
        "edition": 30,
        "release_group": 15,
        "audio_codec": 1,
        "resolution": 1,
        "video_codec": 1,
        "hearing_impaired": 1,
        "streaming_service": 1,
    },
}

MAX_SCORES = {
    "episode": sum(v for k, v in DEFAULT_SCORES["episode"].items() if k != "hash"),
    "movie": sum(v for k, v in DEFAULT_SCORES["movie"].items() if k != "hash"),
}


def _check_hash_sum(scores: dict):
    hash_val = scores["hash"]
    rest_sum = sum(val for key, val in scores.items() if key != "hash")
    logger.debug("Hash value: %s -> Rest sum: %s", hash_val, rest_sum)
    return rest_sum - 1 == hash_val


class ComputeScore:
    def __init__(self, scores=None):
        if scores:
            valid = True
            for val in scores.values():
                if not _check_hash_sum(val):
                    logger.debug("Scores sum - 1 is not equal to hash. Using defaults")
                    self._scores = DEFAULT_SCORES
                    valid = False
                    break

            if valid is True:
                self._scores = scores
        else:
            self._scores = DEFAULT_SCORES

        # Hash values should be the same. Update from defaults to ensure it
        for key in self._scores.keys():
            self._scores[key]["hash"] = DEFAULT_SCORES[key]["hash"]

    def __call__(self, matches, subtitle, video, hearing_impaired=None):
        scores = self._scores[video.__class__.__name__.lower()]
        logger.debug("Scores to use for %s: %s", video, scores)

        is_episode = isinstance(video, Episode)
        is_movie = isinstance(video, Movie)

        episode_hash_valid_if = {"series", "season", "episode", "source"}
        movie_hash_valid_if = {"video_codec", "source"}

        orig_matches = matches.copy()

        # on hash match, discard everything else
        if subtitle.hash_verifiable and "hash" in matches:
            # hash is error-prone, try to fix that
            hash_valid_if = episode_hash_valid_if if is_episode else movie_hash_valid_if

            # don't validate hashes of specials, as season and episode tend to be wrong
            if is_movie or not video.is_special:
                if hash_valid_if <= set(matches):
                    # series, season and episode matched, hash is valid
                    logger.debug(
                        "%r: Using valid hash, as %s are correct (%r) and (%r)",
                        subtitle,
                        hash_valid_if,
                        matches,
                        video,
                    )
                    matches &= {"hash"}
                else:
                    # no match, invalidate hash
                    logger.debug(
                        "%r: Ignoring hash as other matches are wrong (missing: %r) and (%r)",
                        subtitle,
                        hash_valid_if - matches,
                        video,
                    )
                    matches -= {"hash"}
        elif "hash" in matches:
            logger.debug(
                "%r: Hash not verifiable for this provider. Keeping it", subtitle
            )
            matches &= {"hash"}

        # handle equivalent matches
        eq_matches = set()
        if is_episode:
            _episode_checks(video, eq_matches, matches)
        elif is_movie and "imdb_id" in matches:
            logger.debug("Adding imdb_id match equivalents")
            eq_matches |= {"title", "year"}

        matches |= eq_matches

        # handle hearing impaired
        if (
            hearing_impaired is not None
            and subtitle.hearing_impaired == hearing_impaired
        ):
            logger.debug("Matched hearing_impaired")
            matches.add("hearing_impaired")
            orig_matches.add("hearing_impaired")

        # compute the score
        score = sum((scores.get(match, 0) for match in matches))
        logger.info(
            "%r: Computed score %r with final matches %r", subtitle, score, matches
        )

        score_without_hash = sum(
            (
                scores.get(match, 0)
                for match in orig_matches | eq_matches
                if match != "hash"
            )
        )

        return score, score_without_hash


def _episode_checks(video, eq_matches, matches):
    if "title" in matches:
        logger.debug("Adding title match equivalent")
        eq_matches.add("episode")
    if "series_imdb_id" in matches:
        logger.debug("Adding series_imdb_id match equivalent")
        eq_matches |= {"series", "year"}
    if "imdb_id" in matches:
        logger.debug("Adding imdb_id match equivalents")
        eq_matches |= {"series", "year", "season", "episode"}
    if "tvdb_id" in matches:
        logger.debug("Adding tvdb_id match equivalents")
        eq_matches |= {"series", "year", "season", "episode", "title"}
    if "series_tvdb_id" in matches:
        logger.debug("Adding series_tvdb_id match equivalents")
        eq_matches |= {"series", "year"}

    # specials
    if (
        video.is_special
        and "title" in matches
        and "series" in matches
        and "year" in matches
    ):
        logger.debug("Adding special title match equivalent")
        eq_matches |= {"season", "episode"}


compute_score = ComputeScore()
