"""
Vendor Dashboard Views - Using Templates
Vendors can manage their products, orders, and finances
"""
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib.auth import login, logout
from django.contrib import messages
from django.http import HttpResponse
from django.db.models import Sum, Count, Q, Min, Max
from django.utils import timezone
from datetime import timedelta
from django.views.decorators.csrf import csrf_exempt
from functools import wraps
from io import BytesIO

from products.models import Product, Category, ProductVariation, ProductImage
from django.utils.dateparse import parse_datetime
from decimal import Decimal
from orders.models import Order, OrderItem, OrderStatusHistory, CourierCompany
from .models import Vendor, VendorCommission, VendorPayout, VendorStore
from accounts.models import User
from django.core.validators import validate_email, URLValidator
from django.core.exceptions import ValidationError
from django.core.mail import send_mail
from django.conf import settings
import re
from frontend.captcha import generate_captcha, validate_captcha


# Decorator to ensure user has vendor profile
def vendor_required(view_func):
    """Ensure user has active vendor profile"""
    @wraps(view_func)
    def wrapper(request, *args, **kwargs):
        if not request.user.is_authenticated:
            messages.error(request, 'Please login to access vendor dashboard')
            return redirect('vendor:login')
        
        if getattr(request.user, 'role', None) != 'VENDOR':
            messages.error(request, 'You need a vendor account to access this page')
            return redirect('vendor:login')
        
        if not hasattr(request.user, 'vendor_profile'):
            messages.error(request, 'You need a vendor account to access this page')
            return HttpResponse('<h1>No Vendor Account</h1><p>Please contact admin to set up your vendor account.</p>')
        
        # Force KYC completion before accessing most vendor pages
        if not (request.user.vendor_profile.kyc_approved or request.user.vendor_profile.is_verified):
            rm = getattr(request, 'resolver_match', None)
            if rm and rm.app_name == 'vendor' and rm.url_name in ('settings', 'kyc', 'kyc_consent_form'):
                return view_func(request, *args, **kwargs)
            messages.error(request, 'KYC not approved. Please submit required documents.')
            return redirect('vendor:kyc')

        # Block only truly restricted statuses (allow PENDING to pass once KYC is approved)
        if request.user.vendor_profile.status in ('SUSPENDED', 'CLOSED'):
            return HttpResponse(f'<h1>Account Restricted</h1><p>Your vendor account status: {request.user.vendor_profile.get_status_display()}</p>')
        
        return view_func(request, *args, **kwargs)
    return wrapper


# Root View
def vendor_home(request):
    """Redirect to dashboard if logged in, else to login"""
    if request.user.is_authenticated and getattr(request.user, 'role', None) == 'VENDOR' and hasattr(request.user, 'vendor_profile'):
        if not (request.user.vendor_profile.kyc_approved or request.user.vendor_profile.is_verified):
            return redirect('vendor:kyc')
        return redirect('vendor:dashboard')
    return redirect('vendor:login')


# Authentication Views
def vendor_login(request):
    """Vendor login page"""
    if request.method == 'POST':
        from django.contrib.auth import authenticate, login as auth_login
        
        email = request.POST.get('email')
        password = request.POST.get('password')
        captcha_value = request.POST.get('captcha', '')
        if not validate_captcha(request, captcha_value):
            messages.error(request, 'Invalid captcha. Please try again.')
            code = generate_captcha(request)
            return render(request, 'vendor/login.html', {'captcha_code': code})
        
        user = authenticate(request, username=email, password=password)
        
        if user is not None:
            # Check if user has vendor profile or vendor role
            if hasattr(user, 'vendor_profile'):
                auth_login(request, user)
                if not (user.vendor_profile.kyc_approved or user.vendor_profile.is_verified):
                    messages.info(request, 'Please submit KYC documents to proceed.')
                    return redirect('vendor:kyc')
                messages.success(request, 'Welcome back!')
                return redirect('vendor:dashboard')
            elif user.role == 'VENDOR':
                # Role exists but no profile - allow login and redirect to registration
                auth_login(request, user)
                messages.info(request, 'Please complete your vendor registration.')
                return redirect('vendor:register')
            else:
                messages.error(request, f'This account is not registered as a vendor (Current Role: {user.role})')
        else:
            messages.error(request, 'Invalid email or password')
    code = generate_captcha(request)
    return render(request, 'vendor/login.html', {'captcha_code': code})


def vendor_logout(request):
    """Vendor logout"""
    logout(request)
    messages.success(request, 'You have been logged out successfully')
    return redirect('vendor:login')


def vendor_register(request):
    """Vendor registration page"""
    if not request.user.is_authenticated:
        messages.error(request, 'Please login to register as a vendor')
        return redirect('vendor:login')
    
    if hasattr(request.user, 'vendor_profile'):
        messages.info(request, 'You already have a vendor account')
        return redirect('vendor:dashboard')
    
    if request.method == 'POST':
        store_name = request.POST.get('store_name')
        business_name = request.POST.get('business_name')
        business_email = request.POST.get('business_email')
        business_phone = request.POST.get('business_phone')
        address_line1 = request.POST.get('address_line1')
        city = request.POST.get('city')
        state = request.POST.get('state')
        country = request.POST.get('country', 'India')
        postal_code = request.POST.get('postal_code')
        store_description = request.POST.get('store_description', '')
        captcha_value = request.POST.get('captcha', '')
        if not validate_captcha(request, captcha_value):
            messages.error(request, 'Invalid captcha. Please try again.')
            code = generate_captcha(request)
            return render(request, 'vendor/register.html', {'captcha_code': code})
        
        if not all([store_name, business_name, business_email, business_phone, address_line1, city, state, country, postal_code]):
            messages.error(request, 'Please fill in all required fields')
            code = generate_captcha(request)
            return render(request, 'vendor/register.html', {'captcha_code': code})
        
        try:
            Vendor.objects.create(
                user=request.user,
                store_name=store_name,
                store_description=store_description,
                business_email=business_email,
                business_phone=business_phone,
                business_name=business_name,
                business_type='Sole Proprietorship',
                address_line1=address_line1,
                city=city,
                state=state,
                country=country,
                postal_code=postal_code,
                status='PENDING',
            )
            messages.success(request, 'Registration created. Complete KYC to get full access.')
            return redirect('vendor:dashboard')
        except Exception as e:
            messages.error(request, f'Error during registration: {str(e)}')
            code = generate_captcha(request)
            return render(request, 'vendor/register.html', {'captcha_code': code})
    code = generate_captcha(request)
    return render(request, 'vendor/register.html', {'captcha_code': code})


# Dashboard
@login_required
@vendor_required
def dashboard(request):
    """Vendor dashboard with overview statistics"""
    vendor = request.user.vendor_profile
    
    # Get statistics
    total_products = Product.objects.filter(vendor=vendor).count()
    pending_products = Product.objects.filter(vendor=vendor, approval_status='PENDING').count()
    approved_products = Product.objects.filter(vendor=vendor, approval_status='APPROVED').count()
    
    # Get orders
    total_orders = OrderItem.objects.filter(product__vendor=vendor).values('order').distinct().count()
    
    # Get sales
    total_sales = VendorCommission.objects.filter(vendor=vendor).aggregate(
        total=Sum('vendor_payout')
    )['total'] or 0
    
    # Recent orders
    recent_orders = OrderItem.objects.filter(
        product__vendor=vendor
    ).select_related('order', 'product').order_by('-created_at')[:5]
    
    # Recent products
    recent_products = Product.objects.filter(vendor=vendor).order_by('-created_at')[:5]
    
    commission_rate = getattr(getattr(vendor, 'subscription_plan', None), 'commission_rate', 0) or 0
    keep_percentage = 100 - (float(commission_rate) if commission_rate else 0)
    context = {
        'vendor': vendor,
        'total_products': total_products,
        'pending_products': pending_products,
        'approved_products': approved_products,
        'total_orders': total_orders,
        'total_sales': total_sales,
        'recent_orders': recent_orders,
        'recent_products': recent_products,
        'keep_percentage': int(keep_percentage),
        'commission_rate': commission_rate,
    }
    
    return render(request, 'vendor/dashboard.html', context)

