from django.shortcuts import render

# Create your views here.
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from django.db import transaction
from django.db.models import Sum, F, Q
from django.utils import timezone
from accounts.branch_utils import (
    branch_required_response, get_active_branch, get_branch_context,
    resolve_write_branch, scope, scope_for_user,
)
from accounts.permissions import company_admin_required, admin_area_required
from django.http import JsonResponse
from .models import Brand, ProductCategory, ProductSize, Supplier, Product, Purchase, PurchaseItem, StockLog
from .forms import BrandForm, ProductCategoryForm, ProductSizeForm, SupplierForm, ProductForm, PurchaseForm, PurchaseItemFormSet
from .stock import (
    apply_stock_change, branch_products, set_branch_stock, stock_by_branch, stock_map, stock_summary,
)
import json
from decimal import Decimal


def gc(request):
    return request.user.company


def _prepare_purchase_formset(formset, company):
    products = Product.objects.filter(
        company=company, is_active=True
    ).select_related("brand", "category", "size")
    for form in formset.forms:
        form.fields["product"].queryset = products
    return products



@company_admin_required
def brand_list(request):
    return render(request, "inventory/brand_list.html", {"brands": Brand.objects.filter(company=gc(request))})


@company_admin_required
def brand_create(request):
    if request.method == "POST":
        form = BrandForm(request.POST)
        if form.is_valid():
            b = form.save(commit=False)
            b.company = gc(request)
            b.save()
            messages.success(request, "Brand created.")
            return redirect("brand_list")
    else:
        form = BrandForm()
    return render(request, "inventory/brand_form.html", {"form": form, "title": "Create Brand"})


@company_admin_required
def brand_update(request, pk):
    b = get_object_or_404(Brand, pk=pk, company=gc(request))
    if request.method == "POST":
        form = BrandForm(request.POST, instance=b)
        if form.is_valid():
            form.save()
            return redirect("brand_list")
    else:
        form = BrandForm(instance=b)
    return render(request, "inventory/brand_form.html", {"form": form, "title": "Update Brand"})


@company_admin_required
def brand_delete(request, pk):
    get_object_or_404(Brand, pk=pk, company=gc(request)).delete()
    return redirect("brand_list")


@company_admin_required
def category_list(request):
    return render(request, "inventory/category_list.html", {"categories": ProductCategory.objects.filter(company=gc(request))})


@company_admin_required
def category_create(request):
    if request.method == "POST":
        form = ProductCategoryForm(request.POST)
        if form.is_valid():
            c = form.save(commit=False)
            c.company = gc(request)
            c.save()
            return redirect("category_list")
    else:
        form = ProductCategoryForm()
    return render(request, "inventory/category_form.html", {"form": form, "title": "Create Category"})


@company_admin_required
def category_update(request, pk):
    c = get_object_or_404(ProductCategory, pk=pk, company=gc(request))
    if request.method == "POST":
        form = ProductCategoryForm(request.POST, instance=c)
        if form.is_valid():
            form.save()
            return redirect("category_list")
    else:
        form = ProductCategoryForm(instance=c)
    return render(request, "inventory/category_form.html", {"form": form, "title": "Update Category"})


@company_admin_required
def category_delete(request, pk):
    get_object_or_404(ProductCategory, pk=pk, company=gc(request)).delete()
    return redirect("category_list")


@company_admin_required
def size_list(request):
    return render(request, "inventory/size_list.html", {"sizes": ProductSize.objects.filter(company=gc(request))})


@company_admin_required
def size_create(request):
    if request.method == "POST":
        form = ProductSizeForm(request.POST)
        if form.is_valid():
            s = form.save(commit=False)
            s.company = gc(request)
            s.save()
            return redirect("size_list")
    else:
        form = ProductSizeForm()
    return render(request, "inventory/size_form.html", {"form": form, "title": "Create Size"})


@company_admin_required
def size_update(request, pk):
    s = get_object_or_404(ProductSize, pk=pk, company=gc(request))
    if request.method == "POST":
        form = ProductSizeForm(request.POST, instance=s)
        if form.is_valid():
            form.save()
            return redirect("size_list")
    else:
        form = ProductSizeForm(instance=s)
    return render(request, "inventory/size_form.html", {"form": form, "title": "Update Size"})


@company_admin_required
def size_delete(request, pk):
    get_object_or_404(ProductSize, pk=pk, company=gc(request)).delete()
    return redirect("size_list")


@company_admin_required
def supplier_list(request):
    return render(request, "inventory/supplier_list.html", {"suppliers": Supplier.objects.filter(company=gc(request))})


