"""
By specifying the 'proxy' Meta attribute, model subclasses can specify that
they will take data directly from the table of their base class table rather
than using a new table of their own. This allows them to act as simple proxies,
providing a modified interface to the data from the base class.
"""
from django.db import models

# A couple of managers for testing managing overriding in proxy model cases.


class PersonManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().exclude(name="fred")


class SubManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().exclude(name="wilma")


class Person(models.Model):
    """
    A simple concrete base class.
    """
    name = models.CharField(max_length=50)

    objects = PersonManager()

    def __str__(self):
        return self.name


class Abstract(models.Model):
    """
    A simple abstract base class, to be used for error checking.
    """
    data = models.CharField(max_length=10)

    class Meta:
        abstract = True


class MyPerson(Person):
    """
    A proxy subclass, this should not get a new table. Overrides the default
    manager.
    """
    class Meta:
        proxy = True
        ordering = ["name"]
        permissions = (
            ("display_users", "May display users information"),
        )

    objects = SubManager()
    other = PersonManager()

    def has_special_name(self):
        return self.name.lower() == "special"


class ManagerMixin(models.Model):
    excluder = SubManager()

    class Meta:
        abstract = True


class OtherPerson(Person, ManagerMixin):
    """
    A class with the default manager from Person, plus a secondary manager.
    """
    class Meta:
        proxy = True
        ordering = ["name"]


class StatusPerson(MyPerson):
    """
    A non-proxy subclass of a proxy, it should get a new table.
    """
    status = models.CharField(max_length=80)

    objects = models.Manager()

# We can even have proxies of proxies (and subclass of those).


class MyPersonProxy(MyPerson):
    class Meta:
        proxy = True


class LowerStatusPerson(MyPersonProxy):
    status = models.CharField(max_length=80)

    objects = models.Manager()


class User(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name


class UserProxy(User):
    class Meta:
        proxy = True


class AnotherUserProxy(User):
    class Meta:
        proxy = True


class UserProxyProxy(UserProxy):
    class Meta:
        proxy = True


class MultiUserProxy(UserProxy, AnotherUserProxy):
    class Meta:
        proxy = True

# We can still use `select_related()` to include related models in our querysets.


class Country(models.Model):
    name = models.CharField(max_length=50)


class State(models.Model):
    name = models.CharField(max_length=50)
    country = models.ForeignKey(Country, models.CASCADE)

    def __str__(self):
        return self.name


class StateProxy(State):
    class Meta:
        proxy = True

# Proxy models still works with filters (on related fields)
# and select_related, even when mixed with model inheritance


class BaseUser(models.Model):
    name = models.CharField(max_length=255)

    def __str__(self):
        return ':'.join((self.__class__.__name__, self.name,))


class TrackerUser(BaseUser):
    status = models.CharField(max_length=50)


class ProxyTrackerUser(TrackerUser):
    class Meta:
        proxy = True


class Issue(models.Model):
    summary = models.CharField(max_length=255)
    assignee = models.ForeignKey(ProxyTrackerUser, models.CASCADE, related_name='issues')

    def __str__(self):
        return ':'.join((self.__class__.__name__, self.summary,))


class Bug(Issue):
    version = models.CharField(max_length=50)
    reporter = models.ForeignKey(BaseUser, models.CASCADE)


class ProxyBug(Bug):
    """
    Proxy of an inherited class
    """
    class Meta:
        proxy = True


class ProxyProxyBug(ProxyBug):
    """
    A proxy of proxy model with related field
    """
    class Meta:
        proxy = True


class Improvement(Issue):
    """
    A model that has relation to a proxy model
    or to a proxy of proxy model
    """
    version = models.CharField(max_length=50)
    reporter = models.ForeignKey(ProxyTrackerUser, models.CASCADE)
    associated_bug = models.ForeignKey(ProxyProxyBug, models.CASCADE)


class ProxyImprovement(Improvement):
    class Meta:
        proxy = True
