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.db.models import Q
from django.core.paginator import Paginator
from .models import Product, ProductCategory, InventoryTransaction
from .forms import ProductForm, ProductCategoryForm
from django.core.exceptions import PermissionDenied
from core.utils import check_limit_or_block
from accounts.decorators import role_required, perm_required

def dashboard(request):
    return render(request, 'inventory/dashboard.html')

@login_required
def inventory_dashboard(request):
    # Get statistics
    total_products = Product.objects.filter(company=request.company).count()
    total_stock = Product.objects.filter(company=request.company).aggregate(total=Sum('stock_quantity'))['total'] or 0
    low_stock_count = Product.objects.filter(company=request.company, stock_status='low_stock', is_hidden=False).count()
    out_of_stock_count = Product.objects.filter(company=request.company, stock_status='out_of_stock', is_hidden=False).count()
    
    # Calculate Total Worth
    settings = getattr(request.company, 'settings', None)
    allow_zero = settings.enable_unlimited_stock if settings else False

    # For worth, we exclude items with 0 stock implicitly by multiplying, 
    # but let's query all products and sum(price * stock)
    worth_items = Product.objects.filter(company=request.company, stock_quantity__gt=0, is_hidden=False)
    total_inventory_worth = sum((p.price * p.stock_quantity) for p in worth_items)
    if settings and settings.use_comma_separator:
        total_inventory_worth_str = "{:,.2f}".format(total_inventory_worth)
    else:
        total_inventory_worth_str = "{:.2f}".format(total_inventory_worth)
    
    # Get recent products
    recent_products = Product.objects.filter(company=request.company, is_hidden=False).order_by('-created_at')[:5]
    
    # Separate low stock and out-of-stock products
    low_stock_products = Product.objects.filter(company=request.company, stock_status='low_stock', is_hidden=False)[:5]
    out_of_stock_products = Product.objects.filter(company=request.company, stock_status='out_of_stock', is_hidden=False)[:5]
    
    context = {
        'total_products': total_products,
        'total_stock': total_stock,
        'low_stock_count': low_stock_count,
        'out_of_stock_count': out_of_stock_count,
        'total_inventory_worth': total_inventory_worth,
        'recent_products': recent_products,
        'low_stock_products': low_stock_products,
        'out_of_stock_products': out_of_stock_products,  # Added this line
        'user_role': request.user.role,
        'user_name': request.user.get_full_name() or request.user.username,
    }
    return render(request, 'inventory/dashboard.html', context)

@login_required
def product_list(request):
    query = request.GET.get('q', '')
    category_id = request.GET.get('category', '')
    status = request.GET.get('status', '')
    stock_status = request.GET.get('stock_status', '')
    show_hidden = request.GET.get('hidden', 'false')
    
    products = Product.objects.select_related('category').filter(company=request.company)
    
    if show_hidden == 'true':
        products = products.filter(is_hidden=True)
    else:
        products = products.filter(is_hidden=False)
    
    if query:
        products = products.filter(
            Q(name__icontains=query) | 
            Q(sku__icontains=query) | 
            Q(barcode__icontains=query)
        )
    
    if category_id:
        products = products.filter(category_id=category_id)
    
    if status:
        products = products.filter(status=status)
    
    if stock_status:
        products = products.filter(stock_status=stock_status)
    
    # Pagination
    paginator = Paginator(products, 20)
    page_number = request.GET.get('page')
    page_obj = paginator.get_page(page_number)
    
    categories = ProductCategory.objects.filter(company=request.company)
    
    context = {
        'page_obj': page_obj,
        'categories': categories,
        'query': query,
        'selected_category': category_id,
        'selected_status': status,
        'selected_stock_status': stock_status,
        'show_hidden': show_hidden,
        'user_role': request.user.role,
        'user_name': request.user.get_full_name() or request.user.username,
    }
    return render(request, 'inventory/product_list.html', context)


# inventory/views.py (complete add and edit functions)
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.db.models import Q, F, Sum
from django.core.paginator import Paginator
from .models import Product, ProductCategory, InventoryTransaction, StockAdjustment
from .forms import ProductForm, ProductCategoryForm
@login_required
@perm_required('can_manage_products')
def add_product(request):
    """
    Add a new product to inventory, respecting plan limits.
    """
    # Check limit before even showing the form
    try:
        check_limit_or_block("products", request=request)
    except PermissionDenied as e:
        messages.error(request, str(e))
        return redirect('inventory:product_list')

    if request.method == 'POST':
        form = ProductForm(request.POST, request.FILES, company=request.company)
        if form.is_valid():
            try:
                # Check again in case another user added product in between
                check_limit_or_block("products", request=request)

                # Save the product
                product = form.save(commit=False)
                product.company = request.company
                product.save()

                # Create initial stock transaction if quantity > 0
                if product.stock_quantity > 0:
                    from core.models import Branch
                    from inventory.models import BranchStock
                    b = Branch.objects.filter(company=request.company).first()
                    if b:
                        BranchStock.objects.create(company=request.company, product=product, branch=b, quantity=product.stock_quantity)
                    InventoryTransaction.objects.create(
                        company=request.company,
                        product=product,
                        transaction_type='in',
                        quantity=product.stock_quantity,
                        reference=f'Initial stock for {product.name}',
                        created_by=request.user.username
                    )

                from accounts.audit import log_action
                log_action(request, 'add_product', target=f'Product: {product.name}')

                messages.success(request, f'Product "{product.name}" added successfully!')
                return redirect('inventory:product_list')
            except PermissionDenied as e:
                messages.error(request, str(e))
                return redirect('inventory:product_list')
            except Exception as e:
                messages.error(request, f'Error adding product: {str(e)}')
        else:
            messages.error(request, 'Please correct the errors below.')
    else:
        form = ProductForm(company=request.company)

    context = {
        'form': form,
        'user_role': request.user.role,
        'user_name': request.user.get_full_name() or request.user.username,
    }
    return render(request, 'inventory/add_product.html', context)



