# -*- coding: utf-8 -*-

from __future__ import absolute_import

from django.db.models import Q
from django.contrib import messages
from django.http import HttpResponse

from django.forms.models import modelform_factory
from django.utils.translation import ugettext as _
from django.contrib.auth.decorators import permission_required
from django.shortcuts import render, redirect, get_object_or_404

from servo.lib.utils import paginate

from servo.models.note import Note
from servo.models.order import Order
from servo.models.common import Property
from servo.models.customer import Customer, CustomerGroup, ContactInfo

from servo.forms.customer import (CustomerForm,
                                  CustomerSearchForm,
                                  CustomerUploadForm)

GroupForm = modelform_factory(CustomerGroup, exclude=[])


def prepare_view(request, group='all'):

    title = _("Customers")

    customer_list = []
    search_hint = "customers"
    all_customers = Customer.objects.all().order_by('name')
    customer_count = all_customers.count()

    if request.session.get("return_to"):
        del(request.session['return_to'])

    if request.method == 'POST':
        q = request.POST.get('query')
        if q is not None:
            try:
                (key, value) = q.split('=')
                # allow searching customers by arbitrary key/values
                customer_list = Customer.objects.filter(**{key: value.strip()})
            except Exception:
                customer_list = Customer.objects.filter(name__icontains=q)
    else:
        if group == 'all':
            customer_list = all_customers
        else:
            g = CustomerGroup.objects.get(slug=group)
            customer_list = all_customers.filter(groups=g)
            title = g.name

    groups = CustomerGroup.objects.all()

    page = request.GET.get('page')
    customers = paginate(customer_list, page, 40)

    return locals()


def index(request, group='all'):
    data = prepare_view(request, group)
    request.session['customer_query'] = None

    if data['customer_list']:
        customer = data['customer_list'][0]
        return redirect(view, pk=customer.pk, group=group)

    return render(request, "customers/index.html", data)


@permission_required("servo.change_order")
def add_order(request, customer_id, order_id):
    order = get_object_or_404(Order, pk=order_id)
    customer = get_object_or_404(Customer, pk=customer_id)
    order.customer = customer
    order.save()

    for d in order.devices.all():
        customer.devices.add(d)

    customer.save()
    messages.success(request, _('Customer added'))
    return redirect(order)


def notes(request, pk, note_id=None):
    from servo.forms.note import NoteForm
    customer = get_object_or_404(Customer, pk=pk)
    form = NoteForm(initial={'recipient': customer.name})

    return render(request, "notes/form.html", {'form': form})


def view(request, pk, group='all'):
    c = get_object_or_404(Customer, pk=pk)
    data = prepare_view(request, group)

    data['title'] = c.name
    data['orders'] = Order.objects.filter(
        customer__lft__gte=c.lft,
        customer__rght__lte=c.rght,
        customer__tree_id=c.tree_id
    )

    if c.email:
        data['notes'] = Note.objects.filter(recipient=c.email)

    data['customer'] = c
    request.session['return_to'] = request.path

    return render(request, 'customers/view.html', data)


@permission_required("servo.change_customer")
def edit_group(request, group='all'):
    if group == 'all':
        group = CustomerGroup()
    else:
        group = CustomerGroup.objects.get(slug=group)

    title = group.name
    form = GroupForm(instance=group)

    if request.method == "POST":
        form = GroupForm(request.POST, instance=group)
        if form.is_valid():
            group = form.save()
            messages.success(request, _(u'%s saved') % group.name)
            return redirect(index, group.slug)
        messages.error(request, form.errors['name'][0])
        return redirect(index)

    return render(request, "customers/edit_group.html", locals())


@permission_required("servo.change_customer")
def delete_group(request, group):
    group = get_object_or_404(CustomerGroup, slug=group)

    if request.method == "POST":
        group.delete()
        messages.success(request, _(u'%s deleted') % group.name)
        return redirect(index)

    return render(request, "customers/delete_group.html", locals())


