# sales/views/receipts.py
import logging
import json
import csv
import string
import base64
from datetime import datetime, timedelta
from decimal import Decimal
from io import BytesIO

from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse, HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.utils import timezone
from django.db import transaction
from django.db.models import Sum, Count, Q, F
from django.core.exceptions import PermissionDenied

from inventory.models import Product
from core.models import Branch
from core.utils import check_limit_or_block, filter_orders_by_user_branch
from accounts.decorators import role_required
from sales.models import (
    POSOrder, POSOrderItem, PaymentTransaction,
    DailySalesSummary, UnusualTransaction, Customer, Refund,
)
from sales.forms import POSOrderForm, POSOrderItemForm

logger = logging.getLogger(__name__)

BASE36_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz"


def _to_base36(num: int) -> str:
    """Convert positive int to base36 (same as JS toString(36))."""
    if num == 0:
        return "0"
    result = ""
    while num > 0:
        num, rem = divmod(num, 36)
        result = BASE36_ALPHABET[rem] + result
    return result


def _get_company_settings(request):
    """Helper: return CompanySettings for request.company, or None."""
    company = getattr(request, 'company', None)
    return getattr(company, 'settings', None)


def generate_verification_code(order: POSOrder, business_name: str = "") -> str:
    """
    Generate the same verification code as the JS on the receipt template.
    Raw string: order_number | created_at(YmdHis) | business_name
    Hash: 32-bit int, multiplied by 31 each step, base36, padded to 8, SP-XXXX-XXXX.
    """
    created_str = order.created_at.strftime("%Y%m%d%H%M%S")
    raw = f"{order.order_number}|{created_str}|{business_name}"

    h = 0
    for ch in raw:
        h = (h * 31 + ord(ch)) & 0xFFFFFFFF  # 32-bit

    code = _to_base36(h).upper()
    code = code.rjust(8, "0")[-8:]
    return f"SP-{code[:4]}-{code[4:]}"


def verify_order(request):
    """
    Public verification page for QR scans.
    URL: /sales/verify-order/?order=<order_number>
    """
    order_number = request.GET.get("order", "").strip()
    company = getattr(request, 'company', None)
    cs = getattr(company, 'settings', None)
    business_name = cs.business_name if cs else ""

    order = None
    verification_code = None
    is_valid = False

    if order_number and company:
        order = POSOrder.objects.filter(order_number=order_number, company=company).first()
        if order:
            verification_code = generate_verification_code(order, business_name)
            is_valid = True

    context = {
        "order_number": order_number,
        "order": order,
        "verification_code": verification_code,
        "is_valid": is_valid,
        "company_settings": cs,
    }
    return render(request, "sales/verify_order.html", context)


from rest_framework.authtoken.models import Token
from django.conf import settings


def print_receipt(request, order_id):
    """Print HTML receipt (with QR) for completed order"""
    if not request.user.is_authenticated:
        token_key = request.GET.get('token')
        if token_key:
            try:
                token = Token.objects.get(key=token_key)
                # Verify token owner belongs to this company
                token_user_company = getattr(token.user, 'company', None)
                if token_user_company is None or token_user_company != request.company:
                    return redirect(f"{settings.LOGIN_URL}?next={request.path}")
                request.user = token.user
            except Token.DoesNotExist:
                return redirect(f"{settings.LOGIN_URL}?next={request.path}")
        else:
            return redirect(f"{settings.LOGIN_URL}?next={request.path}")

    order = get_object_or_404(POSOrder, id=order_id, company=request.company)
    order_items = POSOrderItem.objects.filter(order=order).select_related('product')

    company = getattr(request, 'company', None)
    cs = getattr(company, 'settings', None)
    
    # --- Ensure cs is at least populated with company details if it's generic ---
    if company and cs:
        if not cs.business_name or cs.business_name in ["My Business", "Modern POS Business", "MODERN POS BUSINESS", "MODERN POS business"]:
            cs.business_name = company.name
        if not cs.business_address and company.address:
            cs.business_address = company.address
        if not cs.business_phone and company.phone:
            cs.business_phone = company.phone
    
    currency_symbol = cs.currency_symbol if cs else '₦'

    # --- Generate QR code for HTML receipt ---
    qr_code_b64 = None
    try:
        import qrcode
        from django.urls import reverse
        qr_url = reverse("sales:verify_order") + f"?order={order.order_number}"
        qr_data = request.build_absolute_uri(qr_url)
        qr = qrcode.QRCode(version=1, box_size=4, border=1)
        qr.add_data(qr_data)
        qr.make(fit=True)
        img = qr.make_image(fill_color="black", back_color="white")
        buffer = BytesIO()
        img.save(buffer, format="PNG")
        qr_code_b64 = base64.b64encode(buffer.getvalue()).decode("ascii")
    except Exception as e:
        logger.warning(f"QR generation error: {e}")

    context = {
        'order': order,
        'order_items': order_items,
        'system_settings': cs,
        'qr_code': qr_code_b64,
        'currency_symbol': currency_symbol,
        'user_role': request.user.role,
        'user_name': request.user.get_full_name() or request.user.username,
        'split_payments': order.split_payments if order.is_split_payment else None,
    }
    return render(request, 'sales/receipt.html', context)


@login_required
def repeat_sale(request, order_id):
    """
    Repeat a previous order: creates a new draft POS order pre-loaded with
    the same products (at their CURRENT prices) and deducts stock.
    Respects unlimited_stock setting.
    """
    import random, string as _string
    company = getattr(request, 'company', None)
    original_order = get_object_or_404(POSOrder, id=order_id, company=company)
    cs = getattr(company, 'settings', None)
    unlimited_stock = getattr(cs, 'enable_unlimited_stock', False) if cs else False

    pos_mode = getattr(cs, 'pos_interface_mode', 'classic') if cs else 'classic'
    
    if pos_mode == 'grid':
        cart_items = []
        for item in original_order.items.select_related('product').all():
            product = item.product
            if not product:
                continue
            cart_items.append({
                'id': product.id,
                'productId': product.id,
                'name': product.name,
                'price': float(product.price),
                'quantity': float(item.quantity),
                'stockQuantity': float(product.stock_quantity) if not unlimited_stock else 999999.0,
                'wholesalePrice': float(product.wholesale_price) if product.wholesale_price else None,
                'wholesaleMinQty': float(product.wholesale_min_quantity) if product.wholesale_min_quantity else 0.0,
                'basePrice': float(product.price)
            })
        request.session['grid_preload_cart'] = cart_items
        messages.success(request, f'Repeat order loaded {len(cart_items)} item(s) into cart.')
        return redirect('sales:pos_grid')

    suffix = ''.join(random.choices(_string.ascii_uppercase + _string.digits, k=5))
    new_order = POSOrder.objects.create(
        company=company,
        order_number=f"RPT-{timezone.now().strftime('%m%d%H%M')}-{suffix}",
        total_amount=0,
        final_amount=0,
        cashier=request.user.username,
        payment_method='cash',
        branch=original_order.branch,
    )

    items_added = 0
    for item in original_order.items.select_related('product').all():
        product = item.product
        if product is None:
            messages.warning(request, f'A product could not be found (it may have been deleted).')
            continue

        qty = item.quantity

        if not unlimited_stock and product.stock_quantity < qty:
            messages.warning(
                request,
                f'Insufficient stock for "{product.name}" (available: {product.stock_quantity}). Skipped.'
            )
            continue

        # Use CURRENT price, not original order price
        current_price = product.price
        # Apply wholesale if applicable
        if (product.wholesale_price and product.wholesale_min_quantity
                and product.wholesale_min_quantity > 0
                and qty >= product.wholesale_min_quantity):
            current_price = product.wholesale_price

        POSOrderItem.objects.create(
            order=new_order,
            product=product,
            product_name=product.name,
            quantity=qty,
            unit_price=current_price,
            total_price=current_price * qty,
        )

        if not unlimited_stock:
            product.stock_quantity -= qty
            product.save()

        items_added += 1

    # Recalculate totals
    order_items = POSOrderItem.objects.filter(order=new_order)
    subtotal = sum(item.total_price for item in order_items)
    new_order.total_amount = subtotal
    new_order.tax_amount = Decimal('0')
    new_order.final_amount = subtotal
    new_order.save()

    request.session['current_order_id'] = new_order.id
    if items_added:
        messages.success(request, f'Repeat order created with {items_added} item(s). Review & complete the order below.')
    else:
        messages.warning(request, 'No items could be added to repeat order (insufficient stock or deleted products).')
        
    return redirect('sales:pos')


@login_required
@role_required(['admin', 'manager'])
def reverse_order(request, order_id):
    """
    Reverse / Refund a completed order:
      1. Restore all product stock quantities.
      2. If customer had credit balance on this order, clear it.
      3. Change order status → 'cancelled'.
      4. Create a Refund record for audit trail.
      5. Log action in AuditLog.
    """
    company = getattr(request, 'company', None)
    order = get_object_or_404(POSOrder, id=order_id, company=company)
    cs = getattr(company, 'settings', None)
    unlimited_stock = getattr(cs, 'enable_unlimited_stock', False) if cs else False

    if order.status == 'cancelled':
        messages.warning(request, f'Order #{order.order_number} is already cancelled.')
        return redirect('sales:transaction_history')

    # Check if already refunded
    from sales.models import Refund
    if hasattr(order, 'refund'):
        messages.warning(request, f'Order #{order.order_number} has already been reversed.')
        return redirect('sales:transaction_history')

    reason = request.POST.get('reason', 'Reversed by staff') if request.method == 'POST' else ''

    if request.method == 'POST':
        with transaction.atomic():
            # 1. Restore stock
            stock_restored = False
            if not unlimited_stock:
                for item in order.items.select_related('product').all():
                    if item.product:
                        item.product.stock_quantity += item.quantity
                        item.product.save()
                stock_restored = True

            # 2. Clear customer credit balance if this was a credit order
            credit_adjusted = False
            if order.balance_amount > Decimal('0') and order.customer:
                order.amount_paid = order.final_amount
                order.balance_amount = Decimal('0')
                order.payment_status = 'full'
                credit_adjusted = True

            # 3. Cancel the order
            order.status = 'cancelled'
            order.save()

            # 4. Create Refund record
            Refund.objects.create(
                company=company,
                original_order=order,
                refunded_by=request.user.username,
                reason=reason or 'No reason provided',
                refund_amount=order.final_amount,
                stock_restored=stock_restored,
                credit_adjusted=credit_adjusted,
            )

            # 5. Audit log
            try:
                from accounts.audit import log_action
                log_action(request, 'reverse_order', target=order.order_number,
                           extra=f'Refund amount: {order.final_amount}')
            except Exception:
                pass

        messages.success(
            request,
            f'Order #{order.order_number} has been reversed. '
            f'Stock restored: {"Yes" if not unlimited_stock else "N/A (unlimited)"}. '
            f'Customer credit adjusted: {"Yes" if credit_adjusted else "No"}.'
        )
        return redirect('sales:transaction_history')

    # GET: show confirmation page
    context = {
        'order': order,
        'order_items': order.items.select_related('product').all(),
        'user_role': request.user.role,
        'user_name': request.user.get_full_name() or request.user.username,
    }
    return render(request, 'sales/reverse_order_confirm.html', context)