@login_required
@vendor_required
def analytics(request):
    vendor = request.user.vendor_profile
    try:
        days = int(request.GET.get('days', 30))
    except (ValueError, TypeError):
        days = 30
    start_date = timezone.now() - timedelta(days=days)
    category_id = request.GET.get('category') or ''
    product_id = request.GET.get('product') or ''
    
    order_items = OrderItem.objects.select_related('order', 'product')\
        .filter(order__created_at__gte=start_date, order__is_paid=True, product__vendor=vendor)
    if category_id:
        try:
            order_items = order_items.filter(product__category_id=int(category_id))
        except Exception:
            pass
    if product_id:
        try:
            order_items = order_items.filter(product__unique_code=product_id)
        except Exception:
            pass
    
    total_orders = order_items.values('order_id').distinct().count()
    total_revenue = order_items.aggregate(total=Sum('total_price'))['total'] or Decimal('0')
    avg_order_value = total_revenue / total_orders if total_orders > 0 else Decimal('0')
    total_customers = Order.objects.filter(
        id__in=order_items.values('order_id').distinct()
    ).values('user_id').distinct().count()
    
    top_product_ids = list(order_items.values_list('product_id', flat=True))
    top_products = Product.objects.filter(id__in=top_product_ids, vendor=vendor).annotate(
        sales=Sum('orderitem__quantity'),
        revenue=Sum('orderitem__total_price')
    ).order_by('-sales')[:10]
    
    categories = Category.objects.filter(products__vendor=vendor).distinct().order_by('name')
    product_options = Product.objects.filter(vendor=vendor).values('unique_code', 'name', 'category_id').order_by('name')
    
    context = {
        'vendor': vendor,
        'total_orders': total_orders,
        'total_revenue': total_revenue,
        'avg_order_value': avg_order_value,
        'total_customers': total_customers,
        'top_products': top_products,
        'days': days,
        'categories': categories,
        'product_options': product_options,
        'selected_category': category_id,
        'selected_product': product_id,
    }
    return render(request, 'vendor/analytics.html', context)

# Product Management
@login_required
@vendor_required
def product_list(request):
    """List all vendor's products"""
    vendor = request.user.vendor_profile
    
    status = request.GET.get('status', 'all')
    category_id = request.GET.get('category') or ''
    product_id = request.GET.get('product') or ''
    products = Product.objects.filter(vendor=vendor).order_by('-created_at')
    if status != 'all':
        products = products.filter(approval_status=status.upper())
    if category_id:
        try:
            products = products.filter(category_id=int(category_id))
        except Exception:
            pass
    if product_id:
        try:
            products = products.filter(unique_code=product_id)
        except Exception:
            pass
    
    # Count by status
    status_counts = {
        'all': Product.objects.filter(vendor=vendor).count(),
        'draft': Product.objects.filter(vendor=vendor, approval_status='DRAFT').count(),
        'pending': Product.objects.filter(vendor=vendor, approval_status='PENDING').count(),
        'approved': Product.objects.filter(vendor=vendor, approval_status='APPROVED').count(),
        'rejected': Product.objects.filter(vendor=vendor, approval_status='REJECTED').count(),
    }
    
    categories = Category.objects.filter(products__vendor=vendor).distinct().order_by('name')
    product_options = Product.objects.filter(vendor=vendor).values('unique_code', 'name', 'category_id').order_by('name')
    context = {
        'vendor': vendor,
        'products': products,
        'current_status': status,
        'status_counts': status_counts,
        'categories': categories,
        'product_options': product_options,
        'selected_category': category_id,
        'selected_product': product_id,
    }
    
    return render(request, 'vendor/products/list.html', context)


@login_required
@vendor_required
def product_add(request):
    """Add new product"""
    vendor = request.user.vendor_profile
    
    if request.method == 'POST':
        details_val = (request.POST.get('details', '') or '').strip()
        if not details_val:
            messages.error(request, 'Details are required')
        try:
            # Create product
            if details_val:
                # Enforce admin-managed selections
                selected_name = (request.POST.get('name_select') or '').strip()
                selected_category = request.POST.get('category')
                selected_brand = (request.POST.get('brand', '') or '').strip()
                if not selected_name or not selected_category:
                    messages.error(request, 'Select Category and Product Name from list')
                    raise ValueError('Invalid category/product selection')
                site_products_qs = Product.objects.filter(vendor__isnull=True, name=selected_name)
                try:
                    cid_int = int(selected_category)
                    site_products_qs = site_products_qs.filter(category_id=cid_int)
                except Exception:
                    pass
                if not site_products_qs.exists():
                    messages.error(request, 'Select a valid Product Name for chosen Category')
                    raise ValueError('Invalid product name/category combination')
                from products.models import Brand
                allowed_brands = list(
                    Brand.objects.filter(is_active=True)
                    .values_list('name', flat=True)
                    .order_by('name')
                )
                if selected_brand and selected_brand not in allowed_brands:
                    messages.error(request, 'Select a valid Brand from list')
                    raise ValueError('Invalid brand selection')
                
                brand_obj = None
                if selected_brand:
                    brand_obj = Brand.objects.filter(name=selected_brand, is_active=True).first()

                product = Product.objects.create(
                    vendor=vendor,
                    name=selected_name,
                    sku=request.POST.get('sku'),
                    category_id=selected_category,
                    description=request.POST.get('description', ''),
                    details=details_val,
                    short_description=request.POST.get('short_description', ''),
                    price=request.POST.get('price'),
                    compare_price=request.POST.get('compare_price') or None,
                    stock=request.POST.get('stock', 0),
                    brand=selected_brand,
                    brand_ref=brand_obj,
                    approval_status='DRAFT',
                    is_active=False
                )
            else:
                raise ValueError('Details are required')
            store_id = request.POST.get('store')
            if store_id:
                try:
                    store = VendorStore.objects.get(id=store_id, vendor=vendor, is_active=True)
                    product.store = store
                except VendorStore.DoesNotExist:
                    pass
            
            try:
                sp = request.POST.get('sale_price') or None
                ss = request.POST.get('sale_start') or None
                se = request.POST.get('sale_end') or None
                product.sale_price = Decimal(sp) if sp else None
                
                # Handle Sale Dates
                new_sale_start = parse_datetime(ss) if ss else None
                new_sale_end = parse_datetime(se) if se else None
                
                if new_sale_start and timezone.is_naive(new_sale_start):
                    new_sale_start = timezone.make_aware(new_sale_start)
                if new_sale_end and timezone.is_naive(new_sale_end):
                    new_sale_end = timezone.make_aware(new_sale_end)
                
                product.sale_start = new_sale_start
                product.sale_end = new_sale_end
                
                if product.sale_start and product.sale_end and product.sale_start >= product.sale_end:
                    messages.warning(request, 'Sale end must be after sale start')
                    product.sale_start = None
                    product.sale_end = None
                product.save()
            except Exception as e:
                messages.warning(request, f'Sale details not saved: {str(e)}')
            
            # Handle featured image
            if request.FILES.get('featured_image'):
                product.featured_image = request.FILES['featured_image']
                product.save()
            
            # Handle gallery images with admin-controlled limit
            try:
                plan_limit = vendor.subscription_plan.max_images_per_product if vendor.subscription_plan else None
                max_images = plan_limit if (plan_limit and plan_limit > 0) else 10
                existing_count = product.images.count()
                files = request.FILES.getlist('images[]')
                allowed = max(0, max_images - existing_count)
                if len(files) > allowed:
                    messages.warning(request, f'Only {allowed} additional images allowed by admin policy')
                    files = files[:allowed]
                for idx, f in enumerate(files):
                    ProductImage.objects.create(product=product, image=f, order=existing_count + idx + 1)
            except Exception as ie:
                messages.warning(request, f'Gallery images not fully saved: {ie}')
            
            # Optional variations (multiple)
            names = request.POST.getlist('variation_name[]')
            values = request.POST.getlist('variation_value[]')
            sku_suffixes = request.POST.getlist('variation_sku_suffix[]')
            stocks = request.POST.getlist('variation_stock[]')
            price_adjustments = request.POST.getlist('variation_price_adjustment[]')
            lengths = request.POST.getlist('variation_length[]')
            widths = request.POST.getlist('variation_width[]')
            heights = request.POST.getlist('variation_height[]')
            dimension_units = request.POST.getlist('variation_dimension_unit[]')
            quantity_values = request.POST.getlist('variation_quantity_value[]')
            quantity_units = request.POST.getlist('variation_quantity_unit[]')
            for i, name in enumerate(names or []):
                if not name and not (values and values[i]):
                    continue
                try:
                    pa = Decimal(str((price_adjustments[i] if i < len(price_adjustments) else 0) or 0))
                    st = int((stocks[i] if i < len(stocks) else 0) or 0)
                    ln = lengths[i] if i < len(lengths) else None
                    wd = widths[i] if i < len(widths) else None
                    ht = heights[i] if i < len(heights) else None
                    qv = quantity_values[i] if i < len(quantity_values) else None
                    if pa < 0 or st < 0:
                        raise ValueError('Negative values not allowed')
                    if ln is not None and ln != '' and Decimal(str(ln)) < 0:
                        raise ValueError('Invalid length')
                    if wd is not None and wd != '' and Decimal(str(wd)) < 0:
                        raise ValueError('Invalid width')
                    if ht is not None and ht != '' and Decimal(str(ht)) < 0:
                        raise ValueError('Invalid height')
                    if qv is not None and qv != '' and Decimal(str(qv)) < 0:
                        raise ValueError('Invalid quantity')
                    ProductVariation.objects.create(
                        product=product,
                        name=name,
                        value=(values[i] if i < len(values) else ''),
                        price_adjustment=pa,
                        stock=st,
                        sku_suffix=(sku_suffixes[i] if i < len(sku_suffixes) else ''),
                        length=(ln if ln not in (None, '') else None),
                        width=(wd if wd not in (None, '') else None),
                        height=(ht if ht not in (None, '') else None),
                        dimension_unit=(dimension_units[i] if i < len(dimension_units) else 'cm') or 'cm',
                        quantity_value=(qv if qv not in (None, '') else None),
                        quantity_unit=(quantity_units[i] if i < len(quantity_units) else 'pcs') or 'pcs',
                    )
                except Exception as ve:
                    messages.warning(request, f'Variation not saved: {ve}')
            
            # Check if should submit for review
            if 'submit' in request.POST:
                product.approval_status = 'PENDING'
                product.save()
                messages.success(request, 'Product submitted for review! Staff will review it soon.')
            else:
                messages.success(request, 'Product saved as draft')
            
            return redirect('vendor:product_list')
            
        except Exception as e:
            messages.error(request, f'Error creating product: {str(e)}')
    
    # Get categories (serialize for json_script and template rendering)
    categories = list(Category.objects.filter(is_active=True).values('id', 'name'))
    stores = VendorStore.objects.filter(vendor=vendor, is_active=True)
    plan_limit = vendor.subscription_plan.max_images_per_product if vendor.subscription_plan else None
    images_limit = plan_limit if (plan_limit and plan_limit > 0) else 10
    product_options = Product.objects.filter(vendor__isnull=True).values('unique_code','name','category_id').order_by('name')
    from products.models import Brand
    brand_options = list(
        Brand.objects.filter(is_active=True)
        .values_list('name', flat=True)
        .order_by('name')
    )
    
    context = {
        'vendor': vendor,
        'categories': categories,
        'stores': stores,
        'images_limit': images_limit,
        'product_options': list(product_options),
        'brand_options': brand_options,
    }
    
    return render(request, 'vendor/products/add.html', context)