@login_required
@perm_required('can_manage_products')
def edit_product(request, product_id):
    """
    Edit an existing product
    """
    product = get_object_or_404(Product, id=product_id, company=request.company)
    
    if request.method == 'POST':
        form = ProductForm(request.POST, request.FILES, instance=product, company=request.company)
        if form.is_valid():
            try:
                # Get old stock quantity before save
                old_stock = product.stock_quantity
                
                # Save the updated product
                updated_product = form.save()
                
                # If stock quantity changed, create adjustment transaction
                new_stock = updated_product.stock_quantity
                if old_stock != new_stock:
                    diff = new_stock - old_stock
                    transaction_type = 'in' if diff > 0 else 'out'
                    quantity = abs(diff)
                    
                    InventoryTransaction.objects.create(
                        company=request.company,
                        product=updated_product,
                        transaction_type=transaction_type,
                        quantity=quantity,
                        reference=f'Stock adjustment for {updated_product.name}',
                        created_by=request.user.username
                    )
                
                from accounts.audit import log_action
                log_action(request, 'edit_product', target=f'Product: {updated_product.name}')

                messages.success(request, f'Product "{updated_product.name}" updated successfully!')
                return redirect('inventory:product_detail', product_id=updated_product.id)
            except Exception as e:
                messages.error(request, f'Error updating product: {str(e)}')
        else:
            messages.error(request, 'Please correct the errors below.')
    else:
        form = ProductForm(instance=product, company=request.company)
    
    context = {
        'form': form,
        'product': product,
        'user_role': request.user.role,
        'user_name': request.user.get_full_name() or request.user.username,
    }
    return render(request, 'inventory/edit_product.html', context)



@login_required
def product_detail(request, product_id):
    """
    View product details with transactions and adjustments
    """
    product = get_object_or_404(Product, id=product_id, company=request.company)
    transactions = InventoryTransaction.objects.filter(company=request.company, product=product).order_by('-created_at')[:10]
    adjustments = StockAdjustment.objects.filter(company=request.company, product=product).order_by('-created_at')[:10]

    from inventory.models import BranchStock, ProductVariant, VariantBranchStock
    from django.db.models import Sum

    branch_stocks = BranchStock.objects.filter(company=request.company, product=product).select_related('branch')

    # ── Variant-aware stock ───────────────────────────────────────────────────
    plan = getattr(request.company, 'plan', None)
    variants_enabled = bool(plan and getattr(plan, 'feature_product_variants', False))
    variants = list(ProductVariant.objects.filter(company=request.company, product=product, is_active=True)) if variants_enabled else []
    has_variants = bool(variants)

    variant_stock_total = None
    variant_branch_stocks = []
    variant_stock_map = {}  # variant.id -> total stock (from VariantBranchStock)

    if has_variants:
        variant_ids = [v.id for v in variants]

        # ── Per-variant stock from VariantBranchStock (source of truth used by POS)
        vbs_totals = (
            VariantBranchStock.objects
            .filter(company=request.company, variant_id__in=variant_ids)
            .values('variant_id')
            .annotate(total=Sum('quantity'))
        )
        variant_stock_map = {row['variant_id']: row['total'] for row in vbs_totals}

        # If a variant has no VariantBranchStock rows, fall back to variant.stock_quantity
        for v in variants:
            if v.id not in variant_stock_map:
                variant_stock_map[v.id] = v.stock_quantity

        variant_stock_total = sum(variant_stock_map.values())

        # ── Per-branch breakdown ─────────────────────────────────────────────
        from core.models import Branch
        for branch in Branch.objects.filter(company=request.company, is_active=True).order_by('name'):
            total = VariantBranchStock.objects.filter(
                company=request.company,
                branch=branch,
                variant_id__in=variant_ids
            ).aggregate(t=Sum('quantity'))['t'] or 0
            variant_branch_stocks.append({'branch': branch, 'quantity': total})

        # ── Sync product.stock_quantity if it's stale ────────────────────────
        if product.stock_quantity != variant_stock_total:
            Product.objects.filter(pk=product.pk).update(stock_quantity=variant_stock_total)
            product.stock_quantity = variant_stock_total  # update in-memory too

        # Attach real_stock to each variant so template can use variant.real_stock
        for v in variants:
            v.real_stock = variant_stock_map.get(v.id, v.stock_quantity)

    context = {
        'product': product,
        'transactions': transactions,
        'adjustments': adjustments,
        'branch_stocks': branch_stocks,
        'has_variants': has_variants,
        'variants': variants,
        'variant_stock_map': variant_stock_map,
        'variant_stock_total': variant_stock_total,
        'variant_branch_stocks': variant_branch_stocks,
        'user_role': request.user.role,
        'user_name': request.user.get_full_name() or request.user.username,
    }
    return render(request, 'inventory/product_detail.html', context)


@login_required
def product_category_list(request):
    categories = ProductCategory.objects.filter(company=request.company)
    
    context = {
        'categories': categories,
        'user_role': request.user.role,
        'user_name': request.user.get_full_name() or request.user.username,
    }
    return render(request, 'inventory/category_list.html', context)

@login_required
def add_category(request):
    """
    Add a new product category, respecting plan limits.
    """
    try:
        check_limit_or_block("categories", request=request)
    except PermissionDenied as e:
        messages.error(request, str(e))
        return redirect('inventory:category_list')

    if request.method == 'POST':
        form = ProductCategoryForm(request.POST, company=request.company)
        if form.is_valid():
            try:
                # Check again in case someone else added a category
                check_limit_or_block("categories", request=request)
                category = form.save(commit=False)
                category.company = request.company
                category.save()
                
                from accounts.audit import log_action
                log_action(request, 'add_category', target=f'Category: {category.name}')

                messages.success(request, f'Category "{category.name}" created successfully!')
                return redirect('inventory:category_list')
            except PermissionDenied as e:
                messages.error(request, str(e))
                return redirect('inventory:category_list')
            except Exception as e:
                messages.error(request, f'Error creating category: {str(e)}')
        else:
            messages.error(request, 'Please correct the errors below.')
    else:
        form = ProductCategoryForm(company=request.company)

    context = {
        'form': form,
        'user_role': request.user.role,
        'user_name': request.user.get_full_name() or request.user.username,
    }
    return render(request, 'inventory/add_category.html', context)