@login_required
@role_required(['admin', 'manager'])
def generate_receipt_with_qr(request, order_id):
    """Generate receipt PDF with QR code for verification"""
    try:
        import qrcode
        from reportlab.pdfgen import canvas
        from reportlab.lib.pagesizes import A4
        from reportlab.lib.colors import Color
    except ImportError:
        return HttpResponse("PDF generation libraries not installed.", status=500)

    order = get_object_or_404(POSOrder, id=order_id, company=request.company)
    order_items = POSOrderItem.objects.filter(order=order).select_related('product')

    company = getattr(request, 'company', None)
    cs = getattr(company, 'settings', None)
    biz_name = cs.business_name if cs else "MODERN POS"
    biz_address = cs.business_address if cs else ""
    receipt_footer = cs.receipt_footer if cs else "Thank you for your patronage!"

    from django.urls import reverse
    qr_url = reverse("sales:verify_order") + f"?order={order.order_number}"
    qr_data = request.build_absolute_uri(qr_url)
    qr = qrcode.QRCode(version=1, box_size=10, border=5)
    qr.add_data(qr_data)
    qr.make(fit=True)
    img = qr.make_image(fill_color="black", back_color="white")
    buf = BytesIO()
    img.save(buf, format='PNG')
    qr_image = buf.getvalue()

    buffer = BytesIO()
    p = canvas.Canvas(buffer, pagesize=A4)
    width, height = A4

    p.saveState()
    texture_color = Color(0.85, 0.85, 0.85, alpha=0.15)
    p.setFillColor(texture_color)
    p.setFont("Helvetica", 22)
    for y in range(0, int(height), 120):
        for x in range(0, int(width), 250):
            p.drawString(x, y, str(order.order_number))
    p.restoreState()

    p.setFont("Helvetica-Bold", 20)
    p.drawString(40, height - 60, biz_name)
    p.setFont("Helvetica", 11)
    p.drawString(40, height - 82, biz_address)
    p.setFont("Helvetica-Bold", 12)
    p.drawString(40, height - 110, f"ORDER RECEIPT - #{order.order_number}")
    p.setFont("Helvetica", 10)
    p.drawString(40, height - 130, f"Date: {order.created_at.strftime('%Y-%m-%d %H:%M')}")
    p.drawString(250, height - 130, f"Cashier: {order.cashier}")

    p.setFont("Helvetica-Bold", 12)
    y_position = height - 170
    p.drawString(40, y_position, "ITEMS PURCHASED")
    p.setFont("Helvetica", 10)
    y_position -= 20
    for item in order_items:
        p.drawString(40, y_position, f"{item.quantity} x {item.product.name}")
        p.drawRightString(400, y_position, f"₦{item.total_price}")
        y_position -= 16

    y_position -= 10
    p.line(40, y_position, 400, y_position)
    y_position -= 20
    p.setFont("Helvetica-Bold", 11)
    p.drawString(40, y_position, "Subtotal:")
    p.drawRightString(400, y_position, f"₦{order.total_amount}")
    y_position -= 18
    p.drawString(40, y_position, "Total Amount:")
    p.drawRightString(400, y_position, f"₦{order.final_amount}")
    y_position -= 18
    p.drawString(40, y_position, "Payment Method:")
    p.drawRightString(400, y_position, order.get_payment_method_display())

    if qr_image:
        p.setFont("Helvetica", 10)
        p.drawString(40, y_position - 40, "Scan to verify order:")
        p.drawImage(BytesIO(qr_image), 40, y_position - 140, width=110, height=110)

    p.setFont("Helvetica-Oblique", 10)
    p.drawString(40, 40, receipt_footer)
    p.showPage()
    p.save()

    buffer.seek(0)
    return HttpResponse(buffer.getvalue(), content_type='application/pdf')