@company_admin_required
def supplier_create(request):
    if request.method == "POST":
        form = SupplierForm(request.POST)
        if form.is_valid():
            s = form.save(commit=False)
            s.company = gc(request)
            s.save()
            return redirect("supplier_list")
    else:
        form = SupplierForm()
    return render(request, "inventory/supplier_form.html", {"form": form, "title": "Create Supplier"})


@company_admin_required
def supplier_update(request, pk):
    s = get_object_or_404(Supplier, pk=pk, company=gc(request))
    if request.method == "POST":
        form = SupplierForm(request.POST, instance=s)
        if form.is_valid():
            form.save()
            return redirect("supplier_list")
    else:
        form = SupplierForm(instance=s)
    return render(request, "inventory/supplier_form.html", {"form": form, "title": "Update Supplier"})


@company_admin_required
def supplier_delete(request, pk):
    get_object_or_404(Supplier, pk=pk, company=gc(request)).delete()
    return redirect("supplier_list")


@company_admin_required
def product_list(request):
    company = gc(request)
    branch = get_active_branch(request)
    products = list(
        Product.objects.filter(company=company).select_related("brand", "category", "size")
    )
    # Stock shown = the selected branch's stock, or the company total for "All Branches".
    quantities = stock_map(company, branch)
    for p in products:
        p.branch_qty = quantities.get(p.pk, Decimal("0"))
    return render(request, "inventory/product_list.html", {"products": products})


def _product_form_kwargs(request, company):
    ctx = get_branch_context(request)
    return {"company": company, "branches": ctx.branches if ctx else []}


@company_admin_required
def product_create(request):
    company = gc(request)
    ctx = get_branch_context(request)
    default_branch = resolve_write_branch(request) or ctx.main
    if request.method == "POST":
        form = ProductForm(
            request.POST, company=company, branches=ctx.branches, stock_branch=default_branch,
        )
        if form.is_valid():
            opening_branch = (
                ctx.get_active_branch(form.cleaned_data.get("opening_branch")) or default_branch
            )
            opening = Decimal(form.cleaned_data.get("stock_qty") or 0)
            with transaction.atomic(using=company.db_alias):
                p = form.save(commit=False)
                p.company = company
                p.current_stock = Decimal("0")
                p.save()
                if opening > 0:
                    apply_stock_change(
                        company=company, product_id=p.pk, branch=opening_branch, delta=opening,
                        stock_type="adjustment", reference_id="Opening stock",
                    )
            messages.success(request, "Product created.")
            return redirect("product_list")
    else:
        form = ProductForm(company=company, branches=ctx.branches, stock_branch=default_branch)
    return render(request, "inventory/product_form.html", {"form": form, "title": "Create Product"})


@company_admin_required
def product_update(request, pk):
    company = gc(request)
    product = get_object_or_404(Product, pk=pk, company=company)
    # Stock is edited for ONE branch: the one selected in the top bar. With
    # "All Branches" (and more than one branch) the stock field is hidden.
    stock_branch = resolve_write_branch(request)
    stock_initial = None
    if stock_branch is not None:
        stock_initial = stock_map(company, stock_branch).get(product.pk, Decimal("0"))
    kwargs = dict(company=company, stock_branch=stock_branch, stock_initial=stock_initial)
    if request.method == "POST":
        form = ProductForm(request.POST, instance=product, **kwargs)
        if form.is_valid():
            with transaction.atomic(using=company.db_alias):
                form.instance.buy_price = product.buy_price
                form.save()
                if stock_branch is not None and "stock_qty" in form.cleaned_data:
                    set_branch_stock(
                        company=company, product_id=product.pk, branch=stock_branch,
                        quantity=form.cleaned_data["stock_qty"],
                        reference_id=f"Manual stock update ({stock_branch.name})",
                    )
            messages.success(request, "Product updated. Buy price unchanged (adjust stock via Purchase Entry).")
            return redirect("product_list")
    else:
        form = ProductForm(instance=product, **kwargs)
        form.fields["buy_price"].widget.attrs["readonly"] = True
    return render(request, "inventory/product_form.html", {"form": form, "title": "Update Product"})


@company_admin_required
def product_delete(request, pk):
    get_object_or_404(Product, pk=pk, company=gc(request)).delete()
    return redirect("product_list")


@admin_area_required
def purchase_list(request):
    branch = get_active_branch(request)
    purchases = scope(
        Purchase.objects.filter(company=gc(request)).select_related("supplier"), branch
    )
    return render(request, "inventory/purchase_list.html", {"purchases": purchases})