@login_required
@vendor_required
def product_edit(request, unique_code):
    """Edit existing product"""
    vendor = request.user.vendor_profile
    product = get_object_or_404(Product, unique_code=unique_code, vendor=vendor)
    
    if request.method == 'POST':
        if product.approval_status == 'APPROVED':
            try:
                # Approved products: Only allow updating specific fields
                price = request.POST.get('price')
                compare_price = request.POST.get('compare_price') or None
                stock = request.POST.get('stock', 0)
                sp = request.POST.get('sale_price') or None
                ss = request.POST.get('sale_start') or None
                se = request.POST.get('sale_end') or None
                
                # Update allowed fields
                product.price = price
                product.compare_price = compare_price
                product.stock = stock
                product.sale_price = Decimal(sp) if sp else None
                
                # Handle Sale Dates
                new_sale_start = parse_datetime(ss) if ss else None
                new_sale_end = parse_datetime(se) if se else None
                
                # Make timezone aware if naive
                if new_sale_start and timezone.is_naive(new_sale_start):
                    new_sale_start = timezone.make_aware(new_sale_start)
                if new_sale_end and timezone.is_naive(new_sale_end):
                    new_sale_end = timezone.make_aware(new_sale_end)
                
                # Validate Sale Start >= Current Time (only if changed or new)
                if new_sale_start:
                    # Check if sale start is being modified or set for the first time
                    # If it's the same as before, we don't enforce "future" check (allows editing active sales)
                    if product.sale_start != new_sale_start:
                        if new_sale_start < timezone.now():
                            messages.error(request, 'Sale Start cannot be in the past')
                            return redirect('vendor:product_edit', unique_code=unique_code)
                
                product.sale_start = new_sale_start
                product.sale_end = new_sale_end
                
                if product.sale_start and product.sale_end:
                    if product.sale_start >= product.sale_end:
                        messages.warning(request, 'Sale End must be after Sale Start')
                        product.sale_start = None
                        product.sale_end = None
                
                product.save(update_fields=['price', 'compare_price', 'stock', 'sale_price', 'sale_start', 'sale_end'])
                messages.success(request, 'Product updated successfully')
                return redirect('vendor:product_list')
            except Exception as e:
                messages.error(request, f'Error updating product: {e}')
                return redirect('vendor:product_edit', unique_code=unique_code)

        try:
            details_val = (request.POST.get('details', '') or '').strip()
            if not details_val:
                messages.error(request, 'Details are required')
                raise ValueError('Details are required')
            # Enforce admin-managed selections
            selected_name = (request.POST.get('name_select') or '').strip()
            selected_category = request.POST.get('category')
            selected_brand = (request.POST.get('brand', '') or '').strip()
            if not selected_name or not selected_category:
                messages.error(request, 'Select Category and Product Name from list')
                raise ValueError('Invalid category/product selection')
            site_products_qs = Product.objects.filter(vendor__isnull=True, name=selected_name)
            try:
                cid_int = int(selected_category)
                site_products_qs = site_products_qs.filter(category_id=cid_int)
            except Exception:
                pass
            if not site_products_qs.exists():
                messages.error(request, 'Select a valid Product Name for chosen Category')
                raise ValueError('Invalid product name/category combination')
            from products.models import Brand
            allowed_brands = list(
                Brand.objects.filter(is_active=True)
                .values_list('name', flat=True)
                .order_by('name')
            )
            if selected_brand and selected_brand not in allowed_brands:
                messages.error(request, 'Select a valid Brand from list')
                raise ValueError('Invalid brand selection')
            product.name = selected_name or product.name
            product.sku = request.POST.get('sku')
            product.category_id = selected_category
            product.description = request.POST.get('description', '')
            product.details = details_val
            product.short_description = request.POST.get('short_description', '')
            product.price = request.POST.get('price')
            product.compare_price = request.POST.get('compare_price') or None
            product.stock = request.POST.get('stock', 0)
            product.brand = selected_brand
            if selected_brand:
                product.brand_ref = Brand.objects.filter(name=selected_brand, is_active=True).first()
            else:
                product.brand_ref = None
            product.video_url = request.POST.get('video_url', '')
            sp = request.POST.get('sale_price') or None
            ss = request.POST.get('sale_start') or None
            se = request.POST.get('sale_end') or None
            product.sale_price = Decimal(sp) if sp else None
            
            # Handle Sale Dates
            new_sale_start = parse_datetime(ss) if ss else None
            new_sale_end = parse_datetime(se) if se else None
            
            if new_sale_start and timezone.is_naive(new_sale_start):
                new_sale_start = timezone.make_aware(new_sale_start)
            if new_sale_end and timezone.is_naive(new_sale_end):
                new_sale_end = timezone.make_aware(new_sale_end)
            
            product.sale_start = new_sale_start
            product.sale_end = new_sale_end
            
            if product.sale_start and product.sale_end and product.sale_start >= product.sale_end:
                messages.warning(request, 'Sale end must be after sale start')
                product.sale_start = None
                product.sale_end = None
            
            # Handle image upload
            if request.FILES.get('featured_image'):
                product.featured_image = request.FILES['featured_image']
            
            product.save()
            
            # Remove selected gallery images
            remove_ids = request.POST.getlist('remove_image_ids[]')
            if remove_ids:
                try:
                    ProductImage.objects.filter(product=product, id__in=remove_ids).delete()
                except Exception:
                    messages.warning(request, 'Some images could not be removed')
            
            # Handle gallery images with admin-controlled limit
            try:
                plan_limit = vendor.subscription_plan.max_images_per_product if vendor.subscription_plan else None
                max_images = plan_limit if (plan_limit and plan_limit > 0) else 10
                existing_count = product.images.count()
                files = request.FILES.getlist('images[]')
                allowed = max(0, max_images - existing_count)
                if len(files) > allowed:
                    messages.warning(request, f'Only {allowed} additional images allowed by admin policy')
                    files = files[:allowed]
                # Recompute existing_count after removals
                existing_count = product.images.count()
                for idx, f in enumerate(files):
                    ProductImage.objects.create(product=product, image=f, order=existing_count + idx + 1)
            except Exception as ve:
                messages.warning(request, f'Gallery images not fully saved: {ve}')
            
            # Set featured image from selected gallery (optional)
            try:
                main_image_id = request.POST.get('main_image_id')
                if main_image_id:
                    try:
                        sel_img = ProductImage.objects.get(id=int(main_image_id), product=product)
                        product.featured_image = sel_img.image
                        product.save(update_fields=['featured_image'])
                    except ProductImage.DoesNotExist:
                        pass
            except Exception:
                pass
            
            # Optional variations create/update/delete (multiple)
            try:
                ids = request.POST.getlist('variation_id[]')
                names = request.POST.getlist('variation_name[]')
                values = request.POST.getlist('variation_value[]')
                sku_suffixes = request.POST.getlist('variation_sku_suffix[]')
                stocks = request.POST.getlist('variation_stock[]')
                price_adjustments = request.POST.getlist('variation_price_adjustment[]')
                lengths = request.POST.getlist('variation_length[]')
                widths = request.POST.getlist('variation_width[]')
                heights = request.POST.getlist('variation_height[]')
                dimension_units = request.POST.getlist('variation_dimension_unit[]')
                quantity_values = request.POST.getlist('variation_quantity_value[]')
                quantity_units = request.POST.getlist('variation_quantity_unit[]')
                
                for i, name in enumerate(names or []):
                    # Determine if update or create
                    var_id = ids[i] if i < len(ids) else ''
                    if var_id:
                        try:
                            variation = ProductVariation.objects.get(id=var_id, product=product)
                        except ProductVariation.DoesNotExist:
                            variation = None
                        if variation:
                            try:
                                pa = Decimal(str((price_adjustments[i] if i < len(price_adjustments) else 0) or 0))
                                st = int((stocks[i] if i < len(stocks) else 0) or 0)
                                ln = lengths[i] if i < len(lengths) else None
                                wd = widths[i] if i < len(widths) else None
                                ht = heights[i] if i < len(heights) else None
                                qv = quantity_values[i] if i < len(quantity_values) else None
                                if pa < 0 or st < 0:
                                    raise ValueError('Negative values not allowed')
                                if ln is not None and ln != '' and Decimal(str(ln)) < 0:
                                    raise ValueError('Invalid length')
                                if wd is not None and wd != '' and Decimal(str(wd)) < 0:
                                    raise ValueError('Invalid width')
                                if ht is not None and ht != '' and Decimal(str(ht)) < 0:
                                    raise ValueError('Invalid height')
                                if qv is not None and qv != '' and Decimal(str(qv)) < 0:
                                    raise ValueError('Invalid quantity')
                                variation.name = name
                                variation.value = (values[i] if i < len(values) else '')
                                variation.price_adjustment = pa
                                variation.stock = st
                                variation.sku_suffix = (sku_suffixes[i] if i < len(sku_suffixes) else '')
                                variation.length = (ln if ln not in (None, '') else None)
                                variation.width = (wd if wd not in (None, '') else None)
                                variation.height = (ht if ht not in (None, '') else None)
                                variation.dimension_unit = (dimension_units[i] if i < len(dimension_units) else 'cm') or 'cm'
                                variation.quantity_value = (qv if qv not in (None, '') else None)
                                variation.quantity_unit = (quantity_units[i] if i < len(quantity_units) else 'pcs') or 'pcs'
                                variation.save()
                            except Exception as ve:
                                messages.warning(request, f'Variation not updated: {ve}')
                            continue
                    # Create new if name/value provided
                    if name or (values and i < len(values) and values[i]):
                        try:
                            pa = Decimal(str((price_adjustments[i] if i < len(price_adjustments) else 0) or 0))
                            st = int((stocks[i] if i < len(stocks) else 0) or 0)
                            ln = lengths[i] if i < len(lengths) else None
                            wd = widths[i] if i < len(widths) else None
                            ht = heights[i] if i < len(heights) else None
                            qv = quantity_values[i] if i < len(quantity_values) else None
                            if pa < 0 or st < 0:
                                raise ValueError('Negative values not allowed')
                            if ln is not None and ln != '' and Decimal(str(ln)) < 0:
                                raise ValueError('Invalid length')
                            if wd is not None and wd != '' and Decimal(str(wd)) < 0:
                                raise ValueError('Invalid width')
                            if ht is not None and ht != '' and Decimal(str(ht)) < 0:
                                raise ValueError('Invalid height')
                            if qv is not None and qv != '' and Decimal(str(qv)) < 0:
                                raise ValueError('Invalid quantity')
                            ProductVariation.objects.create(
                                product=product,
                                name=name,
                                value=(values[i] if i < len(values) else ''),
                                price_adjustment=pa,
                                stock=st,
                                sku_suffix=(sku_suffixes[i] if i < len(sku_suffixes) else ''),
                                length=(ln if ln not in (None, '') else None),
                                width=(wd if wd not in (None, '') else None),
                                height=(ht if ht not in (None, '') else None),
                                dimension_unit=(dimension_units[i] if i < len(dimension_units) else 'cm') or 'cm',
                                quantity_value=(qv if qv not in (None, '') else None),
                                quantity_unit=(quantity_units[i] if i < len(quantity_units) else 'pcs') or 'pcs',
                            )
                        except Exception as ve:
                            messages.warning(request, f'Variation not saved: {ve}')
                # Handle deletions
                remove_ids = request.POST.get('variation_remove_ids', '')
                if remove_ids:
                    for rid in [x for x in remove_ids.split(',') if x]:
                        try:
                            ProductVariation.objects.filter(id=int(rid), product=product).delete()
                        except Exception:
                            pass
            except Exception as ve:
                messages.warning(request, f'Variations not fully saved: {ve}')
            
            messages.success(request, 'Product updated successfully')
            return redirect('vendor:product_list')
            
        except Exception as e:
            messages.error(request, f'Error updating product: {str(e)}')
    
    categories = list(Category.objects.filter(is_active=True).values('id', 'name'))
    plan_limit = vendor.subscription_plan.max_images_per_product if vendor.subscription_plan else None
    images_limit = plan_limit if (plan_limit and plan_limit > 0) else 10
    product_options = Product.objects.filter(vendor__isnull=True).values('unique_code','name','category_id').order_by('name')
    from products.models import Brand
    brand_options = list(
        Brand.objects.filter(is_active=True)
        .values_list('name', flat=True)
        .order_by('name')
    )
    
    context = {
        'vendor': vendor,
        'product': product,
        'categories': categories,
        'is_locked': product.approval_status == 'APPROVED',
        'images_limit': images_limit,
        'product_options': list(product_options),
        'brand_options': brand_options,
    }
    
    return render(request, 'vendor/products/edit.html', context)

@login_required
@vendor_required
def product_view(request, unique_code):
    """View-only product details"""
    vendor = request.user.vendor_profile
    product = get_object_or_404(Product, unique_code=unique_code, vendor=vendor)
    categories = list(Category.objects.filter(is_active=True).values('id', 'name'))
    
    plan_limit = vendor.subscription_plan.max_images_per_product if vendor.subscription_plan else None
    images_limit = plan_limit if (plan_limit and plan_limit > 0) else 10
    
    product_options = Product.objects.filter(vendor__isnull=True).values('unique_code','name','category_id').order_by('name')
    
    from products.models import Brand
    brand_options = list(
        Brand.objects.filter(is_active=True)
        .values_list('name', flat=True)
        .order_by('name')
    )
    
    context = {
        'vendor': vendor,
        'product': product,
        'categories': categories,
        'is_locked': True,
        'view_only': True,
        'images_limit': images_limit,
        'product_options': list(product_options),
        'brand_options': brand_options,
    }
    return render(request, 'vendor/products/edit.html', context)


@login_required
@vendor_required
def product_delete(request, unique_code):
    """Delete product"""
    vendor = request.user.vendor_profile
    product = get_object_or_404(Product, unique_code=unique_code, vendor=vendor)
    
    if request.method == 'POST':
        product_name = product.name
        product.delete()
        messages.success(request, f'Product "{product_name}" deleted successfully')
        return redirect('vendor:product_list')
    
    return HttpResponse(f'<h1>Delete Product</h1><p>Delete {product.name}?</p><form method="post"><button>Delete</button></form>')


@login_required
@vendor_required
def product_submit(request, unique_code):
    """Submit product for review"""
    vendor = request.user.vendor_profile
    product = get_object_or_404(Product, unique_code=unique_code, vendor=vendor)
    
    if product.approval_status == 'DRAFT' or product.approval_status == 'REJECTED':
        product.approval_status = 'PENDING'
        product.save()
        messages.success(request, f'Product "{product.name}" submitted for staff review')
    else:
        messages.warning(request, 'Product is already submitted or approved')
    
    return redirect('vendor:product_list')


@login_required
@vendor_required
def product_update_stock(request, unique_code):
    """Update product stock (vendor can update anytime)"""
    vendor = request.user.vendor_profile
    product = get_object_or_404(Product, unique_code=unique_code, vendor=vendor)
    if request.method != 'POST':
        return redirect('vendor:product_list')
    try:
        new_stock = int(request.POST.get('stock', '0'))
        if new_stock < 0:
            new_stock = 0
        product.stock = new_stock
        product.save(update_fields=['stock'])
        messages.success(request, f'Stock updated to {new_stock} for "{product.name}"')
    except Exception:
        messages.error(request, 'Enter a valid stock number')
    return redirect('vendor:product_list')


@login_required
@vendor_required
def product_toggle_active(request, unique_code):
    """Toggle product live on/off (only if approved)"""
    vendor = request.user.vendor_profile
    product = get_object_or_404(Product, unique_code=unique_code, vendor=vendor)
    if request.method != 'POST':
        return redirect('vendor:product_list')
    if product.approval_status != 'APPROVED':
        messages.error(request, 'Product must be approved before going live')
        return redirect('vendor:product_list')
    product.is_active = not product.is_active
    product.save(update_fields=['is_active'])
    state = 'Live' if product.is_active else 'Offline'
    messages.success(request, f'Product "{product.name}" is now {state}')
    return redirect('vendor:product_list')

@login_required
@vendor_required
def products_bulk_inventory(request):
    """Bulk update stock or live status for selected products"""
    vendor = request.user.vendor_profile
    if request.method != 'POST':
        return redirect('vendor:product_list')
    action = request.POST.get('action', '')
    codes = request.POST.getlist('product_ids')
    codes = [c for c in codes if c]
    
    if not codes:
        messages.warning(request, 'Select at least one product')
        return redirect('vendor:product_list')
    qs = Product.objects.filter(vendor=vendor, unique_code__in=codes)
    updated = 0
    if action == 'set_stock':
        try:
            new_stock = int(request.POST.get('stock_value', '0'))
            if new_stock < 0:
                new_stock = 0
            updated = qs.update(stock=new_stock)
            messages.success(request, f'Stock set to {new_stock} for {updated} product(s)')
        except Exception:
            messages.error(request, 'Enter a valid stock number')
    elif action == 'live_on':
        for p in qs:
            if p.approval_status == 'APPROVED':
                if not p.is_active:
                    p.is_active = True
                    p.save(update_fields=['is_active'])
                    updated += 1
        messages.success(request, f'Live turned ON for {updated} approved product(s)')
    elif action == 'live_off':
        for p in qs:
            if p.is_active:
                p.is_active = False
                p.save(update_fields=['is_active'])
                updated += 1
        messages.success(request, f'Live turned OFF for {updated} product(s)')
    elif action == 'set_sale':
        try:
            sale_percentage = request.POST.get('sale_percentage')
            sale_start = request.POST.get('sale_start')
            sale_end = request.POST.get('sale_end')
            
            pct = Decimal(sale_percentage) if sale_percentage else Decimal('0')
            if pct > 0:
                for p in qs:
                    if p.price:
                        # Set compare_price if not set, to track original price
                        if not p.compare_price:
                            p.compare_price = p.price
                        
                        # Calculate sale price
                        discount = (p.price * pct) / 100
                        p.sale_price = p.price - discount
                        
                        if sale_start:
                            p.sale_start = parse_datetime(sale_start)
                        else:
                            p.sale_start = timezone.now()
                            
                        if sale_end:
                            p.sale_end = parse_datetime(sale_end)
                        else:
                            p.sale_end = None
                            
                        p.save()
                        updated += 1
                messages.success(request, f'Sale applied to {updated} product(s)')
            else:
                # If percentage is 0 or empty, maybe remove sale?
                # Or user might want to clear sale. Let's assume clear sale if 0.
                for p in qs:
                    p.sale_price = None
                    p.sale_start = None
                    p.sale_end = None
                    p.save()
                    updated += 1
                messages.success(request, f'Sale removed from {updated} product(s)')

        except Exception as e:
            messages.error(request, f'Error setting sale: {e}')
    else:
        messages.warning(request, 'Choose a valid bulk action')
    return redirect('vendor:product_list')


# Orders
@login_required
@vendor_required
def order_list(request):
    """List vendor's orders"""
    vendor = request.user.vendor_profile
    
    try:
        days = int(request.GET.get('days', 90))
    except (ValueError, TypeError):
        days = 90
    start_date = timezone.now() - timedelta(days=days)
    category_id = request.GET.get('category') or ''
    product_id = request.GET.get('product') or ''
    
    order_items = OrderItem.objects.select_related('order', 'product')\
        .filter(order__created_at__gte=start_date, product__vendor=vendor).order_by('-created_at')
    if category_id:
        try:
            order_items = order_items.filter(product__category_id=int(category_id))
        except Exception:
            pass
    if product_id:
        try:
            order_items = order_items.filter(product__unique_code=product_id)
        except Exception:
            pass
    
    categories = Category.objects.filter(products__vendor=vendor).distinct().order_by('name')
    product_options = Product.objects.filter(vendor=vendor).values('unique_code', 'name', 'category_id').order_by('name')
    
    context = {
        'vendor': vendor,
        'order_items': order_items,
        'categories': categories,
        'product_options': product_options,
        'selected_category': category_id,
        'selected_product': product_id,
        'days': days,
    }
    
    return render(request, 'vendor/orders/list.html', context)


@login_required
@vendor_required
def order_detail(request, order_code):
    """Order detail"""
    vendor = request.user.vendor_profile
    # Check if order_code is 'None' and handle gracefully
    if order_code == 'None':
        messages.error(request, 'Invalid order code.')
        return redirect('vendor:order_list')
        
    order = get_object_or_404(Order, Q(order_code=order_code) | Q(order_number=order_code))
    
    # Get only this vendor's items from the order
    order_items = OrderItem.objects.filter(
        order=order,
        product__vendor=vendor
    ).select_related('product')
    
    # If no items belong to this vendor, do not show this order here
    if not order_items.exists():
        messages.error(request, 'This order does not contain your products.')
        return redirect('vendor:order_list')
    
    courier_companies = CourierCompany.objects.filter(is_active=True)
    
    context = {
        'vendor': vendor,
        'order': order,
        'order_items': order_items,
        'courier_companies': courier_companies,
    }
    return render(request, 'vendor/orders/detail.html', context)

@login_required
@vendor_required
def order_accept(request, order_code):
    vendor = request.user.vendor_profile
    order = get_object_or_404(Order, Q(order_code=order_code) | Q(order_number=order_code))
    vendor_items = OrderItem.objects.filter(order=order, product__vendor=vendor)
    if not vendor_items.exists():
        messages.error(request, 'This order does not contain your products.')
        return redirect('vendor:order_detail', order_code=order.order_code or order.order_number)
    if request.method == 'POST':
        new_status = Order.OrderStatus.CONFIRMED
        
        # Deduct stock on acceptance
        for item in vendor_items:
            # Only deduct if not already deducted (check if status was PENDING)
            if item.status == Order.OrderStatus.PENDING:
                product = item.product
                if product.track_inventory:
                    if item.variation:
                        variation = item.variation
                        if variation.stock >= item.quantity:
                            variation.stock -= item.quantity
                            variation.save()
                        else:
                            # Log warning or handle error - for now proceed as admin override
                            variation.stock = max(0, variation.stock - item.quantity)
                            variation.save()
                    else:
                        if product.stock >= item.quantity:
                            product.stock -= item.quantity
                            product.save()
                        else:
                            product.stock = max(0, product.stock - item.quantity)
                            product.save()

        vendor_items.update(status=new_status)
        OrderStatusHistory.objects.create(
            order=order,
            status=new_status,
            changed_by=request.user,
            notes='Accepted by vendor'
        )
        statuses = list(order.items.values_list('status', flat=True))
        if statuses and all(s in (Order.OrderStatus.CONFIRMED, Order.OrderStatus.PROCESSING, Order.OrderStatus.SHIPPED, Order.OrderStatus.DELIVERED) for s in statuses):
            order.status = new_status
            order.save(update_fields=['status'])
        messages.success(request, f'Order {order.order_code} updated.')
    return redirect('vendor:order_detail', order_code=order.order_code or order.order_number)