@login_required
@perm_required('can_manage_products')
def adjust_stock(request, product_id):
    product = get_object_or_404(Product, id=product_id, company=request.company)
    
    if request.method == 'POST':
        quantity = int(request.POST.get('quantity', 0))
        transaction_type = request.POST.get('transaction_type', 'adjustment')
        notes = request.POST.get('notes', '')
        
        # Update stock
        if transaction_type == 'in':
            product.stock_quantity += quantity
        elif transaction_type == 'out':
            product.stock_quantity -= quantity
        else:  # adjustment
            product.stock_quantity = quantity
        
        product.save()
        
        # Create transaction record
        InventoryTransaction.objects.create(
            company=request.company,
            product=product,
            transaction_type=transaction_type,
            quantity=quantity,
            notes=notes,
            created_by=request.user.username
        )
        
        messages.success(request, f'Stock adjusted for {product.name}')
        return redirect('inventory:product_detail', product_id=product.id)
    
    from core.models import Branch
    context = {
        'product': product,
        'branches': Branch.objects.filter(company=request.company),
        'user_role': request.user.role,
        'user_name': request.user.get_full_name() or request.user.username,
    }
    return render(request, 'inventory/adjust_stock.html', context)


# inventory/views.py (add to existing views)
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.db.models import Q, F, Sum
from django.core.paginator import Paginator
from .models import Product, ProductCategory, InventoryTransaction, StockAdjustment
from .forms import ProductForm, ProductCategoryForm

@login_required
@perm_required('can_manage_products')
def restock_product(request, product_id):
    product = get_object_or_404(Product, id=product_id, company=request.company)
    
    if request.method == 'POST':
        quantity = int(request.POST.get('quantity', 0))
        reason = request.POST.get('reason', '')
        reference = request.POST.get('reference_number', '')
        
        if quantity <= 0:
            messages.error(request, 'Quantity must be greater than 0')
            return redirect('inventory:restock_product', product_id=product.id)
        
        branch_id = request.POST.get('branch_id')
        branch = None
        if branch_id:
            from core.models import Branch
            branch = Branch.objects.filter(id=branch_id, company=request.company).first()
            
        # Create stock adjustment record
        StockAdjustment.objects.create(
            company=request.company,
            product=product,
            branch=branch,
            adjustment_type='restock',
            quantity=quantity,
            reason=reason,
            reference_number=reference,
            performed_by=request.user.username
        )
        
        messages.success(request, f'Successfully restocked {quantity} units of {product.name}')
        return redirect('inventory:product_detail', product_id=product.id)
    
    from core.models import Branch
    context = {
        'product': product,
        'branches': Branch.objects.filter(company=request.company),
        'user_role': request.user.role,
        'user_name': request.user.get_full_name() or request.user.username,
    }
    return render(request, 'inventory/restock_product.html', context)

@login_required
@perm_required('can_manage_products')
def reduce_stock(request, product_id):
    product = get_object_or_404(Product, id=product_id, company=request.company)
    
    if request.method == 'POST':
        quantity = int(request.POST.get('quantity', 0))
        reason = request.POST.get('reason', '')
        reference = request.POST.get('reference_number', '')
        
        if quantity <= 0:
            messages.error(request, 'Quantity must be greater than 0')
            return redirect('inventory:reduce_stock', product_id=product.id)
        
        if quantity > product.stock_quantity:
            messages.error(request, f'Cannot reduce stock by {quantity}. Current stock is {product.stock_quantity}')
            return redirect('inventory:reduce_stock', product_id=product.id)
        
        branch_id = request.POST.get('branch_id')
        branch = None
        if branch_id:
            from core.models import Branch
            branch = Branch.objects.filter(id=branch_id, company=request.company).first()
            
        # Create stock adjustment record
        StockAdjustment.objects.create(
            company=request.company,
            product=product,
            branch=branch,
            adjustment_type='reduce',
            quantity=quantity,
            reason=reason,
            reference_number=reference,
            performed_by=request.user.username
        )
        
        messages.success(request, f'Successfully reduced {quantity} units of {product.name}')
        return redirect('inventory:product_detail', product_id=product.id)
    
    from core.models import Branch
    context = {
        'product': product,
        'branches': Branch.objects.filter(company=request.company),
        'user_role': request.user.role,
        'user_name': request.user.get_full_name() or request.user.username,
    }
    return render(request, 'inventory/reduce_stock.html', context)

@login_required
def stock_adjustment_history(request):
    adjustments = StockAdjustment.objects.select_related('product').filter(company=request.company).order_by('-created_at')
    
    # Filtering
    product_id = request.GET.get('product', '')
    adjustment_type = request.GET.get('type', '')
    
    if product_id:
        adjustments = adjustments.filter(product_id=product_id)
    
    if adjustment_type:
        adjustments = adjustments.filter(adjustment_type=adjustment_type)
    
    # Pagination
    paginator = Paginator(adjustments, 25)
    page_number = request.GET.get('page')
    page_obj = paginator.get_page(page_number)
    
    products = Product.objects.filter(company=request.company)
    
    context = {
        'page_obj': page_obj,
        'products': products,
        'selected_product': product_id,
        'selected_type': adjustment_type,
        'user_role': request.user.role,
        'user_name': request.user.get_full_name() or request.user.username,
    }
    return render(request, 'inventory/stock_adjustment_history.html', context)

