import random
import string as string_module  # pylint: disable=deprecated-module
import urllib.parse

from django.apps import apps
from django.conf import settings
from django.http import QueryDict


# TODO: Remove legacy apps and this filtering step.
LEGACY_APPS = [
    "american_gut",
    "ancestry_dna",
    "data_selfie",
    "fitbit",
    "go_viral",
    "jawbone",
    "moves",
    "mpower",
    "pgp",
    "runkeeper",
    "twenty_three_and_me",
    "ubiome",
    "vcf_data",
    "wildlife",
    "withings",
]


def querydict_from_dict(input_dict):
    """
    Given a dict, return a QueryDict.
    """
    querydict = QueryDict("", mutable=True)
    querydict.update(input_dict)

    return querydict


def full_url(url_fragment):
    """
    Given a fragment, return that fragment joined to the full Open Humans URL.
    """
    if url_fragment and not url_fragment.startswith("/"):
        return url_fragment

    return urllib.parse.urljoin(
        settings.DEFAULT_HTTP_PROTOCOL + "://" + settings.DOMAIN, str(url_fragment)
    )


def get_source_labels_and_configs():
    """
    Return a list of all current data source app labels and names.
    """
    sources = [
        (app_config.label, app_config)
        for app_config in apps.get_app_configs()
        if app_config.name.startswith("studies.")
        or app_config.name.startswith("activities.")
    ]

    sources = [x for x in sources if x[0] not in LEGACY_APPS]

    return sorted(sources, key=lambda x: x[1].verbose_name.lower())


def get_activities():
    """
    Get just the activities.
    """
    return [
        activity
        for activity in get_source_labels_and_configs()
        if activity[1].name.startswith("activities.")
    ]


def get_studies():
    """
    Get just the studies.
    """
    return [
        study
        for study in get_source_labels_and_configs()
        if study[1].name.startswith("studies.")
    ]


def get_source_labels_and_names():
    """
    Return a list of all current data source app labels and names.
    """
    return [
        (label, app_config.verbose_name)
        for label, app_config in get_source_labels_and_configs()
    ]


def get_source_labels():
    """
    Return a list of all current data source app labels.
    """
    # Use get_source_labels_and_names so labels are sorted by verbose names
    return [label for label, _ in get_source_labels_and_names()]


def app_label_to_app_models(label):
    """
    Given an app's name, return its models.
    """
    return apps.get_app_config(label).get_models()


def app_label_to_verbose_name(label):
    """
    Given an app's name, return its verbose name.
    """
    return apps.get_app_config(label).verbose_name


def app_label_to_user_data_model(label):
    """
    Given an app name, return its UserData type.
    """
    for model in app_label_to_app_models(label):
        if (
            model.__base__.__name__ == "BaseStudyUserData"
            or model.__name__ == "UserData"
        ):
            return model

    app = apps.get_app_config(label)

    if hasattr(app, "user_data"):
        return app.user_data()


def generate_id(
    size=64,
    chars=(
        string_module.ascii_lowercase
        + string_module.ascii_uppercase
        + string_module.digits
    ),
):
    """
    Generate an ID consisting of upper and lowercase letters and digits.
    """
    return "".join(random.SystemRandom().choice(chars) for _ in range(size))


def origin(string):
    """
    Coerce an origin to 'open-humans' or 'external', defaulting to 'external'
    """
    return "open-humans" if string == "open-humans" else "external"