import sys
import warnings

from django.db.models import BooleanField, NullBooleanField


class DeprecatedField(object):
    """
    Descriptor class for deprecated fields. Do not use directly, use the
    deprecate_field function instead.
    """

    def __init__(self, val):
        self.val = val

    def _get_name(self, obj):
        """
        Try to find this field's name in the model class
        """
        for k, v in type(obj).__dict__.items():
            if v is self:
                return k
        return '<unknown>'

    def __get__(self, obj, type=None):
        warnings.warn('accessing deprecated field %s.%s' % (
                obj.__class__.__name__, self._get_name(obj)
            ), DeprecationWarning, stacklevel=2)
        if not callable(self.val):
            return self.val
        return self.val()

    def __set__(self, obj, val):
        warnings.warn('writing to deprecated field %s.%s' % (
                obj.__class__.__name__, self._get_name(obj)
            ), DeprecationWarning, stacklevel=2)
        self.val = val


def deprecate_field(field_instance, return_instead=None):
    """
    Can be used in models to delete a Field in a Backwards compatible manner.
    The process for deleting old model Fields is:
    1. Mark a field as deprecated by wrapping the field with this function
    2. Wait until (1) is deployed to every relevant server/branch
    3. Delete the field from the model.

    For (1) and (3) you need to run ./manage.py makemigrations:
    :param field_instance: The field to deprecate
    :param return_instead: A value or function that
    the field will pretend to have
    """
    if not set(sys.argv) & {"makemigrations", "migrate", "showmigrations"}:
        return DeprecatedField(return_instead)

    if not type(field_instance) == BooleanField:
        field_instance.null = True
        return field_instance

    # A BooleanField does not allow null=True, so we need to cast
    # this to a NullBooleanField
    return NullBooleanField(
        help_text=field_instance.help_text,
        default=field_instance.default
    )