@login_required
def product_detail(request, product_id):
    product = get_object_or_404(Product, id=product_id, company=request.company)
    transactions = InventoryTransaction.objects.filter(company=request.company, product=product).order_by('-created_at')[:10]
    adjustments = StockAdjustment.objects.filter(company=request.company, product=product).order_by('-created_at')[:10]

    from inventory.models import BranchStock, ProductVariant, VariantBranchStock
    from django.db.models import Sum

    branch_stocks = BranchStock.objects.filter(company=request.company, product=product).select_related('branch')

    plan = getattr(request.company, 'plan', None)
    variants_enabled = bool(plan and getattr(plan, 'feature_product_variants', False))
    variants = list(ProductVariant.objects.filter(company=request.company, product=product, is_active=True)) if variants_enabled else []
    has_variants = bool(variants)

    variant_stock_total = None
    variant_branch_stocks = []
    variant_stock_map = {}

    if has_variants:
        variant_ids = [v.id for v in variants]

        vbs_totals = (
            VariantBranchStock.objects
            .filter(company=request.company, variant_id__in=variant_ids)
            .values('variant_id')
            .annotate(total=Sum('quantity'))
        )
        variant_stock_map = {row['variant_id']: row['total'] for row in vbs_totals}
        for v in variants:
            if v.id not in variant_stock_map:
                variant_stock_map[v.id] = v.stock_quantity

        variant_stock_total = sum(variant_stock_map.values())

        from core.models import Branch
        for branch in Branch.objects.filter(company=request.company, is_active=True).order_by('name'):
            total = VariantBranchStock.objects.filter(
                company=request.company,
                branch=branch,
                variant_id__in=variant_ids
            ).aggregate(t=Sum('quantity'))['t'] or 0
            variant_branch_stocks.append({'branch': branch, 'quantity': total})

        if product.stock_quantity != variant_stock_total:
            Product.objects.filter(pk=product.pk).update(stock_quantity=variant_stock_total)
            product.stock_quantity = variant_stock_total

        # Attach real_stock to each variant so template can use variant.real_stock
        for v in variants:
            v.real_stock = variant_stock_map.get(v.id, v.stock_quantity)

    context = {
        'product': product,
        'transactions': transactions,
        'adjustments': adjustments,
        'branch_stocks': branch_stocks,
        'has_variants': has_variants,
        'variants': variants,
        'variant_stock_map': variant_stock_map,
        'variant_stock_total': variant_stock_total,
        'variant_branch_stocks': variant_branch_stocks,
        'user_role': request.user.role,
        'user_name': request.user.get_full_name() or request.user.username,
    }
    return render(request, 'inventory/product_detail.html', context)


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.db.models import Q
from django.core.paginator import Paginator

import csv
import io

from .models import Product, ProductCategory, InventoryTransaction, StockAdjustment
from .forms import ProductForm, ProductCategoryForm

import datetime
from django.http import HttpResponse


import csv
import io
from decimal import Decimal, InvalidOperation

from django.shortcuts import render, redirect
from django.contrib import messages
from django.contrib.auth.decorators import login_required

from .models import ProductCategory, Product, InventoryTransaction


@login_required
@perm_required('can_manage_products')
def product_bulk_import(request):
    """Bulk import products from a CSV file."""
    if request.method == "POST":
        upload = request.FILES.get("file")
        if not upload:
            messages.error(request, "Please choose a CSV file to upload.")
            return redirect("inventory:product_bulk_import")

        if not upload.name.lower().endswith(".csv"):
            messages.error(request, "Only CSV files (.csv) are supported.")
            return redirect("inventory:product_bulk_import")

        try:
            data = upload.read().decode("utf-8")
        except UnicodeDecodeError:
            messages.error(request, "Unable to read file. Please use UTF-8 encoded CSV.")
            return redirect("inventory:product_bulk_import")

        f = io.StringIO(data)
        reader = csv.DictReader(f)

        created_count = 0
        updated_count = 0
        errors = []

        for index, row in enumerate(reader, start=2):
            try:
                # Extract and validate required fields
                name = (row.get("name") or "").strip()
                sku = (row.get("sku") or "").strip()
                category_name = (row.get("category") or "").strip()
                cost_price = row.get("cost_price", "").strip()
                stock_quantity = row.get("stock_quantity", "").strip()
                minimum_stock = row.get("minimum_stock", "").strip()
                description = (row.get("description") or "").strip()
                barcode = (row.get("barcode") or "").strip()
                price = row.get("price", "").strip()

                if not name:
                    raise ValueError("Missing product name.")

                # Validate SKU uniqueness
                if not sku:
                    raise ValueError("Missing SKU.")

                # Validate category
                category = None
                if category_name:
                    category, _ = ProductCategory.objects.get_or_create(
                        company=request.company,
                        name=category_name
                    )

                # Convert numeric values
                def to_decimal(value, default=Decimal("0.00")):
                    value = value.strip() if value else ""
                    if not value:
                        return default
                    try:
                        return Decimal(value)
                    except InvalidOperation:
                        raise ValueError(f"Invalid decimal value: {value}")

                def to_int(value, default=0):
                    value = value.strip() if value else ""
                    if not value:
                        return default
                    try:
                        return int(float(value))
                    except ValueError:
                        raise ValueError(f"Invalid integer value: {value}")

                cost_price = to_decimal(cost_price)
                price = to_decimal(price) if price else cost_price
                stock_quantity = to_int(stock_quantity)
                minimum_stock = to_int(minimum_stock)

                # Lookup product by SKU
                product, created = Product.objects.get_or_create(
                    sku=sku,
                    company=request.company,
                    defaults={
                        "name": name,
                        "category": category,
                        "cost_price": cost_price,
                        "stock_quantity": stock_quantity,
                        "minimum_stock": minimum_stock,
                        "description": description,
                        "barcode": barcode,
                        "price": price,
                    },
                )

                if not created:
                    # Update existing product
                    product.name = name
                    if category:
                        product.category = category
                    product.cost_price = cost_price
                    product.stock_quantity += stock_quantity  # Add to existing stock
                    product.minimum_stock = minimum_stock
                    product.description = description
                    if barcode:
                        product.barcode = barcode
                    product.price = price
                    product.save()
                    updated_count += 1
                else:
                    created_count += 1
                    if stock_quantity > 0:
                        InventoryTransaction.objects.create(
                            company=request.company,
                            product=product,
                            transaction_type="in",
                            quantity=stock_quantity,
                            reference=f"Bulk import initial stock for {product.name}",
                            notes="Initial stock from CSV import",
                            created_by=request.user.username,
                        )

            except Exception as e:
                errors.append(f"Row {index}: {e}")

        msg = f"Bulk import completed. Created: {created_count}, Updated: {updated_count}."
        if errors:
            msg += f" {len(errors)} rows had errors."
        messages.success(request, msg)
        if errors:
            preview = " | ".join(errors[:3])
            messages.warning(request, f"Some rows could not be imported: {preview}")

        return redirect("inventory:product_list")

    context = {
        "user_role": getattr(request.user, "role", None),
        "user_name": request.user.get_full_name() or request.user.username,
    }
    return render(request, "inventory/product_bulk_import.html", context)
    

