# django imports
import hashlib

from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.core.cache import cache
from django.db.models.signals import post_save, m2m_changed, post_delete
from django.db.models.signals import pre_save
from django.db.models.signals import pre_delete

# lfs imports
from lfs.caching.utils import clear_cache, delete_cache, invalidate_cache_group_id
from lfs.cart.models import Cart
from lfs.catalog.models import Category
from lfs.catalog.models import Product
from lfs.catalog.models import StaticBlock
from lfs.core.models import Shop
from lfs.core.signals import cart_changed
from lfs.core.signals import product_changed
from lfs.core.signals import category_changed
from lfs.core.signals import shop_changed
from lfs.core.signals import topseller_changed
from lfs.core.signals import manufacturer_changed
from lfs.criteria.models import CountryCriterion, Criterion, WeightCriterion, WidthCriterion, ShippingMethodCriterion, \
    PaymentMethodCriterion, LengthCriterion, HeightCriterion, CombinedLengthAndGirthCriterion, CartPriceCriterion
from lfs.customer_tax.models import CustomerTax
from lfs.marketing.models import Topseller
from lfs.order.models import OrderItem
from lfs.page.models import Page
from lfs.shipping.models import ShippingMethod
from lfs.tax.models import Tax

# reviews imports
from reviews.signals import review_added


# Shop
def shop_changed_listener(sender, **kwargs):
    clear_cache()
shop_changed.connect(shop_changed_listener)


# Cart
def cart_changed_listener(sender, **kwargs):
    update_cart_cache(sender)
cart_changed.connect(cart_changed_listener)


def cart_deleted_listener(sender, instance, **kwargs):
    update_cart_cache(instance)
pre_delete.connect(cart_deleted_listener, sender=Cart)


# Category
def category_deleted_listener(sender, instance, **kwargs):
    update_category_cache(instance)
pre_delete.connect(category_deleted_listener, sender=Category)


def category_saved_listener(sender, instance, **kwargs):
    update_category_cache(instance)
pre_save.connect(category_saved_listener, sender=Category)


def category_changed_listener(sender, **kwargs):
    update_category_cache(sender)
category_changed.connect(category_changed_listener)


def product_categories_changed_listener(sender, **kwargs):
    instance = kwargs['instance']
    reverse = kwargs['reverse']
    pk_set = kwargs['pk_set']

    if reverse:
        product = instance
        delete_cache("%s-product-categories-%s-%s" % (settings.CACHE_MIDDLEWARE_KEY_PREFIX, product.id, True))
        delete_cache("%s-product-categories-%s-%s" % (settings.CACHE_MIDDLEWARE_KEY_PREFIX, product.id, False))
    else:
        if pk_set:
            for product in Product.objects.filter(pk__in=pk_set):
                delete_cache("%s-product-categories-%s-%s" % (settings.CACHE_MIDDLEWARE_KEY_PREFIX, product.id, True))
                delete_cache("%s-product-categories-%s-%s" % (settings.CACHE_MIDDLEWARE_KEY_PREFIX, product.id, False))
m2m_changed.connect(product_categories_changed_listener, sender=Category.products.through)


# Manufacturer
def manufacturer_changed_listener(sender, **kwargs):
    # filtered lists of products assigned to manufacturer used at manufacturer page
    delete_cache("%s-manufacturer-products-%s" % (settings.CACHE_MIDDLEWARE_KEY_PREFIX, sender.slug))
    # list of all manufacturer products
    delete_cache("%s-manufacturer-all-products-%s" % (settings.CACHE_MIDDLEWARE_KEY_PREFIX, sender.pk))
    # if manufacturer assignment was changed then product navigation might be different too
    invalidate_cache_group_id('product_navigation')
manufacturer_changed.connect(manufacturer_changed_listener)


# OrderItem
def order_item_listener(sender, instance, **kwargs):
    """Deletes topseller after an OrderItem has been updated. Topseller are
    calculated automatically on base of OrderItems, hence we have to take of
    that.
    """
    delete_cache("%s-topseller" % settings.CACHE_MIDDLEWARE_KEY_PREFIX)
    try:
        for category in instance.product.get_categories(with_parents=True):
            delete_cache("%s-topseller-%s" % (settings.CACHE_MIDDLEWARE_KEY_PREFIX, category.id))
    except:
        pass  # fail silently
pre_delete.connect(order_item_listener, sender=OrderItem)
post_save.connect(order_item_listener, sender=OrderItem)


# Page
def page_saved_listener(sender, instance, **kwargs):
    delete_cache("%s-page-%s" % (settings.CACHE_MIDDLEWARE_KEY_PREFIX, instance.slug))
    delete_cache("%s-pages" % settings.CACHE_MIDDLEWARE_KEY_PREFIX)
post_save.connect(page_saved_listener, sender=Page)


# Product
def product_changed_listener(sender, **kwargs):
    update_product_cache(sender)
product_changed.connect(product_changed_listener)


def product_saved_listener(sender, instance, **kwargs):
    # update_product_cache(instance)
    update_category_cache(instance)
post_save.connect(product_saved_listener, sender=Product)


def product_pre_saved_listener(sender, instance, **kwargs):
    """ If product slug was changed we should have cleared slug based product cache"""
    # check if product already exists in database
    if instance.pk:
        if instance.is_variant():
            parent = instance.parent
        else:
            parent = instance

        delete_cache("%s-product-%s" % (settings.CACHE_MIDDLEWARE_KEY_PREFIX, parent.id))

        try:
            old_product = Product.objects.get(pk=parent.pk)
        except Product.DoesNotExist: