import time

from django.conf import settings
from django.forms.models import model_to_dict
from django.utils.translation import get_language

from saleboxdjango.models import CheckoutStore, UserAddress


class SaleboxCheckout:
    def __init__(self, request):
        self._init_sequence()
        self._init_session(request)
        self._write_session(request)

    def get_checkout_nav(self, curr_page_name=None):
        nav = {
            'order': [],
            'lookup': {},
            'previous': None,
            'next': None
        }

        for pos, s in enumerate(self.sequence['order']):
            accessible = curr_page_name is not None and self.sequence['lookup'][s]['accessible']
            current = s == curr_page_name

            # set previous / next item helpers
            if current:
                if pos > 0:
                    tmp = self.sequence['order'][pos - 1]
                    nav['previous'] = {
                        'accessible': self.sequence['lookup'][tmp]['accessible'],
                        'label': self.sequence['lookup'][tmp]['label'],
                        'page_name': tmp,
                        'path': self.sequence['lookup'][tmp]['path'],
                    }
                if pos < len(self.sequence['order']) - 1:
                    tmp = self.sequence['order'][pos + 1]
                    nav['next'] = {
                        'accessible': self.sequence['lookup'][tmp]['accessible'],
                        'label': self.sequence['lookup'][tmp]['label'],
                        'page_name': tmp,
                        'path': self.sequence['lookup'][tmp]['path'],
                    }

            # set lookup
            nav['lookup'][s] = {
                'accessible': accessible,
                'current': current,
            }

            # set order
            nav['order'].append({
                'accessible': accessible,
                'current': current,
                'label': self.sequence['lookup'][s]['label'],
                'page_name': s,
                'path': self.sequence['lookup'][s]['path'],
            })

        for pos, n in enumerate(nav['order']):
            try:
                nav['order'][pos]['previous_accessible'] = nav['order'][pos - 1]['accessible']
            except:
                nav['order'][pos]['previous_accessible'] = False
            try:
                nav['order'][pos]['next_accessible'] = nav['order'][pos + 1]['accessible']
            except:
                nav['order'][pos]['next_accessible'] = False

        return nav

    def get_raw_data(self):
        return self.data

    def get_last_accessible_page(self):
        for o in reversed(self.sequence['order']):
            if self.sequence['lookup'][o]['accessible']:
                return self.sequence['lookup'][o]['path']

        return None

    def get_current_page_path(self, path_name):
        return self.sequence['lookup'][path_name]['path']

    def get_next_page(self, page_name):
        next = self.sequence['order'].index(page_name) + 1
        if next < len(self.sequence['order']):
            return self.sequence['lookup'][
                self.sequence['order'][next]
            ]['path']
        else:
            return None

    def page_redirect(self, page_name):
        if page_name not in self.sequence['order']:
            raise Exception('Unrecognised SaleboxCheckout page_name: %s' % page_name)

        # if basket empty, redirect to the pre-checkout page, e.g.
        # typically the shopping basket
        if len(self.data['basket']) == 0:
            return settings.SALEBOX['CHECKOUT']['PRE_URL']

        # if this page is not marked accessible, i.e. the user is
        # trying to jump steps in the process, redirect them to
        # the 'last known good' page
        if not self.is_page_accessible(page_name):
            return self.get_last_accessible_page()

        return None

    def is_page_accessible(self, page_name):
        try:
            return self.sequence['lookup'][page_name]['accessible']
        except:
            return False

    def save_to_store(self, user, gateway_code='default', sent_to_gateway=True, visible_id=None):
        data = self.get_raw_data()
        cs = CheckoutStore(
            visible_id=visible_id,
            user=user.id if user else None,
            gateway_code=gateway_code,
            status=20 if sent_to_gateway else 10,
            data=self.get_raw_data()
        )
        cs.save()
        return cs

    def set_basket(self, basket, request, reset_completed=True, reset_checkout=True):
        if reset_checkout:
            self._init_data()
        if reset_completed:
            self.data['completed'] = []

        # populate data object
        self.data['basket'] = basket.get_raw_data()['basket']
        self._write_session(request)

        # return url to redirect to
        return self.sequence['lookup'][self.sequence['order'][0]]['path']

    def set_completed(self, page_name, request):
        self.data['completed'].append(page_name)
        self._update_sequence()
        self._write_session(request)
        return self.get_next_page(page_name)

    def set_customer_email(self, email, request):
        self.data['customer']['email'] = email
        self._write_session(request)

    def set_invoice_address(self, required, address_or_id, meta, request):
        if address_or_id is None:
            self.data['invoice_address']['address'] = None
        else:
            if isinstance(address_or_id, int):
                self.data['invoice_address']['address'] = model_to_dict(UserAddress.objects.get(id=address_or_id))
                self.data['invoice_address']['id'] = address_or_id
            else:
                self.data['invoice_address']['address'] = {
                    'default': address_or_id.default,
                    'address_group': address_or_id.address_group,
                    'full_name': address_or_id.full_name,
                    'address_1': address_or_id.address_1,
                    'address_2': address_or_id.address_2,
                    'address_3': address_or_id.address_3,
                    'address_4': address_or_id.address_4,
                    'address_5': address_or_id.address_5,
                    'country_state': model_to_dict(address_or_id.country_state) if address_or_id.country_state else None,
                    'country': model_to_dict(address_or_id.country) if address_or_id.country else None,
                    'id': None,
                    'postcode': address_or_id.postcode,
                    'phone_1': address_or_id.phone_1,
                    'phone_2': address_or_id.phone_2,
                    'email': address_or_id.email,
                    'string_1': address_or_id.string_1,
                    'string_2': address_or_id.string_2,
                    'tax_id': address_or_id.tax_id
                }
                self.data['invoice_address']['id'] = None

        self.data['invoice_address']['meta'] = meta
        self.data['invoice_address']['required'] = required
        self._write_session(request)

    def set_payment_method(self, id, meta, request):
        self.data['payment_method'] = {
            'id': int(id),
            'meta': meta
        }
        self._write_session(request)

    def set_shipping_address(self, required, address_or_id, meta, request):
        if isinstance(address_or_id, int):
            self.data['shipping_address']['address'] = model_to_dict(UserAddress.objects.get(id=address_or_id))
            self.data['shipping_address']['id'] = address_or_id
        else:
            self.data['shipping_address']['address'] = {
                'default': address_or_id.default,
                'address_group': address_or_id.address_group,
                'full_name': address_or_id.full_name,
                'address_1': address_or_id.address_1,
                'address_2': address_or_id.address_2,
                'address_3': address_or_id.address_3,
                'address_4': address_or_id.address_4,
                'address_5': address_or_id.address_5,
                'country_state': model_to_dict(address_or_id.country_state) if address_or_id.country_state else None,
                'country': model_to_dict(address_or_id.country) if address_or_id.country else None,
                'id': None,
                'postcode': address_or_id.postcode,
                'phone_1': address_or_id.phone_1,
                'phone_2': address_or_id.phone_2,
                'email': address_or_id.email,
                'string_1': address_or_id.string_1,
                'string_2': address_or_id.string_2,
                'tax_id': address_or_id.tax_id
            }
            self.data['shipping_address']['id'] = None

        self.data['shipping_address']['meta'] = meta
        self.data['shipping_address']['required'] = required
        self._write_session(request)

    def set_shipping_method(self, id, code, label, price, meta, request):
        self.data['shipping_method'] = {
            'code': code,
            'id': id,
            'label': label,
            'price': price,
            'meta': meta
        }
        self._write_session(request)

    def _init_data(self):
        self.data = {
            'basket': {},
            'completed': [],
            'customer': {
                'email': None
            },
            'data': {},
            'invoice_address': {
                'address': None,
                'id': None,
                'meta': None,
                'required': None,
            },
            'last_seen': int(time.time()),
            'payment_method': {
                'id': None,
                'meta': None,
            },
            'shipping_address': {
                'address': None,
                'id': None,
                'meta': None,
                'required': None,
            },
            'shipping_method': {
                'id': None,
                'label': None,
                'meta': None,
                'price': None,
            }
        }

    def _init_sequence(self):
        self.sequence = {
            'order': [],
            'lookup': {}
        }

        # use language url prefix? e.g. /en/checkout/address
        url_prefix = ''
        if settings.SALEBOX['CHECKOUT'].get('URL_LANGUAGE_PREFIX', False):
            url_prefix = '/%s' % get_language()

        for i, s in enumerate(settings.SALEBOX['CHECKOUT']['SEQUENCE']):
            self.sequence['order'].append(s[0])
            self.sequence['lookup'][s[0]] = {
                'label': s[2] if len(s) == 3 else s[0],
                'path': '%s%s' % (url_prefix, s[1]),
                'position': i,
                'complete': False,
                'accessible': i == 0
            }

    def _init_session(self, request):
        self._init_data()

        # attempt to import data from the session
        request.session.setdefault('saleboxcheckout', None)
        if request.session['saleboxcheckout'] is not None:
            tmp = request.session['saleboxcheckout']
            if int(time.time()) - tmp['last_seen'] < 60 * 60:  # 1 hr
                self.data = request.session['saleboxcheckout']

        # update data
        self._update_sequence()
        self.data['last_seen'] = int(time.time())

    def _update_sequence(self):
        for i, o in enumerate(self.sequence['order']):
            if o in self.data['completed']:
                self.sequence['lookup'][o]['complete'] = True
                self.sequence['lookup'][o]['accessible'] = True
                try:
                    self.sequence['lookup'][
                        self.sequence['order'][i + 1]
                    ]['accessible'] = True
                except:
                    pass

    def _write_session(self, request):
        if len(self.data['basket']) == 0:
            request.session['saleboxcheckout'] = None
        else:
            request.session['saleboxcheckout'] = self.data