@permission_required("servo.change_customer")
def edit(request, pk=None, parent_id=None, group='all'):

    data = prepare_view(request, group)

    customer = Customer()
    form = CustomerForm(instance=customer)

    if group != 'all':
        g = CustomerGroup.objects.get(slug=group)
        form.initial = {'groups': [g]}

    name = request.GET.get('name')

    if name:
        form = CustomerForm(initial={'name': name})

    if pk is not None:
        customer = get_object_or_404(Customer, pk=pk)
        form = CustomerForm(instance=customer)

    if parent_id is not None:
        customer.parent = Customer.objects.get(pk=parent_id)
        form = CustomerForm(initial={'parent': parent_id})

    if request.method == 'POST':
        props = dict()
        keys = request.POST.getlist('keys')
        values = request.POST.getlist('values')

        form = CustomerForm(request.POST, request.FILES, instance=customer)

        if form.is_valid():
            ContactInfo.objects.filter(customer=customer).delete()

            for k, v in enumerate(values):
                if v != '':
                    key = keys[k]
                    props[key] = v

            if form.is_valid():
                try:
                    customer = form.save()
                except Exception as e:
                    messages.error(request, e)
                    return redirect(edit, group, pk)

                for k, v in props.items():
                    if v != '':
                        ContactInfo.objects.create(key=k, value=v, customer=customer)

                messages.success(request, _('Customer saved'))

                if request.session.get('return_to'):
                    return_to = request.session['return_to']
                    if hasattr(return_to, 'set_customer'):
                        return_to.set_customer(customer)
                    del request.session['return_to']
                    return redirect(return_to)

                return redirect(view, pk=customer.pk, group=group)

    data['form'] = form
    data['customer'] = customer
    data['title'] = customer.name
    data['fields'] = Property.objects.filter(type='customer')

    return render(request, 'customers/form.html', data)


@permission_required("servo.delete_customer")
def delete(request, pk=None, group='all'):

    customer = get_object_or_404(Customer, pk=pk)

    if request.method == "POST":
        customer.delete()
        messages.success(request, _("Customer deleted"))
        return redirect(index, group=group)
    else:
        data = {'action': request.path, 'customer': customer}
        return render(request, "customers/remove.html", data)


@permission_required("servo.change_customer")
def merge(request, pk, target=None):
    """
    Merges customer PK with customer TARGET
    Re-links everything from customer PK to TARGET:
    - orders
    - devices
    - invoices
    Deletes the source customer
    """
    customer = get_object_or_404(Customer, pk=pk)
    title = _('Merge %s with') % customer.name

    if request.method == 'POST':
        name = request.POST.get('name')
        results = Customer.objects.filter(name__icontains=name)
        return render(request, 'customers/results-merge.html', locals())

    if pk and target:
        target_customer = Customer.objects.get(pk=target)
        target_customer.orders.add(*customer.orders.all())
        target_customer.devices.add(*customer.devices.all())
        target_customer.note_set.add(*customer.note_set.all())
        target_customer.invoice_set.add(*customer.invoice_set.all())
        target_customer.save()
        customer.delete()
        messages.success(request, _('Customer records merged succesfully'))
        return redirect(target_customer)

    return render(request, "customers/merge.html", locals())


@permission_required("servo.change_customer")
def move(request, pk, new_parent=None):
    """
    Moves a customer under another customer
    """
    customer = get_object_or_404(Customer, pk=pk)

    if new_parent is not None:
        if int(new_parent) == 0:
            new_parent = None
            msg = _(u"Customer %s moved to top level") % customer
        else:
            new_parent = Customer.objects.get(pk=new_parent)
            d = {'customer': customer, 'target': new_parent}
            msg = _(u"Customer %(customer)s moved to %(target)s") % d

        try:
            customer.move_to(new_parent)
            customer.save() # To update fullname
            messages.success(request, msg)
        except Exception as e:
            messages.error(request, e)

        return redirect(customer)

    return render(request, "customers/move.html", locals())


