# -*- coding: utf-8 -*-
"""
Two views only:

  index()        renders the landing page exactly as it was written — the
                 photos and the copy stay static HTML.
  place_order()  receives the order form, validates it again on the server,
                 works out the money itself and saves an Order (plus one
                 OrderItem per book) that then shows up in the Django admin.
"""
import json
import re

from django.db import transaction
from django.http import JsonResponse
from django.shortcuts import render
from django.views.decorators.http import require_POST

from . import pricing
from .models import Order, OrderItem

# Mirrors BD_MOBILE_RE in index.html
BD_MOBILE_RE = re.compile(r'^(?:\+?88)?01[3-9]\d{8}$')

# Bengali digits ০-৯ -> 0-9
BN_DIGITS = {ord('০') + i: str(i) for i in range(10)}

# spaces, zero-width joiners, dashes, dots and brackets a customer may type
PHONE_JUNK_RE = re.compile(r'[\s ​‌‍\-().]')


def index(request):
    return render(request, 'index.html')


# --------------------------------------------------------------------------
# helpers
# --------------------------------------------------------------------------
def _bn_to_en(value):
    return str(value or '').translate(BN_DIGITS)


def _clean_phone(raw):
    """Same cleaning the page does before it validates a number."""
    s = PHONE_JUNK_RE.sub('', _bn_to_en(raw))
    s = re.sub(r'^00', '+', s)
    if s.find('+') > 0:
        s = s.replace('+', '')
    return s


def _normalise_msisdn(raw):
    """'8801XXXXXXXXX' (13 digits, no plus) or None when the number is wrong."""
    c = _clean_phone(raw)
    if not BD_MOBILE_RE.match(c):
        return None
    digits = c.lstrip('+')
    return digits if digits.startswith('88') else '88' + digits


def _squash(value, limit):
    return re.sub(r'\s+', ' ', str(value or '')).strip()[:limit]


def _client_ip(request):
    forwarded = request.META.get('HTTP_X_FORWARDED_FOR', '')
    if forwarded:
        return forwarded.split(',')[0].strip()
    return request.META.get('REMOTE_ADDR') or None


def _payload(request):
    """Read the order from a JSON body, falling back to a normal form POST."""
    if 'application/json' in request.META.get('CONTENT_TYPE', ''):
        try:
            data = json.loads(request.body.decode('utf-8') or '{}')
        except (ValueError, UnicodeDecodeError):
            return None
        return data if isinstance(data, dict) else None
    data = request.POST.dict()
    data['books'] = request.POST.getlist('books')
    return data


# --------------------------------------------------------------------------
# validation — the same rules, and the same Bangla messages, as the page
# --------------------------------------------------------------------------
def _validate(data):
    """Return (cleaned, errors). Error keys are the input ids used on the page."""
    errors = {}
    cleaned = {}

    # ---- which books ----
    raw_books = data.get('books')
    if not isinstance(raw_books, (list, tuple)):
        raw_books = [raw_books] if raw_books else []
    books = pricing.clean_slugs(raw_books)
    if not books:
        errors['books'] = 'কমপক্ষে একটি বই বেছে নিন।'
    cleaned['books'] = books

    name = _squash(data.get('name'), 60)
    if not name:
        errors['cust-name'] = 'আপনার নাম লিখুন।'
    elif len(name) < 2:
        errors['cust-name'] = 'আপনার নামটি লিখুন (কমপক্ষে ২ অক্ষর)।'
    cleaned['customer_name'] = name

    address = _squash(data.get('address'), 300)
    if not address:
        errors['cust-address'] = 'ডেলিভারির সম্পূর্ণ ঠিকানা লিখুন।'
    elif len(address) < 10:
        errors['cust-address'] = ('পূর্ণ ঠিকানা লিখুন — বাসা/রোড, এলাকা, থানা ও '
                                  'জেলা সহ (কমপক্ষে ১০ অক্ষর)।')
    cleaned['address'] = address

    phone_typed = _squash(data.get('phone'), 25)
    msisdn = _normalise_msisdn(phone_typed)
    if not phone_typed:
        errors['cust-phone'] = 'মোবাইল নম্বর লিখুন।'
    elif msisdn is None:
        errors['cust-phone'] = 'সঠিক মোবাইল নম্বর লিখুন, যেমন ০১৭XXXXXXXX।'
    cleaned['phone'] = msisdn or ''
    cleaned['phone_typed'] = phone_typed

    area = str(data.get('area') or '').strip()
    if area not in ('inside', 'outside'):
        errors['area'] = 'ডেলিভারি এলাকা নির্বাচন করুন।'
        area = 'outside'
    cleaned['delivery_area'] = area

    try:
        qty = int(_bn_to_en(data.get('qty')))
    except (TypeError, ValueError):
        qty = pricing.MIN_QTY
    cleaned['quantity'] = max(pricing.MIN_QTY, min(pricing.MAX_QTY, qty))

    # ---- free t-shirt: only asked for when the order actually earns it ----
    gift_size = _squash(data.get('gift_size'), 10)
    if pricing.gift_applies(books):
        if gift_size not in pricing.GIFT_SIZES:
            errors['giftSize'] = 'বাবুর কিডস টি-শার্টের সাইজটি বেছে নিন।'
            gift_size = ''
        cleaned['gift'] = pricing.GIFT
    else:
        gift_size = ''
        cleaned['gift'] = ''
    cleaned['gift_size'] = gift_size

    return cleaned, errors


