# -*- coding: utf-8 -*-
from __future__ import division

# cdecimal is default in python >= 3.3
import decimal
import math
import re

from django.db import models
from django.conf import settings
from django.template.defaultfilters import slugify, truncatechars
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _, ugettext, ungettext

from babel.numbers import parse_decimal

from affiliation import (CURRENCIES, AVAILABILITY_PATTERNS,
                         RE_AVAILABILITY_PATTERNS)
from affiliation.managers import StoreManager, AffiliationItemManager
from items.models import Item
from utils.templatetags.format_utils import format_price

NAME_MAX_LENGTH = 200
ROUND = decimal.Decimal(".01")


class Store(models.Model):
    name = models.CharField(_("name"), max_length=100)
    url = models.URLField(_("url webapp"), max_length=200)
    affiliated_url = models.URLField(max_length=1000)
    slug = models.SlugField(_("slug"), editable=False)
    last_modified = models.DateTimeField(_("last modified"), auto_now=True)

    objects = StoreManager()

    class Meta:
        verbose_name = _("store")

    def __unicode__(self):
        return u"%s" % (self.name)

    def save(self, **kwargs):
        self.slug = slugify(self.name)
        super(Store, self).save(**kwargs)


class AffiliationItem(models.Model):
    name = models.CharField(_("name at store"), max_length=NAME_MAX_LENGTH)
    store = models.ForeignKey(Store, verbose_name=_("store"))
    object_id = models.CharField(_("store object id"), max_length=30)
    ean = models.CharField(_("EAN"), max_length=30)
    url = models.URLField(_("URL"), max_length=1000)
    price = models.DecimalField(_("price"), default=0, max_digits=14,
                                decimal_places=2)
    currency = models.SmallIntegerField(_("currency"), choices=CURRENCIES,
                                        default=CURRENCIES.euro)
    _shipping_price = models.DecimalField(_("shipping price"), default=-1,
                                          max_digits=14, decimal_places=2)
    _availability = models.CharField(_("availability"), max_length=50,
                                     default="see site")
    creation_date = models.DateTimeField(_("date created"),
                                         default=timezone.now, editable=False)
    update_date = models.DateTimeField(_("last modified"), auto_now=True)
    img_small = models.URLField(_("small image"), max_length=1000)
    img_medium = models.URLField(_("medium image"), max_length=1000)
    img_large = models.URLField(_("large image"), max_length=1000)

    item = models.ForeignKey(Item, null=True, blank=True)

    objects = AffiliationItemManager()

    class Meta:
        verbose_name = _("affiliation item")
        unique_together = (('store', 'object_id'),)

    def __unicode__(self):
        return u"%s @ %s" % (self.name[:10], self.store)

    def store_init(self, store, item):
        if store.__class__ is Store and item is not None:
            if store.name == "Amazon":
                self = _amazon_init(self, item)
            elif store.name == "Decathlon":
                self = _decathlon_init(self, item)
            self.store = store

    def same_as(self, other):
        fields = ["name", "store", "object_id", "ean", "url", "price",
                  "currency", "_availability", "_shipping_price"]
        same = True
        for field in fields:
            same = same and getattr(self, field) == getattr(other, field)

        return same

    def total_price(self):
        if self._shipping_price != -1:
            return self.price + self._shipping_price
        else:
            return self.price

    def shipping_price(self):
        if self._shipping_price != -1:
            return format_price(self._shipping_price, self.currency,
                                settings.LANGUAGE_CODE)
        else:
            return _("See site")

    def availability(self):
        match_exact = re.match(RE_AVAILABILITY_PATTERNS["exact"],
                               self._availability)
        match_range = re.match(RE_AVAILABILITY_PATTERNS["range"],
                               self._availability)

        if self._availability == "in stock":
            availability = _("In stock")
        elif match_exact is not None:
            value, unit = match_exact.groups()

            UNIT_TRANSLATIONS = {
                "D": ungettext("day", "days", value),
                "M": ungettext("month", "months", value)
            }

            availability = AVAILABILITY_PATTERNS["exact"].format(
                value=value, unit=UNIT_TRANSLATIONS[unit])
        elif match_range is not None:
            min_value, max_value, unit = match_range.groups()
            UNIT_TRANSLATIONS = {"D": ugettext("days"),
                                 "M": ugettext("months")}
            availability = AVAILABILITY_PATTERNS["range"].format(
                min_value=min_value, max_value=max_value,
                unit=UNIT_TRANSLATIONS[unit])
        else:
            availability = _("See site")
        return availability