def filter(request):
    """
    Search for customers by name
    May return JSON for ajax requests
    or a rendered list
    """
    import json
    from django.http import HttpResponse

    if request.method == "GET":
        results = list()
        query = request.GET.get("query")
        customers = Customer.objects.filter(fullname__icontains=query)

        for c in customers:
            results.append(u"%s <%s>" % (c.name, c.email))
            results.append(u"%s <%s>" % (c.name, c.phone))
    else:
        query = request.POST.get("name")
        results = Customer.objects.filter(fullname__icontains=query)
        data = {'results': results, 'id': request.POST['id']}

        return render(request, "customers/search-results.html", data)

    return HttpResponse(json.dumps(results), content_type="application/json")


def find(request):
    """
    Search from customer advanced search
    """
    results = list()
    request.session['customer_list'] = list()

    if request.method == 'POST':
        form = CustomerSearchForm(request.POST)

        if form.is_valid():
            d = form.cleaned_data
            checkin_start = d.pop('checked_in_start')
            checkin_end = d.pop('checked_in_end')

            if checkin_start and checkin_end:
                d['orders__created_at__range'] = [checkin_start.isoformat(), 
                                                  checkin_end.isoformat()]

            results = Customer.objects.filter(**d).distinct()
            request.session['customer_query'] = d
    else:
        form = CustomerSearchForm()

    title = _('Search for customers')

    page = request.GET.get('page')
    customers = paginate(results, page, 50)

    return render(request, "customers/find.html", locals())


def download(request, format='csv', group='all'):
    """
    Downloads all customers or search results
    """
    filename = 'customers'
    results = Customer.objects.all()
    query = request.session.get('customer_query')

    response = HttpResponse(content_type="text/plain; charset=utf-8")
    response['Content-Disposition'] = 'attachment; filename="%s.txt"' % filename
    response.write(u"ID\tNAME\tEMAIL\tPHONE\tADDRESS\tPOSTAL CODE\tCITY\tCOUNTRY\tNOTES\n")

    if group != 'all':
        results = results.filter(groups__slug=group)

    if query:
        results = Customer.objects.filter(**query).distinct()

    for c in results:
        row = u"%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n" % (c.pk,
                                                         c.name,
                                                         c.email,
                                                         c.phone,
                                                         c.street_address,
                                                         c.zip_code,
                                                         c.city,
                                                         c.country,
                                                         c.notes,)
        response.write(row)

    return response


def create_message(request, pk):
    return redirect("servo.views.note.edit", customer=pk)


def upload(request, group='all'):
    """
    Uploads customer data in CSV format
    """
    action = request.path
    form = CustomerUploadForm()

    if request.method == 'POST':
        form = CustomerUploadForm(request.POST, request.FILES)

        if not form.is_valid():
            messages.error(request, form.errors)
            return redirect(index)

        i, df = 0, form.cleaned_data['datafile'].read()

        for l in df.split("\r"):
            row = force_decode(l).strip().split("\t")

            if len(row) < 5:
                messages.error(request, _("Invalid upload data"))
                return redirect(index)

            if form.cleaned_data.get('skip_dups'):
                if Customer.objects.filter(email=row[1]).exists():
                    continue

            c = Customer(name=row[0], email=row[1])
            c.street_address = row[2]
            c.zip_code = row[3]
            c.city = row[4]
            c.notes = row[5]
            c.save()

            if group != 'all':
                g = CustomerGroup.objects.get(slug=group)
                c.groups.add(g)

            i += 1

        messages.success(request, _("%d customer(s) imported") % i)
        return redirect(index, group=group)

    return render(request, "customers/upload.html", locals())


def force_decode(s, codecs=['mac_roman', 'utf-8', 'latin-1']):
    for i in codecs:
        try:
            return s.decode(i)
        except UnicodeDecodeError:
            pass