@login_required
@vendor_required
def order_ship(request, order_code):
    vendor = request.user.vendor_profile
    order = get_object_or_404(Order, Q(order_code=order_code) | Q(order_number=order_code))
    vendor_items = OrderItem.objects.filter(order=order, product__vendor=vendor)
    if not vendor_items.exists():
        messages.error(request, 'This order does not contain your products.')
        return redirect('vendor:order_detail', order_code=order.order_code or order.order_number)
    if request.method == 'POST':
        tracking = request.POST.get('tracking_number', '').strip()
        courier_id = request.POST.get('courier_company')
        
        now = timezone.now()
        if not tracking:
            messages.error(request, 'Tracking number is required to mark as shipped.')
            return redirect('vendor:order_detail', order_code=order.order_code or order.order_number)
            
        courier = None
        if courier_id:
            try:
                courier = CourierCompany.objects.get(id=courier_id, is_active=True)
            except CourierCompany.DoesNotExist:
                pass
                
        # Deduct stock if jumping straight to SHIPPED from PENDING
        for item in vendor_items:
            if item.status == Order.OrderStatus.PENDING:
                product = item.product
                if product.track_inventory:
                    if item.variation:
                        variation = item.variation
                        variation.stock = max(0, variation.stock - item.quantity)
                        variation.save()
                    else:
                        product.stock = max(0, product.stock - item.quantity)
                        product.save()

        vendor_items.update(
            status=Order.OrderStatus.SHIPPED, 
            tracking_number=tracking, 
            courier_company=courier,
            shipped_at=now
        )
        statuses = list(order.items.values_list('status', flat=True))
        if statuses and all(s in (Order.OrderStatus.SHIPPED, Order.OrderStatus.DELIVERED) for s in statuses):
            order.status = Order.OrderStatus.SHIPPED
            order.save(update_fields=['status'])
        OrderStatusHistory.objects.create(
            order=order,
            status=Order.OrderStatus.SHIPPED,
            changed_by=request.user,
            notes='Shipped by vendor'
        )
        try:
            if order.user and order.user.email:
                send_mail(
                    subject='Your order has been shipped',
                    message=f'Order {order.order_code} has been shipped.',
                    from_email=getattr(settings, 'DEFAULT_FROM_EMAIL', None),
                    recipient_list=[order.user.email],
                    fail_silently=True
                )
        except Exception:
            pass
        messages.success(request, f'Order {order.order_code} updated as shipped for your items.')
    return redirect('vendor:order_detail', order_code=order.order_code or order.order_number)

@login_required
@vendor_required
def order_process(request, order_code):
    vendor = request.user.vendor_profile
    order = get_object_or_404(Order, Q(order_code=order_code) | Q(order_number=order_code))
    vendor_items = OrderItem.objects.filter(order=order, product__vendor=vendor)
    if not vendor_items.exists():
        messages.error(request, 'This order does not contain your products.')
        return redirect('vendor:order_detail', order_code=order.order_code or order.order_number)
    if request.method == 'POST':
        now = timezone.now()

        # Deduct stock if jumping straight to PROCESSING from PENDING
        for item in vendor_items:
            if item.status == Order.OrderStatus.PENDING:
                product = item.product
                if product.track_inventory:
                    if item.variation:
                        variation = item.variation
                        variation.stock = max(0, variation.stock - item.quantity)
                        variation.save()
                    else:
                        product.stock = max(0, product.stock - item.quantity)
                        product.save()

        vendor_items.update(status=Order.OrderStatus.PROCESSING)
        OrderStatusHistory.objects.create(
            order=order,
            status=Order.OrderStatus.PROCESSING,
            changed_by=request.user,
            notes='Processing by vendor'
        )
        statuses = list(order.items.values_list('status', flat=True))
        if statuses and all(s in (Order.OrderStatus.PROCESSING, Order.OrderStatus.SHIPPED, Order.OrderStatus.DELIVERED) for s in statuses):
            if all(s in (Order.OrderStatus.SHIPPED, Order.OrderStatus.DELIVERED) for s in statuses):
                pass
            else:
                order.status = Order.OrderStatus.PROCESSING
                order.save(update_fields=['status'])
        messages.success(request, f'Order {order.order_code} marked as processing for your items.')
    return redirect('vendor:order_detail', order_code=order.order_code or order.order_number)

@login_required
@vendor_required
def order_cancel(request, order_code):
    vendor = request.user.vendor_profile
    order = get_object_or_404(Order, Q(order_code=order_code) | Q(order_number=order_code))
    vendor_items = OrderItem.objects.filter(order=order, product__vendor=vendor)
    if not vendor_items.exists():
        messages.error(request, 'This order does not contain your products.')
        return redirect('vendor:order_detail', order_code=order.order_code or order.order_number)
        
    if request.method == 'POST':
        # Restore stock if needed
        for item in vendor_items:
            # Check if stock was likely deducted (status is CONFIRMED, PROCESSING, or SHIPPED)
            if item.status in [Order.OrderStatus.CONFIRMED, Order.OrderStatus.PROCESSING, Order.OrderStatus.SHIPPED]:
                product = item.product
                if product.track_inventory:
                    if item.variation:
                        variation = item.variation
                        variation.stock += item.quantity
                        variation.save()
                    else:
                        product.stock += item.quantity
                        product.save()
                        
        vendor_items.update(status=Order.OrderStatus.CANCELLED)
        
        OrderStatusHistory.objects.create(
            order=order,
            status=Order.OrderStatus.CANCELLED,
            changed_by=request.user,
            notes='Cancelled by vendor'
        )
        
        # Check if all items in the order are now cancelled
        statuses = list(order.items.values_list('status', flat=True))
        if statuses and all(s == Order.OrderStatus.CANCELLED for s in statuses):
            order.status = Order.OrderStatus.CANCELLED
            order.save(update_fields=['status'])
            
        messages.success(request, f'Order {order.order_code} has been cancelled.')
        
    return redirect('vendor:order_detail', order_code=order.order_code or order.order_number)

@login_required
@vendor_required
def order_item_ship(request, order_code, item_id):
    vendor = request.user.vendor_profile
    order = get_object_or_404(Order, Q(order_code=order_code) | Q(order_number=order_code))
    item = get_object_or_404(OrderItem, id=item_id, order=order, product__vendor=vendor)
    if request.method == 'POST':
        tracking = request.POST.get('tracking_number', '').strip()
        courier_id = request.POST.get('courier_company')
        
        now = timezone.now()
        if not tracking:
            messages.error(request, 'Tracking number is required to mark this item as shipped.')
            return redirect('vendor:order_detail', order_code=order.order_code or order.order_number)
            
        courier = None
        if courier_id:
            try:
                courier = CourierCompany.objects.get(id=courier_id, is_active=True)
            except CourierCompany.DoesNotExist:
                pass
        
        # Deduct stock if jumping straight to SHIPPED from PENDING
        if item.status == Order.OrderStatus.PENDING:
            product = item.product
            if product.track_inventory:
                if item.variation:
                    variation = item.variation
                    variation.stock = max(0, variation.stock - item.quantity)
                    variation.save()
                else:
                    product.stock = max(0, product.stock - item.quantity)
                    product.save()
                
        item.status = Order.OrderStatus.SHIPPED
        item.shipped_at = now
        item.tracking_number = tracking
        item.courier_company = courier
        item.save(update_fields=['status', 'shipped_at', 'tracking_number', 'courier_company'])
        statuses = list(order.items.values_list('status', flat=True))
        if statuses and all(s in (Order.OrderStatus.SHIPPED, Order.OrderStatus.DELIVERED) for s in statuses):
            order.status = Order.OrderStatus.SHIPPED
            order.save(update_fields=['status'])
        OrderStatusHistory.objects.create(
            order=order,
            status=Order.OrderStatus.SHIPPED,
            changed_by=request.user,
            notes='Item shipped by vendor'
        )
        try:
            if order.user and order.user.email:
                send_mail(
                    subject='Your order item has been shipped',
                    message=f'An item in Order {order.order_code} has been shipped.',
                    from_email=getattr(settings, 'DEFAULT_FROM_EMAIL', None),
                    recipient_list=[order.user.email],
                    fail_silently=True
                )
        except Exception:
            pass
        messages.success(request, 'Item updated as shipped.')
    return redirect('vendor:order_detail', order_code=order.order_code or order.order_number)