@login_required
def product_import_template(request):
    """Download a CSV template for bulk product import."""
    response = HttpResponse(content_type="text/csv")
    response["Content-Disposition"] = 'attachment; filename="product_import_template.csv"'

    writer = csv.writer(response)
    writer.writerow(
        ["name", "sku", "barcode", "category", "cost_price", "price", "stock_quantity", "minimum_stock", "description"]
    )
    writer.writerow(
        ["Coca Cola 50cl", "CC50", "123456789", "Drinks", "500", "700", "150", "24", "Yayi"]
    )
    writer.writerow(
        ["Coca Cola 30cl", "CC30", "987654321", "Drinks", "350", "450", "150", "24", "Okay"]
    )
    return response


@login_required
@perm_required('can_export_data')
def product_bulk_export(request):
    """Export all products as CSV."""
    response = HttpResponse(content_type="text/csv")
    filename = f"products_{datetime.date.today().isoformat()}.csv"
    response["Content-Disposition"] = f'attachment; filename="{filename}"'

    writer = csv.writer(response)
    writer.writerow(
        [
            "id",
            "name",
            "sku",
            "category",
            "cost_price",
            # "selling_price",
            "stock_quantity",
            "minimum_stock",
            "status",
            "stock_status",
            "created_at",
            "updated_at",
        ]
    )

    products = Product.objects.select_related("category").filter(company=request.company).order_by("name")
    for p in products:
        writer.writerow(
            [
                p.id,
                p.name,
                p.sku or "",
                p.category.name if p.category else "",
                p.cost_price,
                # p.selling_price,
                p.stock_quantity,
                p.minimum_stock,
                p.status,
                p.stock_status,
                p.created_at,
                p.updated_at,
            ]
        )

    return response


@login_required
def category_bulk_export(request):
    """Export all product categories as CSV."""
    response = HttpResponse(content_type="text/csv")
    filename = f"categories_{datetime.date.today().isoformat()}.csv"
    response["Content-Disposition"] = f'attachment; filename="{filename}"'

    writer = csv.writer(response)
    writer.writerow(["id", "name", "description", "color_code", "icon", "created_at", "updated_at"])

    for c in ProductCategory.objects.filter(company=request.company).order_by("name"):
        writer.writerow(
            [c.id, c.name, c.description, c.color_code, c.icon, c.created_at, c.updated_at]
        )

    return response


@login_required
def inventory_transactions_export(request):
    """Export all inventory transactions as CSV."""
    response = HttpResponse(content_type="text/csv")
    filename = f"inventory_transactions_{datetime.date.today().isoformat()}.csv"
    response["Content-Disposition"] = f'attachment; filename="{filename}"'

    writer = csv.writer(response)
    writer.writerow(
        [
            "id",
            "product_name",
            "product_sku",
            "transaction_type",
            "quantity",
            "reference",
            "notes",
            "created_by",
            "created_at",
        ]
    )

    txns = InventoryTransaction.objects.select_related("product").filter(company=request.company).order_by("-created_at")
    for t in txns:
        writer.writerow(
            [
                t.id,
                t.product.name if t.product else "",
                t.product.sku if t.product else "",
                t.transaction_type,
                t.quantity,
                t.reference,
                t.notes,
                t.created_by,
                t.created_at,
            ]
        )

    return response


@login_required
def stock_adjustments_export(request):
    """Export all stock adjustments as CSV."""
    response = HttpResponse(content_type="text/csv")
    filename = f"stock_adjustments_{datetime.date.today().isoformat()}.csv"
    response["Content-Disposition"] = f'attachment; filename="{filename}"'

    writer = csv.writer(response)
    writer.writerow(
        [
            "id",
            "product_name",
            "product_sku",
            "adjustment_type",
            "quantity",
            "reason",
            "reference_number",
            "performed_by",
            "created_at",
        ]
    )

    adjustments = StockAdjustment.objects.select_related("product").filter(company=request.company).order_by("-created_at")
    for adj in adjustments:
        writer.writerow(
            [
                adj.id,
                adj.product.name if adj.product else "",
                adj.product.sku if adj.product else "",
                adj.adjustment_type,
                adj.quantity,
                adj.reason,
                adj.reference_number,
                adj.performed_by,
                adj.created_at,
            ]
        )

    return response


@login_required
@perm_required('can_export_data')
def export_center(request):
    """Central page where user can export inventory & sales data."""
    context = {
        "user_role": getattr(request.user, "role", None),
        "user_name": request.user.get_full_name() or request.user.username,
    }
    return render(request, "inventory/export_center.html", context)


from .models import Product, ProductCategory, InventoryTransaction, Supplier, PurchaseOrder, PurchaseOrderItem, SupplierPayment
from .forms import ProductForm, ProductCategoryForm, SupplierForm, PurchaseOrderForm, PurchaseOrderItemForm
from django.forms import inlineformset_factory
from django.utils import timezone
from decimal import Decimal

# -----------------------------------
# SUPPLIERS

@login_required
@perm_required('can_manage_suppliers')
def supplier_list(request):
    # Fetch all suppliers
    suppliers = Supplier.objects.filter(company=request.company).order_by("name")

    return render(request, "inventory/supplier_list.html", {
        "suppliers": suppliers,
        "user_role": request.user.role,
        "user_name": request.user.get_full_name() or request.user.username,
    })





@login_required
@perm_required('can_manage_suppliers')
def supplier_create(request):
    if request.method == "POST":
        form = SupplierForm(request.POST, company=request.company)
        if form.is_valid():
            supplier = form.save(commit=False)
            supplier.company = request.company
            supplier.save()
            messages.success(request, f'Supplier "{supplier.name}" added.')
            return redirect("inventory:supplier_list")
    else:
        form = SupplierForm(company=request.company)

    return render(request, "inventory/supplier_form.html", {"form": form, "mode": "create"})


@login_required
@perm_required('can_manage_suppliers')
def supplier_edit(request, supplier_id):
    supplier = get_object_or_404(Supplier, id=supplier_id, company=request.company)
    
    if request.method == "POST":
        form = SupplierForm(request.POST, instance=supplier, company=request.company)
        if form.is_valid():
            form.save()
            messages.success(request, f'Supplier "{supplier.name}" updated successfully.')
            return redirect("inventory:supplier_detail", supplier_id=supplier.id)
    else:
        form = SupplierForm(instance=supplier, company=request.company)

    return render(request, "inventory/supplier_form.html", {"form": form, "mode": "edit", "supplier": supplier})

@login_required
@perm_required('can_manage_suppliers')
def supplier_delete(request, supplier_id):
    supplier = get_object_or_404(Supplier, id=supplier_id, company=request.company)
    
    if request.method == "POST":
        name = supplier.name
        supplier.delete()
        messages.success(request, f'Supplier "{name}" deleted successfully.')
        return redirect("inventory:supplier_list")
        
    messages.error(request, "Invalid request to delete supplier.")
    return redirect("inventory:supplier_detail", supplier_id=supplier.id)

@login_required
@perm_required('can_manage_suppliers')
def supplier_detail(request, supplier_id):
    supplier = get_object_or_404(Supplier, id=supplier_id, company=request.company)

    # All purchase orders for this supplier
    purchase_orders = supplier.purchase_orders.all().order_by("-created_at")

    return render(request, "inventory/supplier_detail.html", {
        "supplier": supplier,
        "purchase_orders": purchase_orders,
        "user_role": request.user.role,
        "user_name": request.user.get_full_name() or request.user.username,
    })


@login_required
@perm_required('can_manage_suppliers')
def supplier_record_payment(request, supplier_id):
    supplier = get_object_or_404(Supplier, id=supplier_id, company=request.company)

    if request.method == "POST":
        amount_raw = request.POST.get("amount", "").strip()
        notes = request.POST.get("notes", "").strip()
        try:
            amount = Decimal(amount_raw)
            if amount <= 0:
                raise ValueError
        except (ValueError, Exception):
            messages.error(request, "Please enter a valid payment amount greater than 0.")
            return redirect("inventory:supplier_record_payment", supplier_id=supplier.id)

        SupplierPayment.objects.create(
            supplier=supplier,
            amount=amount,
            notes=notes or f"Payment to {supplier.name}",
        )

        messages.success(request, f"Payment of {amount} recorded for {supplier.name}.")
        return redirect("inventory:supplier_detail", supplier_id=supplier.id)

    # Pre-fill from ?prefill=<amount>&po=<id>
    prefill_amount = request.GET.get('prefill', '')
    prefill_po = request.GET.get('po', '')

    return render(request, "inventory/supplier_record_payment.html", {
        "supplier": supplier,
        "prefill_amount": prefill_amount,
        "prefill_po": prefill_po,
        "balance": supplier.balance,
        "total_purchased": supplier.total_purchased,
        "total_paid": supplier.total_paid,
        "user_role": request.user.role,
        "user_name": request.user.get_full_name() or request.user.username,
    })



# -----------------------------------
# PURCHASE ORDERS
# -----------------------------------

@login_required
@perm_required('can_manage_purchase_orders')
def purchase_order_list(request):
    orders = PurchaseOrder.objects.filter(supplier__company=request.company).select_related("supplier").order_by("-date_ordered", "-id")
    return render(request, "inventory/purchase_order_list.html", {"orders": orders})


from .forms import PurchaseOrderForm, PurchaseOrderItemFormSet

@login_required
@perm_required('can_manage_purchase_orders')
def purchase_order_create(request, supplier_id=None):
    supplier = None

    if supplier_id:
        supplier = get_object_or_404(Supplier, id=supplier_id, company=request.company)

    if request.method == "POST":
        form = PurchaseOrderForm(request.POST, company=request.company)
        formset = PurchaseOrderItemFormSet(request.POST, form_kwargs={'company': request.company})

        if form.is_valid() and formset.is_valid():
            po = form.save(commit=False)

            # IF creating from supplier page, lock supplier field
            if supplier:
                po.supplier = supplier

            po.save()

            formset.instance = po
            formset.save()

            messages.success(request, "Purchase order created successfully.")
            return redirect("inventory:purchase_order_detail", po.id)

    else:
        if supplier:
            form = PurchaseOrderForm(initial={"supplier": supplier}, company=request.company)
        else:
            form = PurchaseOrderForm(company=request.company)

        formset = PurchaseOrderItemFormSet(form_kwargs={'company': request.company})

    return render(request, "inventory/purchase_order_form.html", {
        "form": form,
        "formset": formset,
        "mode": "create",
        "supplier": supplier,
        "user_role": request.user.role,
        "user_name": request.user.get_full_name() or request.user.username,
    })


@login_required
@role_required('admin')
def purchase_order_detail(request, order_id):
    po = get_object_or_404(PurchaseOrder, id=order_id, supplier__company=request.company)
    return render(
        request,
        "inventory/purchase_order_detail.html",
        {"order": po},
    )


# @login_required
# def purchase_order_mark_received(request, order_id):
#     po = get_object_or_404(PurchaseOrder, id=order_id)
#     if po.is_received:
#         messages.info(request, "This purchase order is already marked as received.")
#         return redirect("inventory:purchase_order_detail", order_id=po.id)

#     if request.method == "POST":
#         po.status = "received"
#         if not po.date_received:
#             po.date_received = timezone.now().date()
#         po.save()

#         _apply_purchase_order_receipt(po, request.user.username)
#         messages.success(request, "Purchase order marked as received and stock updated.")
#         return redirect("inventory:purchase_order_detail", order_id=po.id)

#     return render(
#         request,
#         "inventory/purchase_order_confirm_receive.html",
#         {"order": po},
#     )

@role_required('admin')
def _apply_purchase_order_receipt(po: PurchaseOrder, username: str):
    """
    Apply stock increases and cost_price updates when PO is received.
    - Increase product.stock_quantity
    - Update product.cost_price to latest unit_cost
    - Log InventoryTransaction or StockAdjustment
    """
    for item in po.items.select_related("product"):
        product = item.product
        qty = item.quantity or 0
        if qty <= 0:
            continue

        # Update stock quantity
        product.stock_quantity = (product.stock_quantity or 0) + qty

        # Update cost_price to latest purchase cost (simple strategy)
        product.cost_price = item.unit_cost
        product.save()

        # Log inventory transaction
        InventoryTransaction.objects.create(
            company=product.company,
            product=product,
            transaction_type="in",
            quantity=qty,
            reference=f"PO #{po.id}",
            notes=f"Purchase order received from {po.supplier.name}",
            created_by=username,
        )


@login_required
@perm_required('can_manage_purchase_orders')
def purchase_order_mark_received(request, order_id):
    order = get_object_or_404(PurchaseOrder, id=order_id, supplier__company=request.company)

    if order.status == 'received':
        messages.info(request, 'This purchase order is already marked as received.')
        return redirect('inventory:purchase_order_detail', order_id=order_id)

    from core.models import Branch
    branches = Branch.objects.filter(company=request.company, is_active=True).order_by('name')

    if request.method == 'POST':
        branch_id = request.POST.get('branch_id', '').strip()
        branch = None
        if branch_id:
            branch = get_object_or_404(Branch, id=branch_id, company=request.company)

        order.mark_as_received(
            received_by=request.user.get_full_name() or request.user.username,
            branch=branch,
        )
        item_count = order.items.count()
        branch_msg = f' -> {branch.name}' if branch else ' (global stock)'
        messages.success(request, f'Purchase order received. Stock updated for {item_count} product(s){branch_msg}.')
        return redirect('inventory:purchase_order_detail', order_id=order_id)

    # GET -> confirmation page with branch selector
    return render(request, 'inventory/purchase_order_confirm_receive.html', {
        'order': order,
        'branches': branches,
        'user_branch': getattr(request.user, 'branch', None),
        'user_role': request.user.role,
        'user_name': request.user.get_full_name() or request.user.username,
    })


@login_required
@role_required(['admin', 'manager'])
def print_product_barcode(request, product_id):
    import io
    import base64
    from barcode import Code128
    from barcode.writer import SVGWriter

    product = get_object_or_404(Product, id=product_id, company=request.company)

    code_to_print = product.barcode or product.sku
    if not code_to_print:
        # If neither exists, generate one dynamically
        import uuid
        code_to_print = str(uuid.uuid4().int)[:12]
        product.barcode = code_to_print
        product.save()

    # Generate SVG string
    rv = io.BytesIO()
    try:
        Code128(str(code_to_print), writer=SVGWriter()).write(rv)
        svg_xml = rv.getvalue().decode('utf-8')
    except Exception as e:
        svg_xml = f"<p>Error generating barcode: {e}</p>"

    return render(request, "inventory/print_barcode.html", {
        "product": product,
        "barcode_svg": svg_xml,
    })


@login_required
@role_required(['admin', 'manager'])
def barcode_center(request):
    """Centralized page to list all products for quick barcode printing."""
    company = getattr(request, 'company', None)
    
    products = Product.objects.filter(company=company, status='active').order_by('name')
    
    return render(request, "inventory/barcode_center.html", {
        "products": products,
        "user_role": request.user.role,
        "user_name": request.user.get_full_name() or request.user.username,
    })


# =========================================================
# PRODUCT VARIANTS VIEWS  (Phase 2)
# =========================================================

@login_required
def product_variants(request, product_id):
    '''List variants (HTML/JSON) or create a new one via POST.'''
    from .models import ProductVariant
    import json as _json
    from django.http import JsonResponse as _JR

    product = get_object_or_404(Product, id=product_id, company=request.company)

    if request.method == 'POST':
        try:
            data = _json.loads(request.body)
        except Exception:
            data = request.POST.dict()

        sku = data.get('sku', '').strip()
        if not sku:
            return _JR({'ok': False, 'error': 'SKU is required'}, status=400)
        if ProductVariant.objects.filter(company=request.company, sku=sku).exists():
            return _JR({'ok': False, 'error': 'A variant with this SKU already exists'}, status=400)

        raw_attrs = data.get('attribute_values', {})
        if isinstance(raw_attrs, str):
            try:
                raw_attrs = _json.loads(raw_attrs)
            except Exception:
                raw_attrs = {}

        variant = ProductVariant.objects.create(
            company=request.company,
            product=product,
            sku=sku,
            barcode=data.get('barcode', ''),
            attribute_values=raw_attrs,
            price=data.get('price') or None,
            cost_price=data.get('cost_price') or None,
            stock_quantity=int(data.get('stock_quantity', 0)),
            minimum_stock=int(data.get('minimum_stock', 5)),
            is_active=data.get('is_active', True),
        )
        return _JR({'ok': True, 'variant_id': variant.id, 'display_label': variant.display_label})

    variants = ProductVariant.objects.filter(company=request.company, product=product).order_by('sku')

    if request.headers.get('Accept') == 'application/json' or request.GET.get('format') == 'json':
        data = [
            {
                'id': v.id,
                'sku': v.sku,
                'barcode': v.barcode,
                'display_label': v.display_label,
                'attribute_values': v.attribute_values,
                'price': str(v.effective_price),
                'cost_price': str(v.effective_cost),
                'stock_quantity': v.stock_quantity,
                'stock_status': v.stock_status,
                'is_active': v.is_active,
            }
            for v in variants
        ]
        from django.http import JsonResponse as JR2
        return JR2(data, safe=False)

    from core.models import Branch
    branches = Branch.objects.filter(company=request.company, is_active=True).order_by('name')

    # Build branch-stock list for current user's branch â€” used in template to show per-variant branch qty
    from inventory.models import VariantBranchStock
    user_branch = getattr(request.user, 'branch', None)
    vbs_list = []
    if user_branch:
        variant_ids = list(variants.values_list('id', flat=True))
        vbs_list = list(VariantBranchStock.objects.filter(
            company=request.company, branch=user_branch, variant_id__in=variant_ids
        ).values('variant_id', 'quantity'))

    return render(request, 'inventory/product_variants.html', {
        'product': product,
        'variants': variants,
        'branches': branches,
        'user_branch': user_branch,
        'vbs_list': vbs_list,
        'user_role': request.user.role,
        'user_name': request.user.get_full_name() or request.user.username,
    })