@admin_area_required
def purchase_create(request):
    company = gc(request)
    branch = resolve_write_branch(request, request.POST.get("branch") if request.method == "POST" else None)
    if branch is None:
        return branch_required_response(request, "New Purchase")

    if request.method == "POST":
        form = PurchaseForm(request.POST, company=company)
        formset = PurchaseItemFormSet(request.POST)
        _prepare_purchase_formset(formset, company)
        if form.is_valid() and formset.is_valid():
            with transaction.atomic(using=company.db_alias):
                purchase = form.save(commit=False)
                purchase.company = company
                purchase.branch_id = branch.pk
                purchase.created_by = request.user
                if not purchase.invoice_number:
                    count = Purchase.objects.filter(company=company).count() + 1
                    purchase.invoice_number = f"PUR-{timezone.now().strftime('%Y%m%d')}-{count:04d}"
                purchase.save()
                total_amount = 0
                for item_form in formset:
                    if item_form.cleaned_data and not item_form.cleaned_data.get("DELETE"):
                        item = item_form.save(commit=False)
                        item.purchase = purchase
                        item.total = item.quantity * item.buy_price
                        item.save()
                        total_amount += item.total
                        # Stock goes into THIS branch; the buy price becomes the
                        # product's latest cost.
                        apply_stock_change(
                            company=company,
                            product_id=item.product_id,
                            branch=branch,
                            delta=item.quantity,
                            stock_type="purchase",
                            reference_id=purchase.invoice_number,
                            new_buy_price=item.buy_price,
                        )
                purchase.total_amount = total_amount
                purchase.net_amount = total_amount - purchase.discount
                purchase.save()
            messages.success(request, f"Purchase created for {branch.name}.")
            return redirect("purchase_list")
    else:
        count = Purchase.objects.filter(company=company).count() + 1
        form = PurchaseForm(initial={
            "date": timezone.now().date(),
            "invoice_number": f"PUR-{timezone.now().strftime('%Y%m%d')}-{count:04d}",
        }, company=company)
        formset = PurchaseItemFormSet()
        _prepare_purchase_formset(formset, company)
    products_json = json.dumps([
        {"id": str(p.id), "name": str(p.name),
         "buy_price": float(p.buy_price), "stock": float(p.branch_qty)}
        for p in branch_products(company, branch)
    ])
    return render(request, "inventory/purchase_form.html", {
        "form": form, "formset": formset, "write_branch": branch,
        "products_json": products_json, "title": "Create Purchase"
    })


@admin_area_required
def purchase_detail(request, pk):
    purchase = get_object_or_404(
        scope_for_user(request, Purchase.objects.filter(company=gc(request))), pk=pk
    )
    items = purchase.items.select_related("product")
    return render(request, "inventory/purchase_detail.html", {
        "purchase": purchase, "items": items
    })


@company_admin_required
def inventory_view(request):
    company = gc(request)
    branch = get_active_branch(request)
    ctx = get_branch_context(request)
    summary = stock_summary(company, branch)
    products = summary["products"]
    show_branch_columns = branch is None and ctx.is_multi
    if show_branch_columns:
        per_branch = stock_by_branch(company, [p.pk for p in products])
        for p in products:
            p.by_branch = [
                {"branch": b, "qty": per_branch.get(p.pk, {}).get(str(b.pk), Decimal("0"))}
                for b in ctx.branches
            ]
    for p in products:
        p.stock_value_branch = p.branch_qty * p.buy_price
    return render(request, "inventory/inventory_view.html", {
        "products": products,
        "total_value": summary["total_value"],
        "low_stock_count": len(summary["alert_products"]),
        "out_of_stock": len(summary["out_of_stock"]),
        "show_branch_columns": show_branch_columns,
    })


@company_admin_required
def product_search_api(request):
    company = gc(request)
    branch = get_active_branch(request)
    q = request.GET.get("q", "").strip().lower()
    data = []
    for p in branch_products(company, branch, in_stock_only=True):
        haystack = " ".join([
            p.name, p.brand.name if p.brand else "", p.category.name if p.category else ""
        ]).lower()
        if q and q not in haystack:
            continue
        data.append({
            "id": str(p.id),
            "name": str(p.name),
            "brand": p.brand.name if p.brand else "",
            "category": p.category.name if p.category else "",
            "size": p.size.name if p.size else "",
            "stock": float(p.branch_qty),
            "buy_price": float(p.buy_price),
            "sell_price": float(p.sell_price),
        })
        if len(data) >= 20:
            break
    return JsonResponse(data, safe=False)