def _amazon_init(aff_item, amazon_item):
    CURRENCY_TABLE = {"EUR": CURRENCIES.euro, "USD": CURRENCIES.dollar,
                      "GBP": CURRENCIES.pound}

    EXACT_PATTERN = AVAILABILITY_PATTERNS["exact"]
    RANGE_PATTERN = AVAILABILITY_PATTERNS["range"]

    aff_item.name = truncatechars(amazon_item.ItemAttributes.Title.pyval,
                                  NAME_MAX_LENGTH)
    aff_item.object_id = unicode(amazon_item.ASIN)
    aff_item.ean = unicode(getattr(amazon_item.ItemAttributes, "EAN", ""))
    aff_item.url = unicode(amazon_item.DetailPageURL)

    # Look for amazon then foreign new, then foreign used prices
    price = None
    if hasattr(amazon_item, "Offers") and hasattr(amazon_item, "OfferSummary"):
        if amazon_item.Offers.TotalOffers > 0:
            listing = amazon_item.Offers.Offer.OfferListing
            price = getattr(listing, "SalePrice", listing.Price)

            attr = listing.AvailabilityAttributes
            h = [attr.MinimumHours.pyval, attr.MaximumHours.pyval]
            d = [int(math.floor(h[0] / 24)), int(math.ceil(h[1] / 24))]
            d15 = [int(round(d[0] / 15) * 15), int(round(d[1] / 15) * 15)]
            m = [int(round(h[0] / (24 * 30))), int(round(h[1] / (24 * 30)))]
            if attr.AvailabilityType in ["now", "futureDate"]:
                if h[0] < 48:
                    availability = "in stock"
                elif d[1] < 15:
                    availability = RANGE_PATTERN.format(
                        min_value=d[0], max_value=d[1], unit="D")
                elif d[1] < 60:
                    if d15[0] != d15[1]:
                        availability = RANGE_PATTERN.format(
                            min_value=d15[0], max_value=d15[1], unit="D")
                    else:
                        availability = EXACT_PATTERN.format(
                            value=d15[0], unit="D")
                else:
                    if m[0] != m[1]:
                        availability = RANGE_PATTERN.format(
                            min_value=m[0], max_value=m[1], unit="M")
                    else:
                        availability = EXACT_PATTERN.format(
                            value=m[0], unit="M")
                aff_item._availability = availability
            elif attr.AvailabilityType == "unknown":
                pass
            else:
                # TODO: find a better way to grab new cases
                print attr.AvailabilityType
        elif hasattr(amazon_item.OfferSummary, "LowestNewPrice"):
            price = amazon_item.OfferSummary.LowestNewPrice
        elif hasattr(amazon_item.OfferSummary, "LowestUsedPrice"):
            price = amazon_item.OfferSummary.LowestUsedPrice

    if price is not None:
        aff_item.currency = CURRENCY_TABLE.get(price.CurrencyCode,
                                               CURRENCIES.euro)
        aff_item.price = (decimal.Decimal(price.Amount.pyval) /
                          decimal.Decimal(100)).quantize(ROUND)

    if hasattr(amazon_item, "SmallImage"):
        aff_item.img_small = unicode(amazon_item.SmallImage.URL)
    if hasattr(amazon_item, "MediumImage"):
        aff_item.img_medium = unicode(amazon_item.MediumImage.URL)
    if hasattr(amazon_item, "LargeImage"):
        aff_item.img_large = unicode(amazon_item.LargeImage.URL)

    return aff_item


def _decathlon_init(aff_item, decathlon_item):
    MATCHING_TABLE = {
        "Url": "url",
        "EAN": "ean",
        "Id produit": "object_id",
        "Url image petite": "img_small",
        "Url image moyenne": "img_medium",
        "Url image grande": "img_large"
    }

    CURRENCY_TABLE = {u"€": CURRENCIES.euro, u"$": CURRENCIES.dollar,
                      u"£": CURRENCIES.pound}

    DECATHLON_AVAILABILITY_PATTERN = "(?P<days>\d*)\s?jours"
    EXACT_PATTERN = AVAILABILITY_PATTERNS["exact"]

    ROUND = decimal.Decimal(".01")

    for key, value in decathlon_item.items():
        val = unicode(value, "utf-8")
        if key in ["Prix", "Prix barré", "Frais de port"]:
            parsed_price = parse_decimal(val, locale="fr")
            decimal_price = decimal.Decimal(parsed_price).quantize(ROUND)
            if key == "Prix":
                price = decimal_price
            elif key == "Prix barré":
                price_2 = decimal_price
            elif key == "Frais de port":
                aff_item._shipping_price = decimal_price
        elif key == "Monnaie":
            aff_item.currency = CURRENCY_TABLE.get(val, CURRENCIES.euro)
        elif key == "Nom":
            aff_item.name = truncatechars(val, NAME_MAX_LENGTH)
        elif key == "Disponibilité":
            match = re.match(DECATHLON_AVAILABILITY_PATTERN, val)
            if match is not None:
                days = match.groups("days")[0]
                if days:
                    availability = EXACT_PATTERN.format(
                        value=days, unit="D")
                else:
                    availability = "in stock"
                aff_item._availability = availability
        elif key in MATCHING_TABLE:
            setattr(aff_item, MATCHING_TABLE[key], val)

    aff_item.price = price if price and price < price_2 else price_2

    return aff_item