@login_required
def variant_edit(request, product_id, variant_id):
    '''Edit or delete a specific variant.'''
    from .models import ProductVariant
    import json as _json
    from django.http import JsonResponse as _JR

    product = get_object_or_404(Product, id=product_id, company=request.company)
    variant = get_object_or_404(ProductVariant, id=variant_id, product=product, company=request.company)

    if request.method == 'DELETE':
        variant.delete()
        return _JR({'ok': True})

    if request.method in ('POST', 'PUT', 'PATCH'):
        try:
            data = _json.loads(request.body)
        except Exception:
            data = request.POST.dict()

        raw_attrs = data.get('attribute_values', variant.attribute_values)
        if isinstance(raw_attrs, str):
            try:
                raw_attrs = _json.loads(raw_attrs)
            except Exception:
                raw_attrs = variant.attribute_values

        sku = data.get('sku', variant.sku).strip()
        if sku != variant.sku and ProductVariant.objects.filter(company=request.company, sku=sku).exists():
            return _JR({'ok': False, 'error': 'SKU already used by another variant'}, status=400)

        variant.attribute_values = raw_attrs
        variant.sku              = sku
        variant.barcode          = data.get('barcode', variant.barcode)
        variant.price            = data.get('price') or None
        variant.cost_price       = data.get('cost_price') or None
        variant.stock_quantity   = int(data.get('stock_quantity', variant.stock_quantity))
        variant.minimum_stock    = int(data.get('minimum_stock', variant.minimum_stock))
        variant.is_active        = data.get('is_active', variant.is_active)
        variant.save()

        return _JR({'ok': True, 'display_label': variant.display_label, 'stock_status': variant.stock_status})

    return render(request, 'inventory/variant_edit.html', {
        'product': product,
        'variant': variant,
        'user_role': request.user.role,
        'user_name': request.user.get_full_name() or request.user.username,
    })


@login_required
def variant_restock(request, product_id, variant_id):
    '''Quick stock addition to a specific variant, optionally scoped to a branch.
    POST body (JSON or form):
        quantity   â€“ int, required, > 0
        branch_id  â€“ int, optional; if provided, stock is added to VariantBranchStock
                     for that branch (and auto-synced to variant + product totals).
                     If omitted, stock is added directly to variant.stock_quantity.
    '''
    from .models import ProductVariant, VariantBranchStock
    from core.models import Branch
    import json as _json
    from django.http import JsonResponse as _JR

    if request.method != 'POST':
        return _JR({'ok': False, 'error': 'POST required'}, status=405)

    product = get_object_or_404(Product, id=product_id, company=request.company)
    variant = get_object_or_404(ProductVariant, id=variant_id, product=product, company=request.company)

    try:
        data = _json.loads(request.body)
    except Exception:
        data = request.POST.dict()

    qty = int(data.get('quantity', 0))
    if qty <= 0:
        return _JR({'ok': False, 'error': 'Quantity must be greater than 0'}, status=400)

    branch_id = data.get('branch_id')

    if branch_id:
        # Branch-level restock: update VariantBranchStock, which syncs product total
        branch = get_object_or_404(Branch, id=branch_id, company=request.company)
        vbs, _ = VariantBranchStock.objects.get_or_create(
            company=request.company, variant=variant, branch=branch,
            defaults={'quantity': 0}
        )
        vbs.quantity += qty
        vbs.save()  # triggers VariantBranchStock.save() â†’ product stock sync
        new_stock = vbs.quantity
        branch_name = branch.name
        # Total across all branches for this variant
        from django.db.models import Sum as _Sum
        branch_total = variant.branch_stocks.aggregate(t=_Sum('quantity'))['t'] or 0
    else:
        # Global restock (no specific branch): add directly to variant
        variant.stock_quantity += qty
        variant.save()  # triggers ProductVariant.save() â†’ product sync
        new_stock = variant.stock_quantity
        branch_name = None
        branch_total = variant.stock_quantity

    # Refresh variant from DB so we return accurate totals
    variant.refresh_from_db()

    return _JR({
        'ok': True,
        'new_stock': new_stock,          # branch qty (or global if no branch)
        'branch_total': branch_total,    # sum of all branch stocks for this variant
        'variant_total_stock': variant.stock_quantity,  # global variant stock
        'stock_status': variant.stock_status,
        'branch_name': branch_name,
    })


@login_required
def api_variants_for_product(request, product_id):
    '''Lightweight JSON endpoint used by POS variant picker modal.'''
    from .models import ProductVariant
    from django.http import JsonResponse as _JR
    product = get_object_or_404(Product, id=product_id, company=request.company)
    plan = getattr(request.company, 'plan', None)
    if not (plan and getattr(plan, 'feature_product_variants', False)):
        return _JR([], safe=False)

    variants = (
        ProductVariant.objects
        .filter(company=request.company, product=product, is_active=True)
        .order_by('sku')
    )
    data = [
        {
            'id': v.id,
            'display_label': v.display_label,
            'sku': v.sku,
            'barcode': v.barcode,
            'price': str(v.effective_price),
            'cost_price': str(v.effective_cost),
            'stock_quantity': v.stock_quantity,
            'stock_status': v.stock_status,
        }
        for v in variants
    ]
    return _JR(data, safe=False)

@login_required
@role_required(['admin', 'manager'])
def toggle_product_visibility(request, product_id):
    product = get_object_or_404(Product, id=product_id, company=request.company)
    if request.method == 'POST':
        product.is_hidden = not product.is_hidden
        product.save()
        status = "hidden" if product.is_hidden else "visible"
        messages.success(request, f"Product '{product.name}' is now {status}.")
        return redirect(request.META.get('HTTP_REFERER', 'inventory:product_list'))
    return redirect('inventory:product_list')
