from django.db.models import Q

from hookmigration.peewee_models import CustomersOrder
from oscar.core.loading import get_model
from django.core.management import BaseCommand
import requests
from hookmigration.models import Transient
from django.conf import settings
import uuid
import itertools
from peewee import *
from django.db import transaction
from django.conf import settings
import os
from django.contrib.contenttypes.models import ContentType
from decimal import Decimal as D
import re
import crayons
from django.contrib.auth.models import User
from apps.order.utils import OrderNumberGenerator
from progressbar import ProgressBar
from pprint import pprint
from django.utils import timezone
from subscription.models import Subscription

Product = get_model('catalogue', 'Product')
Category = get_model('catalogue', 'Category')
Option = get_model('catalogue', 'Option')
ProductAttribute = get_model('catalogue', 'ProductAttribute')
ProductAttributeValue = get_model('catalogue', 'ProductAttributeValue')
ProductClass = get_model('catalogue', 'ProductClass')
StockRecord = get_model('partner', 'StockRecord')
Partner = get_model('partner', 'Partner')
ProductImage = get_model('catalogue', 'ProductImage')
ProductCategory = get_model('catalogue', 'ProductCategory')
AttributeOption = get_model('catalogue', 'AttributeOption')
AttributeOptionGroup = get_model('catalogue', 'AttributeOptionGroup')
Country = get_model('address', 'Country')
Order = get_model('order', 'Order')
Line = get_model('order', 'Line')
LinePrice = get_model('order', 'LinePrice')
UserAddress = get_model('address', 'UserAddress')
ShippingAddress = get_model('order', 'ShippingAddress')
LineAttribute = get_model('order', 'LineAttribute')

product_content_type = ContentType.objects.get_for_model(Product)
order_content_type = ContentType.objects.get_for_model(Order)
address_content_type = ContentType.objects.get_for_model(UserAddress)
shipping_address_content_type = ContentType.objects.get_for_model(ShippingAddress)
user_content_type = ContentType.objects.get_for_model(User)

coffee_product_class = ProductClass.objects.get(slug='coffee-bag')
pods_product_class = ProductClass.objects.get(slug='coffee-pods')

GRINDED = 'GR'
WHOLEBEANS = 'WB'
DRIP_BAGS = 'DR'
BOTTLED = 'BO'
BREW_BAG = 'BB'
NESPRESSO = 'NE'  # ['NE', 'SP'] in fact
SHOT_PODS = 'SP'
PODS = 'PODS'

PACKAGE_CHOICES = {
    GRINDED: 'Ground',
    WHOLEBEANS: 'Whole Beans',
    DRIP_BAGS: 'Drip Bags',
    BOTTLED: '',
    BREW_BAG: '',
    NESPRESSO: ''  # This will be the pods orders
}


