"""
The shared callback logic

expects to be called as part of a class with the properties:
* instance.should_whitelist
* instance.category
* instance.analyzer
* instance.white_lister
"""
from qai.issues.meta import get_meta_value


def _is_valid(instance):
    assert instance.should_whitelist is not None
    assert instance.category is not None
    assert instance.analyzer is not None


def _has_callable(instance, method):
    return hasattr(instance, method) and callable(getattr(instance, method))


def _filter_0distance(segment, issues):
    """
    filter issues to drop 0-distance edits
    :param issues: issues to filter
    :return: filter issues
    """
    filtered_issues = []
    for issue in issues:
        if issue["suggestions"]:
            suggestions = []
            for suggestion in issue["suggestions"]:
                # shouldn't lowercase, as this would prevent capitalization fixes
                # e.g. this would prevent 1-distance edits
                # but without it, this doesn't work, for unexplained reasons
                if (
                    segment[issue["from"] : issue["until"]]
                ) == suggestion:
                    continue
                suggestions.append(suggestion)

            if suggestions:
                issue["suggestions"] = suggestions
                filtered_issues.append(issue)
        else:  # some services don't offer suggestions, so no need to check
            filtered_issues.append(issue)
    return filtered_issues


def qallback(instance, segment, metadata, language):
    """
    Helper libs won't memoize this for us, because
    "Arguments to memoized function must be hashable"
    so we need to build it ourselves... weakass Python can't hash class instances
    """
    _is_valid(instance)

    if language not in instance.supported_languages:
        print(
            f"Received language {language}, supported Languages: {instance.supported_languages}"
        )
        return []

    # first param is now dealt with by mediator, we don't have to look at it
    _, meta_value, sub_groups = get_meta_value(instance.category, metadata)

    if _has_callable(instance.analyzer, "analyze_with_full_meta"):
        issues = instance.analyzer.analyze_with_full_meta(
            segment, meta=metadata[0].get("styleGuideMeta", {})
        )
    elif _has_callable(instance.analyzer, "analyze_with_metadata"):
        issues = instance.analyzer.analyze_with_metadata(
            segment, language=language, meta_value=meta_value, sub_groups=sub_groups
        )
    else:
        issues = instance.analyzer.analyze(segment)

    if instance.should_whitelist:
        # Should we have whitelisting on? This is a per-service setting;
        # e.g. Passive voice => no whitelisting
        # Gender bias => whitelisting
        issues = instance.white_lister(issues, meta_value, sub_groups)

    # filter issues to drop 0-distance edits
    if len(issues):
        if isinstance(issues[0], list):  # issues is List[List[dict]]
            filtered_issues = []
            for issues_list, sentence in zip(issues, segment):
                filtered_issues.append(_filter_0distance(sentence, issues_list))
        else:  # List[dict]
            filtered_issues = _filter_0distance(segment, issues)

        return filtered_issues
    else:
        return issues
