"""File containing Members app models."""

from django.db import models
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from django.db.models import Q, OuterRef, Subquery


class BaseMember(models.Model):
    """Abstract base model for member."""

    first_name = models.CharField(_("First name"), max_length=127)
    last_name = models.CharField(_("Last name"), max_length=127)
    email = models.EmailField(_("Email"), unique=True)
    POR = models.CharField(_("Place of residence"),
                           max_length=255)  # place of residence
    AYY = models.BooleanField(_("AYY"), default=False)
    jas = models.BooleanField(_("JAS"), default=False)

    class Meta:
        """Meta for base member model."""

        abstract = True

    def __str__(self):
        """Return member last name, first name and email."""
        return "{} {}, {}".format(self.last_name, self.first_name, self.email)

    def as_array(self):
        """Return member model as an array."""
        return [
            self.first_name,
            self.last_name,
            self.email,
            self.POR,
            int(self.AYY),
            int(self.jas)
        ]


class Request(BaseMember):
    """Member request model represents one member request."""

    class Meta:
        permissions = (
            ('read_application', 'Can see member application in list'),
        )

    submitted = models.DateTimeField(_('Submitted'), default=timezone.now)

    def to_member(self):
        """Convert array to member model."""
        member = Member.from_array(self.as_array())

        return member


class Payment(models.Model):
    """Payment model representing one payment event."""

    class Meta:
        permissions = (
            ('read_payment', 'Can see payment in list'),
        )

    date = models.DateTimeField(_('Date'), default=timezone.now)
    source = models.CharField(_('Source'), choices=[
        ('AYY', _('AYY')),
        ('cash', _('Cash')),
        ('bank_transfer', _('Bank transfer')),
    ], max_length=255)

    member = models.ForeignKey('Member',
                               on_delete=models.PROTECT,
                               blank=True,
                               null=True,
                               related_name='payments')

    def __str__(self):
        """Return payment id and date."""
        return 'Payment no. {}, {}'.format(self.id, str(self.date))

    @staticmethod
    def find_payments_by_name(query_name):
        qs = Payment.objects.all()
        for term in query_name.split():
            qs = qs.filter(Q(member__first_name__icontains=term) | Q(member__last_name__icontains=term))
        return qs


class Member(BaseMember):
    """Member model represets one member on the registry."""

    created = models.DateTimeField(_('Created'), default=timezone.now)

    class Meta:
        permissions = (
            ('check_by_email', 'Can check if user exists by email'),
            ('read_member', 'Can see member in list'),
        )
        verbose_name = _('Member')
        verbose_name_plural = _('Members')

    @staticmethod
    def from_array(array):
        """Create member from array."""
        if len(array) != 6:
            raise Exception("Invalid array length for member instantiation")

        return Member.objects.create(
            first_name=array[0],
            last_name=array[1],
            email=array[2],
            POR=array[3],
            AYY=bool(array[4]),
            jas=bool(array[5]),
        )

    @staticmethod
    def find_members_by_name(query_name):
        qs = Member.objects.all()
        for term in query_name.split():
            qs = qs.filter(Q(first_name__icontains=term) | Q(last_name__icontains=term))
        return qs

    @staticmethod
    def get_members_with_latest_payment(members_query):
        """Return QuerySet of given members QS with last_paid attribute."""
        latest = Payment.objects.filter(member=OuterRef('pk')).order_by('-date')
        return members_query.annotate(last_paid=Subquery(latest.values('date')[:1]))