class Command(BaseCommand):
    help = 'Migrate the coffee images'

    problem_users = set()
    problem_products = set()
    problem_orders = set()

    def create_the_product(self, parent_id, packaging, weight, recurring):
        parent = Product.objects.filter(parent_id=parent_id).first()
        if not parent:
            raise Exception('No Product {}'.format(parent_id))

        product_class = ProductClass.objects.filter(slug='coffee-bag').first()
        packaging_attr = ProductAttribute.objects.filter(
            product_class=product_class,
            code='packaging'
        ).first()
        weight_attr = ProductAttribute.objects.filter(
            product_class=product_class,
            code='weight'
        ).first()
        product = Product.objects.create(
            structure=parent.CHILD,
            parent=parent,
            slug=parent.slug,
            is_discountable=1,
            live=False
        )
        ProductAttributeValue.objects.create(
            attribute=weight_attr,
            product=product,
            value_option=weight,
        )
        ProductAttributeValue.objects.create(
            attribute=packaging_attr,
            product=product,
            value_option=packaging,
        )

        partner = Partner.objects.order_by('pk').first()
        currencies = set([c['currency'] for c in getattr(settings, 'SUPPORTED_COUNTRIES')])

        for currency in currencies:
            sku = uuid.uuid4()
            StockRecord.objects.create(
                product=product,
                partner=partner,
                price_currency=currency,
                partner_sku=sku.hex,
            )
        return product

    def get_coffee(self, order, parent):
        if order.package == DRIP_BAGS:
            weight = '10 Drip Coffee Bags'
        else:
            if order.size == "80":
                weight = '80g'
            elif order.size == "200":
                weight = '200g'
            elif order.size == "1000":
                weight = '1kg'
            else:
                raise Exception("kill batman")

        if not order.package in [GRINDED, WHOLEBEANS, DRIP_BAGS]:
            return None

        for child in parent.children.all():
            if str(child.attr.weight) == weight and child.subscription == order.recurrent and str(
                    child.attr.packaging) == \
                    PACKAGE_CHOICES[order.package]:
                return child
        self.problem_products.add(order.id)

    def get_pod(self, order, parent):
        for child in parent.children.all():
            if child.subscription == order.recurrent:
                return child
        self.problem_products.add(order.id)

    def handle(self, *args, **options):
        STATUS_CHOICES = {
            'AC': 'Being processed',
            'SH': 'Shipped',
            'PA': 'Pending',
            'CA': 'Cancelled',
            'ER': 'Error',
            'DE': 'Error'
        }

        GRINDED = 'GR'
        WHOLEBEANS = 'WB'
        DRIP_BAGS = 'DR'
        BOTTLED = 'BO'
        BREW_BAG = 'BB'
        NESPRESSO = 'NE'  # ['NE', 'SP'] in fact
        SHOT_PODS = 'SP'
        PODS = 'PODS'

        PACKAGE_CHOICES = {
            GRINDED: 'Ground',
            WHOLEBEANS: 'Whole Beans',
            DRIP_BAGS: 'Drip Bags',
            BOTTLED: '',
            BREW_BAG: '',
            NESPRESSO: ''  # This will be the pods orders
        }

        # bew method mapping
        brewing_method_option = Option.objects.filter(code='brewing-method').first()
        BREW_MAP = {
            1: 'Espresso',  # Espresso
            2: 'Drip',  # Drip
            3: 'Aeropress',  # Aeropress
            4: 'French Press',  # French Press
            5: 'Stove Top',  # Stove top
            6: 'Nespresso',  # Nespresso
            7: 'None',  # None
            8: 'Cold Brew',  # Cold Brew
        }

        with transaction.atomic():
            orders = CustomersOrder.select()
            finished = 0
            count = orders.count()
            progress = ProgressBar(max_value=count)
            for order in orders:
                transient = Transient.objects.filter(
                    content_type=product_content_type,
                    old_pk=order.coffee_id,
                    old_model=order.coffee.__class__.__name__
                ).first()
                finished += 1
                progress.update(finished)
                # Find  the right child.
                # if order.package in [GRINDED, WHOLEBEANS, DRIP_BAGS]:
                parent = Product.objects.filter(pk=transient.new_pk).prefetch_related(
                    'product_class', 'parent', 'attribute_values', 'parent__attribute_values', 'stockrecords',
                    'children'
                ).first()
                if not parent:
                    raise Exception('No product', order.id, ' - ', order.coffee_id)

                if parent.product_class == coffee_product_class:
                    product = self.get_coffee(order, parent)
                elif parent.product_class == pods_product_class:
                    product = self.get_pod(order, parent)

                if product is None:
                    self.problem_orders.add(order.id)
                    continue

                new_user_id = Transient.objects.filter(content_type=user_content_type,
                                                       old_pk=order.customer.user_id).first()
                if not new_user_id:
                    self.problem_users.add(order.id)
                    continue
                user = User.objects.get(pk=new_user_id.new_pk)
                stockrecord = product.stockrecords.filter(price_currency=settings.OSCAR_DEFAULT_CURRENCY).first()
                if order.address:
                    new_address_id = Transient.objects.filter(content_type=address_content_type,
                                                              old_pk=order.address_id).first()

                    user_address = UserAddress.objects.get(pk=new_address_id.new_pk)
                    address = ShippingAddress()
                    user_address.populate_alternative_model(address)
                    address.save()
                else:
                    address = ShippingAddress()
                    user_address = UserAddress.objects.filter(user=user).first()
                    user_address.populate_alternative_model(address)
                    address.save()

                # Find the right status for the order.
                # If the status is active and its a recurring order, it should be pending.
                if order.status == 'AC' and order.recurrent:
                    status = settings.ORDER_STATUS_PENDING
                else:
                    status = STATUS_CHOICES[order.status]

                # Create the order.
                new_order = Order(
                    user=user,
                    number=order.id,
                    total_incl_tax=order.amount,
                    total_excl_tax=order.amount,
                    shipping_incl_tax=0,
                    shipping_excl_tax=0,
                    shipping_address=address,
                    shipping_method='singpost',
                    shipping_code='singpost',
                    status=status,
                    date_placed=timezone.localtime(order.date),
                    currency=settings.OSCAR_DEFAULT_CURRENCY,
                )
                new_order.save()
                Transient.objects.create(
                    content_type=order_content_type,
                    old_pk=order.id,
                    new_pk=new_order.pk,
                    old_model=order.__class__.__name__
                )
                # Create the order line
                stockrecord = stockrecord if product else None
                line = Line(
                    order=new_order,
                    partner=Partner.objects.order_by('pk').first(),
                    partner_name='Hook Coffee',
                    partner_sku=stockrecord.partner_sku,
                    stockrecord=stockrecord,
                    product=product if product else None,
                    title=product.get_title() if product else order.coffee.name,
                    quantity=1,
                    line_price_incl_tax=order.amount,
                    line_price_excl_tax=order.amount,
                    line_price_before_discounts_incl_tax=order.amount,
                    line_price_before_discounts_excl_tax=order.amount,
                    unit_cost_price=order.amount,
                    unit_price_incl_tax=order.amount,
                    unit_price_excl_tax=order.amount,
                    unit_retail_price=order.amount,
                    status=status,
                    est_dispatch_date=timezone.localtime(order.shipping_date).date()
                )
                line.save()
                # Add the line price.
                LinePrice.objects.create(
                    order=new_order,
                    line=line,
                    quantity=line.quantity,
                    price_incl_tax=line.line_price_incl_tax,
                    price_excl_tax=line.line_price_excl_tax,
                    shipping_incl_tax=0,
                    shipping_excl_tax=0
                )

                # Add the line attribute.
                LineAttribute.objects.create(
                    type='brewing-method',
                    line=line,
                    value=BREW_MAP[order.brew_id],
                    option=brewing_method_option
                )

            print('========================================================================')
            print(crayons.red('There are no users for the below orders'))
            print(set(self.problem_users))
            print(crayons.red('There are problems with these orders product'))
            print(self.problem_products)
            print(crayons.red('There are problems with these orders data'))
            print(self.problem_orders)
            print('========================================================================')