# Finances
@login_required
@vendor_required
def finances(request):
    """Financial overview"""
    vendor = request.user.vendor_profile
    
    commissions = VendorCommission.objects.filter(
        vendor=vendor,
        order__items__product__vendor=vendor
    ).distinct()
    
    total_sales = commissions.aggregate(total=Sum('order_total'))['total'] or 0
    total_commission = commissions.aggregate(total=Sum('commission_amount'))['total'] or 0
    total_earnings = commissions.aggregate(total=Sum('vendor_payout'))['total'] or 0
    
    # Paid vs unpaid
    paid_earnings = commissions.filter(is_paid=True).aggregate(total=Sum('vendor_payout'))['total'] or 0
    pending_earnings = commissions.filter(is_paid=False).aggregate(total=Sum('vendor_payout'))['total'] or 0
    total_rows = commissions.count()
    paid_count = commissions.filter(is_paid=True).count()
    pending_count = commissions.filter(is_paid=False).count()
    min_date = commissions.aggregate(min=Min('created_at'))['min']
    max_date = commissions.aggregate(max=Max('created_at'))['max']
    
    context = {
        'vendor': vendor,
        'commissions': commissions.order_by('-created_at')[:20],
        'total_sales': total_sales,
        'total_commission': total_commission,
        'total_earnings': total_earnings,
        'paid_earnings': paid_earnings,
        'pending_earnings': pending_earnings,
        'total_rows': total_rows,
        'paid_count': paid_count,
        'pending_count': pending_count,
        'min_date': min_date,
        'max_date': max_date,
    }
    
    return render(request, 'vendor/finances/overview.html', context)


@login_required
@vendor_required
def payouts(request):
    """Payout requests"""
    vendor = request.user.vendor_profile
    
    payouts = VendorPayout.objects.filter(vendor=vendor).order_by('-created_at')
    
    # Calculate available balance
    unpaid_commissions = VendorCommission.objects.filter(
        vendor=vendor,
        is_paid=False
    ).aggregate(total=Sum('vendor_payout'))['total'] or 0
    
    context = {
        'vendor': vendor,
        'payouts': payouts,
        'available_balance': unpaid_commissions,
    }
    return render(request, 'vendor/payouts.html', context)


@login_required
@vendor_required
def request_payout(request):
    """Request payout"""
    vendor = request.user.vendor_profile
    if request.method != 'POST':
        return redirect('vendor:payouts')
    
    unpaid = VendorCommission.objects.filter(vendor=vendor, is_paid=False).order_by('created_at')
    total = unpaid.aggregate(total=Sum('vendor_payout'))['total'] or 0
    
    if not unpaid.exists() or total <= 0:
        messages.warning(request, 'No available balance for payout')
        return redirect('vendor:payouts')
    
    payout = VendorPayout.objects.create(
        vendor=vendor,
        amount=total,
        status='PENDING'
    )
    payout.commissions.add(*list(unpaid))
    messages.success(request, f'Payout request #{payout.id} created for Rs.{total}')
    return redirect('vendor:payouts')


# Settings
@login_required
@vendor_required
def settings(request):
    """Vendor settings"""
    vendor = request.user.vendor_profile
    if request.method == 'POST':
        form_type = request.POST.get('form_type', 'store')
        if form_type == 'bank':
            if vendor.is_verified or vendor.kyc_approved:
                messages.error(request, 'Payment information is locked after approval. Contact admin for changes.')
                return redirect('vendor:settings')
            bank_account_name = request.POST.get('bank_account_name', '').strip()
            bank_account_number = request.POST.get('bank_account_number', '').strip()
            bank_name = request.POST.get('bank_name', '').strip()
            bank_routing_number = request.POST.get('bank_routing_number', '').strip()
            crossed_cheque_photo = request.FILES.get('crossed_cheque_photo')
            errors = []
            field_errors = {}
            if not bank_account_name:
                errors.append('Account holder name is required')
                field_errors['bank_account_name'] = 'Account holder name is required'
            if not bank_account_number:
                errors.append('Account number is required')
                field_errors['bank_account_number'] = 'Account number is required'
            if not bank_name:
                errors.append('Bank name is required')
                field_errors['bank_name'] = 'Bank name is required'
            if not bank_routing_number:
                errors.append('IFSC / Routing number is required')
                field_errors['bank_routing_number'] = 'IFSC / Routing number is required'
            if errors:
                for e in errors:
                    messages.error(request, e)
                context = {
                    'vendor': vendor,
                    'field_errors': field_errors,
                    'show_store': True,
                    'show_kyc': False,
                }
                return render(request, 'vendor/settings.html', context)
            vendor.bank_account_name = bank_account_name or vendor.bank_account_name
            vendor.bank_account_number = bank_account_number or vendor.bank_account_number
            vendor.bank_name = bank_name or vendor.bank_name
            vendor.bank_routing_number = bank_routing_number or vendor.bank_routing_number
            if crossed_cheque_photo:
                vendor.crossed_cheque_photo = crossed_cheque_photo
            vendor.save()
            messages.success(request, 'Payment information updated')
            return redirect('vendor:settings')
        if vendor.is_verified or vendor.kyc_approved:
            messages.error(request, 'Profile is locked after approval. Contact admin for changes.')
            return redirect('vendor:settings')
        store_name = request.POST.get('store_name', '').strip()
        store_description = request.POST.get('store_description', '').strip()
        business_email = request.POST.get('business_email', '').strip()
        business_phone = request.POST.get('business_phone', '').strip()
        website = request.POST.get('website', '').strip()
        
        errors = []
        field_errors = {}
        if not store_name:
            errors.append('Store name is required')
            field_errors['store_name'] = 'Store name is required'
        try:
            validate_email(business_email)
        except ValidationError:
            errors.append('Enter a valid business email')
            field_errors['business_email'] = 'Enter a valid business email'
        phone_digits = re.sub(r'\D+', '', business_phone)
        if len(phone_digits) < 7 or len(phone_digits) > 20:
            errors.append('Enter a valid business phone')
            field_errors['business_phone'] = 'Enter a valid business phone'
        if website:
            try:
                URLValidator()(website)
            except ValidationError:
                errors.append('Enter a valid website URL')
                field_errors['website'] = 'Enter a valid website URL'
        
        if errors:
            with open('kyc_debug.log', 'a') as f:
                f.write(f"Errors: {errors}\n")
            for e in errors:
                messages.error(request, e)
            context = {
                'vendor': vendor,
                'field_errors': field_errors,
                'show_store': True,
                'show_kyc': False,
            }
            return render(request, 'vendor/settings.html', context)
        else:
            vendor.store_name = store_name or vendor.store_name
            vendor.store_description = store_description or vendor.store_description
            vendor.business_email = business_email or vendor.business_email
            vendor.business_phone = business_phone or vendor.business_phone
            vendor.website = website or vendor.website
            # Optional branding files
            store_logo = request.FILES.get('store_logo')
            store_banner = request.FILES.get('store_banner')
            if store_logo:
                vendor.store_logo = store_logo
            if store_banner:
                vendor.store_banner = store_banner
            vendor.save()
            messages.success(request, 'Settings updated')
            return redirect('vendor:settings')
    
    context = {
        'vendor': vendor,
        'field_errors': {},
        'show_store': True,
        'show_kyc': False,
    }
    return render(request, 'vendor/settings.html', context)


@login_required
@vendor_required
def kyc(request):
    """Vendor KYC submission"""
    vendor = request.user.vendor_profile
    if request.method == 'POST':
        if vendor.kyc_approved or vendor.is_verified:
            messages.error(request, 'KYC is already approved. Contact admin for changes.')
            return redirect('vendor:kyc')
        
        aadhaar_no = request.POST.get('aadhaar_no', '').strip()
        pan_no = request.POST.get('pan_no', '').strip()
        gst_no = request.POST.get('gst_no', '').strip()
        consent_form = request.FILES.get('consent_form')
        self_photo = request.FILES.get('self_photo')
        gst_photo = request.FILES.get('gst_photo')
        kyc_photo = request.FILES.get('kyc_photo')
        kyc_address_proof = request.FILES.get('kyc_address_proof')
        
        errors = []
        if not aadhaar_no:
            errors.append('Aadhaar number is required for KYC submission')
        if not pan_no:
            errors.append('PAN number is required for KYC submission')
        if not gst_no:
            errors.append('GST number is required for KYC submission')
        if not (consent_form or vendor.consent_form):
            errors.append('Consent form is required for KYC submission')
        if not (gst_photo or vendor.gst_photo):
            errors.append('GST certificate image is required for KYC submission')
        if not (kyc_photo or vendor.kyc_photo):
            errors.append('KYC photo is required for KYC submission')
        if not (kyc_address_proof or vendor.kyc_address_proof):
            errors.append('Current address proof image is required for KYC submission')
        if not (self_photo or getattr(vendor.user, 'profile_picture', None)):
            errors.append('Self photo is required for KYC submission')
        
        if errors:
            for e in errors:
                messages.error(request, e)
            context = {
                'vendor': vendor,
                'field_errors': {},
                'show_store': False,
                'show_kyc': True,
            }
            return render(request, 'vendor/settings.html', context)
        
        vendor.aadhaar_no = aadhaar_no
        vendor.pan_no = pan_no
        vendor.gst_no = gst_no
        if consent_form:
            vendor.consent_form = consent_form
        if gst_photo:
            vendor.gst_photo = gst_photo
        if kyc_photo:
            vendor.kyc_photo = kyc_photo
        if kyc_address_proof:
            vendor.kyc_address_proof = kyc_address_proof
        if self_photo:
            vendor.user.profile_picture = self_photo
            vendor.user.save(update_fields=['profile_picture'])
        vendor.kyc_submitted = True
        vendor.save()
        
        messages.success(request, 'KYC details submitted successfully. Awaiting admin approval.')
        return redirect('vendor:kyc')
    
    context = {
        'vendor': vendor,
        'field_errors': {},
        'show_store': False,
        'show_kyc': True,
    }
    return render(request, 'vendor/settings.html', context)


