import os

from django.conf import settings
from django.contrib.postgres.indexes import GinIndex
from django.db import models
from django.db.models import JSONField, Q
from django.utils import timezone
from django_prices.models import MoneyField

from ..app.models import App
from ..core.models import ModelWithMetadata
from ..core.permissions import GiftcardPermissions
from ..core.utils.json_serializer import CustomJsonEncoder
from . import GiftCardEvents


class GiftCardQueryset(models.QuerySet):
    def active(self, date):
        return self.filter(
            Q(expiry_date__isnull=True) | Q(expiry_date__gte=date),
            is_active=True,
        )


class GiftCard(ModelWithMetadata):
    code = models.CharField(max_length=16, unique=True, db_index=True)
    is_active = models.BooleanField(default=True)
    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name="+",
    )
    used_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name="gift_cards",
    )
    created_by_email = models.EmailField(null=True, blank=True)
    used_by_email = models.EmailField(null=True, blank=True)
    app = models.ForeignKey(
        App,
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name="+",
    )

    expiry_date = models.DateField(null=True, blank=True)

    tag = models.CharField(max_length=255, null=True, blank=True)
    created = models.DateTimeField(auto_now_add=True)
    last_used_on = models.DateTimeField(null=True, blank=True)
    product = models.ForeignKey(
        "product.Product",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="gift_cards",
    )

    currency = models.CharField(
        max_length=settings.DEFAULT_CURRENCY_CODE_LENGTH,
        default=os.environ.get("DEFAULT_CURRENCY", "USD"),
    )

    initial_balance_amount = models.DecimalField(
        max_digits=settings.DEFAULT_MAX_DIGITS,
        decimal_places=settings.DEFAULT_DECIMAL_PLACES,
    )
    initial_balance = MoneyField(
        amount_field="initial_balance_amount", currency_field="currency"
    )

    current_balance_amount = models.DecimalField(
        max_digits=settings.DEFAULT_MAX_DIGITS,
        decimal_places=settings.DEFAULT_DECIMAL_PLACES,
    )
    current_balance = MoneyField(
        amount_field="current_balance_amount", currency_field="currency"
    )

    objects = models.Manager.from_queryset(GiftCardQueryset)()

    class Meta:
        ordering = ("code",)
        permissions = (
            (GiftcardPermissions.MANAGE_GIFT_CARD.codename, "Manage gift cards."),
        )
        indexes = [
            *ModelWithMetadata.Meta.indexes,
            GinIndex(
                name="giftcard_search_gin",
                # `opclasses` and `fields` should be the same length
                fields=["tag"],
                opclasses=["gin_trgm_ops"],
            ),
        ]

    @property
    def display_code(self):
        return "****%s" % self.code[-4:]


class GiftCardEvent(models.Model):
    date = models.DateTimeField(default=timezone.now, editable=False)
    type = models.CharField(max_length=255, choices=GiftCardEvents.CHOICES)
    parameters = JSONField(blank=True, default=dict, encoder=CustomJsonEncoder)
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        related_name="gift_card_events",
        on_delete=models.SET_NULL,
        null=True,
    )
    app = models.ForeignKey(
        App, related_name="gift_card_events", on_delete=models.SET_NULL, null=True
    )
    gift_card = models.ForeignKey(
        GiftCard, related_name="events", on_delete=models.CASCADE
    )

    class Meta:
        ordering = ("date",)
