import logging
from contextlib import contextmanager

from django.contrib.auth.models import Permission, User
from django.forms.models import model_to_dict as _model_to_dict


def model_to_dict(instance, fields=None, exclude=None):
    """
    Customized wrapper for Django's built-in model_to_dict(). Does the following:
      - Excludes the instance ID field
      - Exclude any fields prepended with an underscore
      - Convert any assigned tags to a comma-separated string
    """
    _exclude = ['id']
    if exclude is not None:
        _exclude += exclude

    model_dict = _model_to_dict(instance, fields=fields, exclude=_exclude)

    for key in list(model_dict.keys()):
        if key.startswith('_'):
            del model_dict[key]

        # TODO: Differentiate between tags assigned to the instance and a M2M field for tags (ex: ConfigContext)
        elif key == 'tags':
            model_dict[key] = ','.join(sorted([tag.name for tag in model_dict['tags']]))

        # Convert ManyToManyField to list of instance PKs
        elif model_dict[key] and type(model_dict[key]) in (list, tuple) and hasattr(model_dict[key][0], 'pk'):
            model_dict[key] = [obj.pk for obj in model_dict[key]]

    return model_dict


def post_data(data):
    """
    Take a dictionary of test data (suitable for comparison to an instance) and return a dict suitable for POSTing.
    """
    ret = {}

    for key, value in data.items():
        if value is None:
            ret[key] = ''
        elif type(value) in (list, tuple):
            ret[key] = value
        else:
            ret[key] = str(value)

    return ret


def create_test_user(username='testuser', permissions=list()):
    """
    Create a User with the given permissions.
    """
    user = User.objects.create_user(username=username)
    for perm_name in permissions:
        app, codename = perm_name.split('.')
        perm = Permission.objects.get(content_type__app_label=app, codename=codename)
        user.user_permissions.add(perm)

    return user


def choices_to_dict(choices_list):
    """
    Convert a list of field choices to a dictionary suitable for direct comparison with a ChoiceSet. For example:

        [
            {
                "value": "choice-1",
                "label": "First Choice"
            },
            {
                "value": "choice-2",
                "label": "Second Choice"
            }
        ]

    Becomes:

        {
            "choice-1": "First Choice",
            "choice-2": "Second Choice
        }
    """
    return {
        choice['value']: choice['label'] for choice in choices_list
    }


@contextmanager
def disable_warnings(logger_name):
    """
    Temporarily suppress expected warning messages to keep the test output clean.
    """
    logger = logging.getLogger(logger_name)
    current_level = logger.level
    logger.setLevel(logging.ERROR)
    yield
    logger.setLevel(current_level)

import logging
from contextlib import contextmanager

from django.contrib.auth.models import Permission, User
from django.forms.models import model_to_dict as _model_to_dict


def model_to_dict(instance, fields=None, exclude=None):
    """
    Customized wrapper for Django's built-in model_to_dict(). Does the following:
      - Excludes the instance ID field
      - Exclude any fields prepended with an underscore
      - Convert any assigned tags to a comma-separated string
    """
    _exclude = ['id']
    if exclude is not None:
        _exclude += exclude

    model_dict = _model_to_dict(instance, fields=fields, exclude=_exclude)

    for key in list(model_dict.keys()):
        if key.startswith('_'):
            del model_dict[key]

        # TODO: Differentiate between tags assigned to the instance and a M2M field for tags (ex: ConfigContext)
        elif key == 'tags':
            model_dict[key] = ','.join(sorted([tag.name for tag in model_dict['tags']]))

        # Convert ManyToManyField to list of instance PKs
        elif model_dict[key] and type(model_dict[key]) in (list, tuple) and hasattr(model_dict[key][0], 'pk'):
            model_dict[key] = [obj.pk for obj in model_dict[key]]

    return model_dict


def post_data(data):
    """
    Take a dictionary of test data (suitable for comparison to an instance) and return a dict suitable for POSTing.
    """
    ret = {}

    for key, value in data.items():
        if value is None:
            ret[key] = ''
        elif type(value) in (list, tuple):
            ret[key] = value
        else:
            ret[key] = str(value)

    return ret


def create_test_user(username='testuser', permissions=list()):
    """
    Create a User with the given permissions.
    """
    user = User.objects.create_user(username=username)
    for perm_name in permissions:
        app, codename = perm_name.split('.')
        perm = Permission.objects.get(content_type__app_label=app, codename=codename)
        user.user_permissions.add(perm)

    return user


def choices_to_dict(choices_list):
    """
    Convert a list of field choices to a dictionary suitable for direct comparison with a ChoiceSet. For example:

        [
            {
                "value": "choice-1",
                "label": "First Choice"
            },
            {
                "value": "choice-2",
                "label": "Second Choice"
            }
        ]

    Becomes:

        {
            "choice-1": "First Choice",
            "choice-2": "Second Choice
        }
    """
    return {
        choice['value']: choice['label'] for choice in choices_list
    }


@contextmanager
def disable_warnings(logger_name):
    """
    Temporarily suppress expected warning messages to keep the test output clean.
    """
    logger = logging.getLogger(logger_name)
    current_level = logger.level
    logger.setLevel(logging.ERROR)
    yield
    logger.setLevel(current_level)

Customized wrapper for Django's built-in model_to_dict(). Does the following:       - Excludes the instance ID field       - Exclude any fields prepended with an underscore       - Convert any assigned tags to a comma-separated string # TODO: Differentiate between tags assigned to the instance and a M2M field for tags (ex: ConfigContext) # Convert ManyToManyField to list of instance PKs Take a dictionary of test data (suitable for comparison to an instance) and return a dict suitable for POSTing. Create a User with the given permissions. Convert a list of field choices to a dictionary suitable for direct comparison with a ChoiceSet. For example:          [             {                 "value": "choice-1",                 "label": "First Choice"             },             {                 "value": "choice-2",                 "label": "Second Choice"             }         ]      Becomes:          {             "choice-1": "First Choice",             "choice-2": "Second Choice         } Temporarily suppress expected warning messages to keep the test output clean.