# --------------------------------------------------------------------------
# the order endpoint
# --------------------------------------------------------------------------
@require_POST
def place_order(request):
    data = _payload(request)
    if data is None:
        return JsonResponse(
            {'ok': False, 'message': 'অর্ডারের তথ্য পড়া যায়নি। আবার চেষ্টা করুন।'},
            status=400)

    cleaned, errors = _validate(data)
    if errors:
        return JsonResponse(
            {'ok': False,
             'errors': errors,
             'message': 'কিছু তথ্য ঠিক করতে হবে — লাল লেখাগুলো দেখুন।'},
            status=400)

    books = cleaned['books']
    qty = cleaned['quantity']
    full_set = pricing.is_full_set(books)

    # Money is never read from the browser.
    money = pricing.quote(books, qty, cleaned['delivery_area'])

    if full_set:
        product_name = pricing.PRODUCT_NAME
        product_name_en = pricing.PRODUCT_NAME_EN
    else:
        product_name = 'আলাদা বই (%d টি)' % len(books)
        product_name_en = pricing.describe(books)

    with transaction.atomic():
        order = Order.objects.create(
            customer_name=cleaned['customer_name'],
            phone=cleaned['phone'],
            phone_typed=cleaned['phone_typed'],
            address=cleaned['address'],
            country='BD',
            is_full_set=full_set,
            books_count=len(books),
            books_summary=pricing.describe(books),
            product_name=product_name,
            product_name_en=product_name_en,
            product_sku=pricing.PRODUCT_SKU if full_set else '',
            quantity=qty,
            delivery_area=cleaned['delivery_area'],
            gift=cleaned['gift'],
            gift_size=cleaned['gift_size'],
            ip_address=_client_ip(request),
            user_agent=request.META.get('HTTP_USER_AGENT', '')[:300],
            **money
        )

        OrderItem.objects.bulk_create([
            OrderItem(
                order=order,
                book_slug=b['slug'],
                book_name=b['bn'],
                book_name_en=b['en'],
                pages=b['pages'],
                unit_price=pricing.book_price(b),
                quantity=qty,
                line_total=pricing.book_price(b) * qty,
            )
            for b in (pricing.BOOKS_BY_SLUG[s] for s in books)
        ])

    return JsonResponse({
        'ok': True,
        'order_no': order.order_no,
        'total': order.total,
        'delivery_charge': order.delivery_charge,
        'subtotal': order.subtotal,
        'is_full_set': order.is_full_set,
        'books_count': order.books_count,
        'message': 'অর্ডারটি আমরা পেয়েছি।',
    })
