import uuid

from autoslug import AutoSlugField

from django.apps import apps
from django.conf import settings
from django.contrib.sites.models import Site
from django.contrib.sites.managers import CurrentSiteManager
from django.db import models
from django.utils.translation import gettext_lazy as _

from vendor.config import VENDOR_PRODUCT_MODEL, DEFAULT_CURRENCY

from .validator import validate_msrp
from .utils import is_currency_available
from .modelmanagers import SoftDeleteManager


def get_product_model():
    return apps.get_model(VENDOR_PRODUCT_MODEL)


##################
# DEFAULTS
##################
# TODO: Nice to have class MSRP(NestedModels)
def product_meta_default():
    return {'msrp': {'default': DEFAULT_CURRENCY, DEFAULT_CURRENCY: 0.00}}


def product_description_default():
    return {
        'description': '',
        'call_out': ' '
    }

def create_sku_if_empty(instance):
    return f"{instance.site.pk}-{instance.name}"

##################
# BASE MODELS
##################

class CreateUpdateModelBase(models.Model):
    '''
    This is a shared models base that provides created & updated timestamp fields
    '''
    created = models.DateTimeField("date created", auto_now_add=True)
    updated = models.DateTimeField("last updated", auto_now=True)

    class Meta:
        abstract = True


class SoftDeleteModelBase(models.Model):
    '''
    Adds "Soft Delete" functionality to models
    '''
    deleted = models.BooleanField(_("Deleted"), default=False)

    # Managers
    objects = models.Manager()                  # Standard Model manager
    not_deleted = SoftDeleteManager()                # Records filtered with deleted=False

    class Meta:
        abstract = True

    def delete(self, using=None, keep_parents=False):
        self.deleted = True
        return self.save()


class ProductModelBase(CreateUpdateModelBase):
    '''
    This is the base class that all Products should inherit from.
    '''
    sku = models.CharField(_("SKU"), max_length=40, unique=True, blank=True, null=True, help_text=_("User Defineable SKU field"))   # Needs to be autogenerated by default, and unique from the PK
    uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)                                           # Used to track the product
    name = models.CharField(_("Name"), max_length=80, blank=False)
    site = models.ForeignKey(Site, verbose_name=_("Site"), on_delete=models.CASCADE, default=settings.SITE_ID, related_name="products")        # For multi-site support
    slug = AutoSlugField(populate_from='name', unique_with='site__id')                                                                         # Gets set in the save
    available = models.BooleanField(_("Available"), default=False, help_text=_("Is this currently available?"))        # This can be forced to be unavailable if there is no prices attached.
    description = models.JSONField(_("Description"), default=product_description_default, blank=True, null=True, help_text=_("Eg: {'call out': 'The ultimate product'}"))
    meta = models.JSONField(_("Meta"), validators=[validate_msrp], default=product_meta_default, blank=True, null=True, help_text=_("Eg: { 'msrp':{'usd':10.99} }\n(iso4217 Country Code):(MSRP Price)"))
    classification = models.ManyToManyField("vendor.TaxClassifier", blank=True)                                        # What taxes can apply to this item
    offers = models.ManyToManyField("vendor.Offer", blank=True, related_name="products")
    receipts = models.ManyToManyField("vendor.Receipt", blank=True, related_name="products")

    objects = models.Manager()
    on_site = CurrentSiteManager()

    class Meta:
        abstract = True

    def __str__(self):
        return self.name

    def get_msrp(self, currency):
        if currency in self.meta['msrp']:
            return self.meta['msrp'][currency]
        else:
            return self.meta['msrp'][self.meta['msrp']['default']]

    def add_to_cart_url(self):
        """
        Link to add the item to the user's cart.
        """
    # TODO: ADD trigger when object becomes unavailable to disable offer if it exisits.

    def get_best_currency(self, currency=DEFAULT_CURRENCY):
        """
        If no currency is provided as an argument it will default to the products's msrp default currency.
        If currency is provided but is not available in the product it will default to the products's msrp default currency.
        """
        if is_currency_available(self.meta['msrp'].keys(), currency=currency):
            return currency
        else:
            return self.meta['msrp']['default']

    def active_profile_receipts(self):
        """
        Gets currently active reciepts by checking if the customer owns the product
        """
        return [receipt for receipt in self.receipts.all() if receipt.profile.has_product(self)]
    
    def owners(self):
        """
        Gets a set list of profiles that own the product
        """
        active_receipts = self.active_profile_receipts()
        return set([receipt.profile for receipt in active_receipts])

    def inactive_profile_receipts(self):
        """
        Gets a list of reciepts for customers that no longer own the product.
        """
        return [receipt for receipt in self.receipts.all() if not receipt.profile.has_product(self)]

    def expired_owners(self):
        """
        Gets a set list of profiles that no longer own the product
        """
        owners = self.owners()
        inactive_receipts = self.inactive_profile_receipts()
        return set([receipt.profile for receipt in inactive_receipts if receipt.profile not in owners])

    def save(self, *args, **kwargs):
        if not self.sku:
            self.sku = create_sku_if_empty(self)
        super().save(*args, **kwargs)