@login_required
@vendor_required
def kyc_consent_form(request):
    vendor = request.user.vendor_profile
    try:
        from reportlab.lib.pagesizes import A4
        from reportlab.pdfgen import canvas
        from reportlab.lib.units import cm
    except Exception:
        return render(request, 'vendor/consent_form.html', {'vendor': vendor})
    buffer = BytesIO()
    pdf = canvas.Canvas(buffer, pagesize=A4)
    width, height = A4
    pdf.saveState()
    pdf.setFont("Helvetica-Bold", 60)
    pdf.setFillGray(0.9)
    pdf.translate(width / 2, height / 2)
    pdf.rotate(45)
    pdf.drawCentredString(0, 0, "AiBiMagics")
    pdf.restoreState()
    y = height - 2 * cm
    pdf.setFont("Helvetica-Bold", 16)
    pdf.drawCentredString(width / 2, y, "Vendor KYC Consent Form")
    y -= 0.8 * cm
    pdf.setFont("Helvetica", 12)
    pdf.drawCentredString(width / 2, y, "AiBiMagics.com")
    y -= 1.4 * cm
    pdf.setFont("Helvetica", 11)
    left = 2 * cm
    label_width = 6 * cm
    photo_box_w = 3 * cm
    photo_box_h = 4 * cm
    photo_x = width - 2 * cm - photo_box_w
    line_right = photo_x - 0.5 * cm
    meta_start_y = y
    pdf.drawString(left, y, "Vendor / Store Name:")
    pdf.line(left + label_width, y - 0.1 * cm, line_right, y - 0.1 * cm)
    y -= 0.8 * cm
    pdf.drawString(left, y, "Authorized Person Name:")
    pdf.line(left + label_width, y - 0.1 * cm, line_right, y - 0.1 * cm)
    y -= 0.8 * cm
    pdf.drawString(left, y, "Business Email:")
    pdf.line(left + label_width, y - 0.1 * cm, line_right, y - 0.1 * cm)
    y -= 0.8 * cm
    pdf.drawString(left, y, "Business Phone:")
    pdf.line(left + label_width, y - 0.1 * cm, line_right, y - 0.1 * cm)
    y -= 0.8 * cm
    pdf.drawString(left, y, "Registered Address:")
    pdf.line(left + label_width, y - 0.1 * cm, line_right, y - 0.1 * cm)
    y -= 0.8 * cm
    pdf.line(left + label_width, y - 0.1 * cm, line_right, y - 0.1 * cm)
    y -= 0.8 * cm
    pdf.line(left + label_width, y - 0.1 * cm, line_right, y - 0.1 * cm)
    y -= 0.8 * cm
    pdf.drawString(left, y, "Aadhaar Number (Authorized Person):")
    pdf.line(left + label_width, y - 0.1 * cm, line_right, y - 0.1 * cm)
    y -= 0.8 * cm
    pdf.drawString(left, y, "GST Number:")
    pdf.line(left + label_width, y - 0.1 * cm, line_right, y - 0.1 * cm)
    meta_end_y = y
    mid_y = (meta_start_y + meta_end_y) / 2
    photo_y = mid_y - photo_box_h / 2
    pdf.rect(photo_x, photo_y, photo_box_w, photo_box_h)
    pdf.setFont("Helvetica", 9)
    pdf.drawCentredString(
        photo_x + photo_box_w / 2,
        photo_y + photo_box_h / 2,
        "Affix Authorized Person Photo"
    )
    y -= 1.4 * cm
    text = pdf.beginText()
    text.setTextOrigin(left, y)
    text.setFont("Helvetica", 11)
    paragraphs = [
        "I / We, the undersigned authorized representative of the above vendor, hereby",
        "consent and declare that:",
        "",
        "1. We are voluntarily submitting our KYC documents (including Aadhaar, PAN, GST",
        "   certificate, business address proof and supporting documents) to AiBiMagics",
        "   for vendor onboarding, verification and compliance purposes.",
        "",
        "2. AiBiMagics is permitted to securely store and process the submitted KYC",
        "   information and use it for risk assessment, compliance, payout processing",
        "   and audit requirements related to our store operations on the platform.",
        "",
        "3. We confirm that all information and documents provided are true, correct and",
        "   valid to the best of our knowledge, and we will promptly update AiBiMagics",
        "   if any details change.",
        "",
        "4. We understand that providing false, incorrect or misleading information may",
        "   lead to suspension or termination of our vendor account and withholding of",
        "   payouts as per the platform policies.",
    ]
    for line in paragraphs:
        text.textLine(line)
    pdf.drawText(text)
    y = 4.2 * cm
    pdf.setFont("Helvetica", 11)
    pdf.drawString(left, y, "Date:")
    pdf.line(left + 1.7 * cm, y - 0.1 * cm, width / 2 - cm, y - 0.1 * cm)
    sig_y = 2.4 * cm
    block_width = (width - 4 * cm) / 2
    witness_y = sig_y + 1.1 * cm
    pdf.line(left, witness_y, left + block_width, witness_y)
    pdf.drawString(left, witness_y - 0.6 * cm, "Witness Signature")
    right_witness_x = left + block_width + 1.5 * cm
    pdf.line(right_witness_x, witness_y, right_witness_x + block_width, witness_y)
    pdf.drawString(right_witness_x, witness_y - 0.6 * cm, "Witness Name")
    pdf.line(left, sig_y, left + block_width, sig_y)
    pdf.drawString(left, sig_y - 0.6 * cm, "Authorized Signatory Signature")
    right_x = left + block_width + 1.5 * cm
    pdf.line(right_x, sig_y, right_x + block_width, sig_y)
    pdf.drawString(right_x, sig_y - 0.6 * cm, "Name and Designation")
    pdf.showPage()
    pdf.save()
    pdf_bytes = buffer.getvalue()
    buffer.close()
    response = HttpResponse(pdf_bytes, content_type="application/pdf")
    response["Content-Disposition"] = 'attachment; filename=\"vendor_kyc_consent_form.pdf\"'
    return response

@login_required
@vendor_required
def stores_list(request):
    vendor = request.user.vendor_profile
    stores = VendorStore.objects.filter(vendor=vendor).order_by('name')
    can_create = stores.count() < (vendor.store_limit or 0)
    return render(request, 'vendor/stores/list.html', {
        'vendor': vendor,
        'stores': stores,
        'can_create': can_create,
    })

@login_required
@vendor_required
def stores_add(request):
    vendor = request.user.vendor_profile
    current = VendorStore.objects.filter(vendor=vendor).count()
    if current >= (vendor.store_limit or 0):
        messages.error(request, 'Store limit reached. Contact admin to increase your limit.')
        return redirect('vendor:stores_list')
    if not (vendor.is_verified or vendor.kyc_approved):
        messages.error(request, 'Complete KYC and wait for admin approval to create stores.')
        return redirect('vendor:stores_list')
    if request.method == 'POST':
        name = request.POST.get('name', '').strip()
        description = request.POST.get('description', '').strip()
        email = request.POST.get('email', '').strip()
        phone = request.POST.get('phone', '').strip()
        website = request.POST.get('website', '').strip()
        errors = []
        if not name:
            errors.append('Store name is required')
        if email:
            try:
                validate_email(email)
            except ValidationError:
                errors.append('Enter a valid store email')
        phone_digits = re.sub(r'\D+', '', phone or '')
        if phone and (len(phone_digits) < 7 or len(phone_digits) > 20):
            errors.append('Enter a valid store phone')
        if website:
            try:
                URLValidator()(website)
            except ValidationError:
                errors.append('Enter a valid website URL')
        if errors:
            for e in errors:
                messages.error(request, e)
            return render(request, 'vendor/stores/add.html', {
                'vendor': vendor,
            })
        VendorStore.objects.create(
            vendor=vendor,
            name=name,
            description=description,
            email=email or '',
            phone=phone or '',
            website=website or '',
        )
        messages.success(request, 'Store created')
        return redirect('vendor:stores_list')
    return render(request, 'vendor/stores/add.html', {
        'vendor': vendor,
    })
