# sales/views/pos.py
import logging
import json
import csv
import string
from datetime import datetime, timedelta
from decimal import Decimal

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, BranchStock, VariantBranchStock, ProductVariant
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 companies.permissions import company_feature_required
from sales.models import (
    POSOrder, POSOrderItem, PaymentTransaction,
    DailySalesSummary, UnusualTransaction, Customer,
)
from sales.forms import POSOrderForm, POSOrderItemForm

logger = logging.getLogger(__name__)


@login_required
def pos_sales(request):
    """Main POS sales screen with product selection dropdown — scoped to company."""
    company = getattr(request, 'company', None)
    plan = getattr(company, 'plan', None) if company else None

    if not company:
        return redirect('company_inactive')

    # ── 1. Feature/settings from company ───────────────────────────────────────
    company_settings = getattr(company, 'settings', None)
    barcode_scanner_enabled = getattr(company_settings, 'enable_camera_scanner', False) if company_settings else False
    unlimited_stock = getattr(company_settings, 'enable_unlimited_stock', False) if company_settings else False

    # ── 2. Get active products scoped to company ───────────────────────────────
    from django.db.models import Subquery, OuterRef, IntegerField, F
    from django.db.models.functions import Coalesce
    from inventory.models import BranchStock
    
    user_branch = getattr(request.user, "branch", None)
    products_qs = Product.objects.filter(company=company, status="active", is_hidden=False)
    
    if user_branch:
        branch_stock_sq = BranchStock.objects.filter(
            product=OuterRef('pk'),
            branch=user_branch
        ).values('quantity')[:1]
        products_qs = products_qs.annotate(
            local_stock=Coalesce(Subquery(branch_stock_sq, output_field=IntegerField()), 0)
        )
    else:
        products_qs = products_qs.annotate(local_stock=F('stock_quantity'))

    products = products_qs.order_by("name").values(
        "id", "name", "sku", "barcode", "price", "local_stock",
        "wholesale_price", "wholesale_min_quantity"
    )
    # Re-map local_stock to stock_quantity for template compatibility
    products_list = list(products)
    for p in products_list:
        p["stock_quantity"] = p.pop("local_stock")
    products = products_list

    logger.debug(f"DEBUG POS: Found {len(products)} active products for company={company}")

    # ── 3. Ensure there is a current order in session ──────────────────────────
    order = None
    order_id = request.session.get("current_order_id")

    if order_id:
        try:
            order = POSOrder.objects.get(id=order_id, company=company)
            logger.debug(f"DEBUG POS: Using existing order id={order.id}, number={order.order_number}")
        except POSOrder.DoesNotExist:
            logger.debug(f"DEBUG POS: Invalid order id in session ({order_id}); creating new order")
            order = None

    if order is None:
        user_branch = getattr(request.user, "branch", None)
        if user_branch is None and company:
            user_branch = Branch.objects.filter(company=company, is_active=True).first()

        order = POSOrder.objects.create(
            company=company,
            order_number=f"POS-{timezone.now().strftime('%m%d%H%M')}-{''.join(__import__('random').choices(__import__('string').ascii_uppercase+__import__('string').digits,k=5))}",
            total_amount=0,
            tax_amount=0,
            final_amount=0,
            cashier=request.user.username,
            payment_method="cash",
            branch=user_branch,
        )
        request.session["current_order_id"] = order.id
        logger.debug(f"DEBUG POS: Created new order id={order.id}, number={order.order_number}")

    # ── 4. Handle POST actions ─────────────────────────────────────────────────
    if request.method == "POST":
        action = (request.POST.get("action") or "").strip()
        logger.debug(f"DEBUG POS: POST action={action}")

        if action == "add_item":
            selected_sku = (request.POST.get("selected_product") or "").strip()
            try:
                raw_qty = request.POST.get("quantity") or "1"
                quantity = Decimal(str(raw_qty)).quantize(Decimal('0.0001'))
                if quantity <= Decimal('0'):
                    quantity = Decimal('1')
            except Exception:
                quantity = Decimal('1')

            if not selected_sku:
                messages.error(request, "Please select a product.")
                return redirect("sales:pos")

            ub = getattr(request.user, "branch", None)

            # ── Resolve: variant first (barcode or SKU), then base product ──
            variant = ProductVariant.objects.filter(
                company=company, is_active=True, product__is_hidden=False,
            ).filter(Q(barcode=selected_sku) | Q(sku=selected_sku)).select_related('product').first()

            if variant:
                product = variant.product
                if product.status != 'active':
                    messages.error(request, f"Product {product.name} is not active.")
                    return redirect("sales:pos")
                unit_price = variant.effective_price
                item_label = f"{product.name} ({variant.display_label})"

                # Stock check for variant
                if not unlimited_stock:
                    if ub:
                        vbs, _ = VariantBranchStock.objects.get_or_create(
                            company=company, variant=variant, branch=ub, defaults={'quantity': 0}
                        )
                        avail = Decimal(str(vbs.quantity))
                    else:
                        avail = Decimal(str(variant.stock_quantity))
                    if avail < quantity:
                        messages.error(request, f"Insufficient stock for {item_label}. Available: {avail}")
                        return redirect("sales:pos")
            else:
                product_filter = {"status": "active", "is_hidden": False}
                if company:
                    product_filter["company"] = company
                product = Product.objects.filter(
                    **product_filter
                ).filter(Q(sku=selected_sku) | Q(barcode=selected_sku)).first()
                if not product:
                    messages.error(request, f"Product not found with SKU/barcode: {selected_sku}")
                    return redirect("sales:pos")

                variant = None
                unit_price = product.price
                item_label = product.name

                if not unlimited_stock:
                    if ub:
                        bs, _ = BranchStock.objects.get_or_create(
                            company=company, product=product, branch=ub, defaults={'quantity': 0}
                        )
                        avail = Decimal(str(bs.quantity))
                    else:
                        avail = Decimal(str(product.stock_quantity))
                    if avail < quantity:
                        messages.error(request, f"Insufficient stock for {item_label}. Available: {avail}")
                        return redirect("sales:pos")

            # Wholesale price override (product-level only)
            if (not variant and product.wholesale_price and product.wholesale_min_quantity
                    and product.wholesale_min_quantity > 0
                    and quantity >= product.wholesale_min_quantity):
                unit_price = product.wholesale_price

            # ── Find/create cart line item ───────────────────────────────────
            qs = POSOrderItem.objects.filter(order=order, product=product)
            if variant:
                qs = qs.filter(variant_id=variant.id)
            else:
                qs = qs.filter(variant_id__isnull=True)
            existing_item = qs.first()

            if existing_item:
                new_qty = existing_item.quantity + quantity
                if not unlimited_stock:
                    if variant:
                        avail_check = (vbs.quantity if ub else variant.stock_quantity) if 'vbs' in dir() else variant.stock_quantity
                    else:
                        avail_check = (bs.quantity if ub else product.stock_quantity) if 'bs' in dir() else product.stock_quantity
                    if new_qty > avail_check + existing_item.quantity:
                        messages.error(request, f"Insufficient stock for {item_label}. Available: {avail_check}")
                        return redirect("sales:pos")

                # Recalc wholesale on combined qty
                if (not variant and product.wholesale_price and product.wholesale_min_quantity
                        and product.wholesale_min_quantity > 0 and new_qty >= product.wholesale_min_quantity):
                    unit_price = product.wholesale_price

                existing_item.quantity   = new_qty
                existing_item.unit_price = unit_price
                existing_item.total_price = float(unit_price) * new_qty
                existing_item.save()
                ws_msg = " (wholesale price applied!)" if not variant and unit_price != product.price else ""
                messages.success(request, f"Added {quantity} more {item_label} to cart{ws_msg}")
            else:
                POSOrderItem.objects.create(
                    order=order, product=product,
                    quantity=quantity, unit_price=unit_price,
                    total_price=float(unit_price) * quantity,
                    variant_id=variant.id if variant else None,
                )
                ws_msg = " (wholesale price applied!)" if not variant and unit_price != product.price else ""
                messages.success(request, f"Added {quantity} {item_label} to cart{ws_msg}")

            # ── Deduct stock ─────────────────────────────────────────────────
            if not unlimited_stock:
                if variant:
                    if ub:
                        vbs, _ = VariantBranchStock.objects.get_or_create(
                            company=company, variant=variant, branch=ub, defaults={'quantity': 0}
                        )
                        vbs.quantity = max(Decimal('0'), Decimal(str(vbs.quantity)) - quantity)
                        vbs.save()
                    else:
                        variant.stock_quantity = max(Decimal('0'), Decimal(str(variant.stock_quantity)) - quantity)
                        variant.save()
                else:
                    if ub:
                        bs, _ = BranchStock.objects.get_or_create(
                            company=company, product=product, branch=ub, defaults={'quantity': 0}
                        )
                        bs.quantity = max(Decimal('0'), Decimal(str(bs.quantity)) - quantity)
                        bs.save()
                    else:
                        product.stock_quantity = max(Decimal('0'), Decimal(str(product.stock_quantity)) - quantity)
                        product.save()

            return redirect("sales:pos")

        elif action == "remove_item":
            item_id = request.POST.get("item_id")
            try:
                order_item = get_object_or_404(POSOrderItem, id=item_id, order=order)
                if not unlimited_stock:
                    from inventory.models import BranchStock
                    ub = getattr(request.user, "branch", None)
                    if ub:
                        bs, _ = BranchStock.objects.get_or_create(company=company, product=order_item.product, branch=ub, defaults={'quantity': 0})
                        bs.quantity += order_item.quantity
                        bs.save()
                    else:
                        order_item.product.stock_quantity += order_item.quantity
                        order_item.product.save()
                order_item.delete()
                messages.success(request, "Item removed from cart")
            except Exception as exc:
                messages.error(request, "Error removing item")
            return redirect("sales:pos")

        elif action == "clear_order":
            for item in order.items.all():
                if not unlimited_stock:
                    from inventory.models import BranchStock
                    ub = getattr(request.user, "branch", None)
                    if ub:
                        bs, _ = BranchStock.objects.get_or_create(company=company, product=item.product, branch=ub, defaults={'quantity': 0})
                        bs.quantity += item.quantity
                        bs.save()
                    else:
                        item.product.stock_quantity += item.quantity
                        item.product.save()
            order.delete()
            if "current_order_id" in request.session:
                del request.session["current_order_id"]
            messages.info(request, "Current order cleared")
            return redirect("sales:pos")

        elif action == "complete_order":
            try:
                check_limit_or_block("orders_per_day", request=request)
            except PermissionDenied as e:
                messages.error(request, str(e))
                return redirect("sales:pos")

            is_split = request.POST.get("is_split_payment") == "1"
            payment_method = request.POST.get("payment_method", "cash")
            reference_number = request.POST.get("reference_number", "")
            customer_name = request.POST.get("customer_name", "")
            customer_phone = request.POST.get("customer_phone", "")

            order_items = POSOrderItem.objects.filter(order=order).select_related("product")
            subtotal = sum(item.total_price for item in order_items)
            vat_rate = Decimal(str(getattr(company_settings, 'vat_rate', 0) or 0))
            tax_amount = (subtotal * vat_rate / 100).quantize(Decimal('0.01'))
            final_total = subtotal + tax_amount

            order.total_amount = subtotal
            order.tax_amount = tax_amount
            order.final_amount = final_total
            order.amount_paid = final_total
            order.balance_amount = Decimal("0.00")
            order.payment_status = "full"
            order.customer_name = customer_name
            order.customer_phone = customer_phone
            order.status = "completed"

            if is_split:
                split_method_1 = request.POST.get("split_method_1", "cash")
                split_method_2 = request.POST.get("split_method_2", "pos")
                try:
                    split_amount_1 = Decimal(str(request.POST.get("split_amount_1", "0")))
                except Exception:
                    split_amount_1 = Decimal("0")
                try:
                    split_amount_2 = Decimal(str(request.POST.get("split_amount_2", "0")))
                except Exception:
                    split_amount_2 = Decimal("0")

                order.payment_method = "split"
                order.is_split_payment = True
                order.split_method_1 = split_method_1
                order.split_method_2 = split_method_2
                order.save()

                # Create one transaction per leg
                import uuid as _uuid_cls
                if split_amount_1 > 0:
                    PaymentTransaction.objects.create(
                        order=order, payment_method=split_method_1,
                        amount=split_amount_1,
                        reference_number=reference_number or f"SPLIT1-{_uuid_cls.uuid4().hex[:8].upper()}",
                        status="completed",
                    )
                if split_amount_2 > 0:
                    PaymentTransaction.objects.create(
                        order=order, payment_method=split_method_2,
                        amount=split_amount_2,
                        reference_number=reference_number or f"SPLIT2-{_uuid_cls.uuid4().hex[:8].upper()}",
                        status="completed",
                    )
            else:
                order.payment_method = payment_method
                order.is_split_payment = False
                order.save()
                if final_total > 0:
                    import uuid as _uuid_cls
                    PaymentTransaction.objects.create(
                        order=order, payment_method=payment_method,
                        amount=final_total,
                        reference_number=reference_number or f"POS-{_uuid_cls.uuid4().hex[:10].upper()}",
                        status="completed",
                    )

            if "current_order_id" in request.session:
                del request.session["current_order_id"]

            from sales.suspicious import detect_suspicious
            from accounts.audit import log_action
            log_action(request, 'complete_order', target=f'Order #{order.order_number}')
            detect_suspicious(order)

            # ── Generate Daily Summary ──
            DailySalesSummary.generate_summary_from_queryset(
                timezone.now().date(),
                POSOrder.objects.filter(company=request.company, created_at__date=timezone.now().date(), status='completed')
            )

            messages.success(request, f"Order #{order.order_number} completed successfully!")
            if request.POST.get('skip_receipt'):
                return redirect("sales:pos")
            return redirect("sales:print_receipt", order_id=order.id)

        else:
            return redirect("sales:pos")


    # ── 5. GET request: render POS ─────────────────────────────────────────────
    order_items = POSOrderItem.objects.filter(order=order).select_related("product")
    subtotal = sum(float(item.total_price) for item in order_items)
    vat_rate = float(getattr(company_settings, 'vat_rate', 0) or 0)
    tax_amount = round(subtotal * vat_rate / 100, 2)
    final_total = subtotal + tax_amount

    order.total_amount = subtotal
    order.tax_amount = tax_amount
    order.final_amount = final_total
    order.save()

    customers_qs = Customer.objects.order_by('name')
    if company:
        customers_qs = customers_qs.filter(company=company)

    context = {
        "order": order,
        "order_items": order_items,
        "subtotal": subtotal,
        "tax_amount": tax_amount,
        "vat_rate": vat_rate,
        "final_total": final_total,
        "products": products,
        "company_settings": company_settings,
        "unlimited_stock": unlimited_stock,
        "user_role": request.user.role,
        "user_name": request.user.get_full_name() or request.user.username,
        "barcode_scanner_enabled": barcode_scanner_enabled,
        "customers": customers_qs,
    }
    return render(request, "sales/pos_sales.html", context)


@login_required
@company_feature_required('feature_pos_terminal_v2_grid')
def pos_grid(request):
    """Smart Grid POS V2 — gated by plan.feature_pos_terminal_v2_grid."""
    company = getattr(request, 'company', None)

    company_settings = getattr(company, 'settings', None)
    unlimited_stock = getattr(company_settings, 'enable_unlimited_stock', False) if company_settings else False
    customers_qs = Customer.objects.order_by('name')
    if company:
        customers_qs = customers_qs.filter(company=company)

    preload_cart = None
    if 'grid_preload_cart' in request.session:
        # Convert to JSON string for safe template injection
        import json
        preload_cart = json.dumps(request.session.pop('grid_preload_cart'))

    vat_rate = float(getattr(company_settings, 'vat_rate', 0) or 0) if company_settings else 0

    context = {
        'user_role': request.user.role,
        'user_name': request.user.get_full_name() or request.user.username,
        'company_settings': company_settings,
        'customers': customers_qs,
        'unlimited_stock': unlimited_stock,
        'preload_cart_json': preload_cart,
        'vat_rate': vat_rate,
    }
    return render(request, 'sales/pos_grid.html', context)
