from django.shortcuts import render, redirect, get_object_or_404
from django.http import JsonResponse, HttpResponse
from django.core.paginator import Paginator
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_POST
from django.urls import reverse
from reportlab.pdfgen import canvas
from reportlab.graphics.barcode import code39
from reportlab.lib.pagesizes import letter
from .models import Product, Category, Batch, ProductImage
from .forms import ProductForm, CategoryForm, BatchForm
from django.db.models import Sum, Q, F
import csv
import io
import json
from django.db import transaction
from django.db.utils import IntegrityError


@login_required(login_url='/auth/login/')
def category_list(request):
    # Get parent categories for the original categories list
    categories = Category.objects.filter(parent__isnull=True).prefetch_related('subcategories', 'products')

    # Get all categories (including subcategories) for display
    all_categories = Category.objects.all().prefetch_related('subcategories', 'products', 'parent').order_by('parent', 'name')

    form = CategoryForm()
    return render(request, 'inventory/category_list.html', {
        'categories': categories,
        'all_categories': all_categories,
        'form': form
    })


@login_required(login_url='/auth/login/')
def add_category(request):
    if request.method == 'POST':
        form = CategoryForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('category_list')
    else:
        form = CategoryForm()
    return render(request, 'inventory/add_category.html', {'form': form})


@login_required(login_url='/auth/login/')
def edit_category(request, pk):
    category = get_object_or_404(Category, pk=pk)
    if request.method == 'POST':
        form = CategoryForm(request.POST, instance=category)
        if form.is_valid():
            form.save()
            return redirect('category_list')
    else:
        form = CategoryForm(instance=category)
    return render(request, 'inventory/edit_category.html', {'form': form})


@login_required(login_url='/auth/login/')
def delete_category(request, category_id):
    category = get_object_or_404(Category, pk=category_id)
    if request.method == 'POST':
        category.delete()
        messages.success(request, f'Category "{category.name}" has been deleted successfully.')
        return redirect('category_list')
    return redirect('category_list')


@login_required(login_url='/auth/login/')
def product_list(request):
    # Get all categories for the filter dropdown
    categories = Category.objects.all()

    # Start with all products
    products = Product.objects.all().order_by('name')  # Add default ordering

    # Apply filters
    category_id = request.GET.get('category')
    if category_id:
        products = products.filter(category_id=category_id)

    status = request.GET.get('status')
    if status == 'active':
        products = products.filter(is_active=True)
    elif status == 'inactive':
        products = products.filter(is_active=False)
    elif status == 'low_stock':
        products = products.filter(stock_quantity__lte=F('low_stock_threshold'))

    # Search functionality - case insensitive
    search_query = request.GET.get('search')
    if search_query:
        products = products.filter(
            Q(name__icontains=search_query) |
            Q(barcode__icontains=search_query) |
            Q(description__icontains=search_query)
        )

    # Sorting
    sort_by = request.GET.get('sort', 'name')
    if sort_by == 'price_asc':
        products = products.order_by('selling_price')
    elif sort_by == 'price_desc':
        products = products.order_by('-selling_price')
    elif sort_by == 'stock_asc':
        products = products.order_by('stock_quantity')
    elif sort_by == 'stock_desc':
        products = products.order_by('-stock_quantity')
    elif sort_by == 'date_asc':
        products = products.order_by('created_at')
    elif sort_by == 'date_desc':
        products = products.order_by('-created_at')
    else:  # default to name
        products = products.order_by('name')

    context = {
        'products': products,
        'categories': categories,
        'current_category': category_id,
        'current_status': status,
        'current_sort': sort_by,
        'search_query': search_query,
    }

    return render(request, 'inventory/product_list.html', context)


@login_required(login_url='/auth/login/')
def search_products_ajax(request):
    """AJAX endpoint for product search"""
    query = request.GET.get('q', '').strip()

    if len(query) < 2:
        return JsonResponse([], safe=False)

    products = Product.objects.filter(
        Q(name__icontains=query) |
        Q(barcode__icontains=query) |
        Q(description__icontains=query),
        is_active=True
    )[:10]  # Limit to 10 results for performance

    results = []
    for product in products:
        results.append({
            'id': product.id,
            'name': product.name,
            'barcode': product.barcode or '',
            'price': str(product.selling_price),
            'stock': product.stock_quantity,
            'url': reverse('product_detail', args=[product.id])
        })

    return JsonResponse(results, safe=False)


@login_required(login_url='/auth/login/')
def product_detail(request, pk):
    product = get_object_or_404(Product, pk=pk)
    batches = product.batches.all()

    return render(request, 'inventory/product_detail.html', {
        'product': product,
        'batches': batches,
    })


@login_required(login_url='/auth/login/')
def add_product(request):
    if request.method == 'POST':
        form = ProductForm(request.POST, request.FILES)
        if form.is_valid():
            product = form.save(commit=False)

            # Handle primary image
            if 'image' in request.FILES:
                product.image = request.FILES['image']

            product.save()

            # Handle additional images
            images = request.FILES.getlist('images')
            for image in images:
                ProductImage.objects.create(product=product, image=image)

            messages.success(request, 'Product added successfully!')
            return redirect('product_list')
        else:
            messages.error(request, 'There was an error adding the product.')
    else:
        form = ProductForm()

    categories = Category.objects.all()
    return render(request, 'inventory/add_product.html', {
        'form': form,
        'categories': categories,
    })


@login_required(login_url='/auth/login/')
def edit_product(request, pk):
    product = get_object_or_404(Product, pk=pk)
    if request.method == 'POST':
        form = ProductForm(request.POST, request.FILES, instance=product)
        if form.is_valid():
            product = form.save(commit=False)

            # Handle primary image
            if 'image' in request.FILES:
                product.image = request.FILES['image']

            product.save()

            # Handle additional images
            images = request.FILES.getlist('images')
            for image in images:
                ProductImage.objects.create(product=product, image=image)

            messages.success(request, 'Product updated successfully!')
            return redirect('product_list')
        else:
            messages.error(request, 'There was an error updating the product.')
    else:
        form = ProductForm(instance=product)

    categories = Category.objects.all()
    return render(request, 'inventory/edit_product.html', {
        'form': form,
        'product': product,
        'categories': categories,
    })


@login_required(login_url='/auth/login/')
def delete_product(request, pk):
    product = get_object_or_404(Product, pk=pk)
    product.delete()
    return redirect('product_list')


@login_required(login_url='/auth/login/')
def receive_stock(request, product_id):
    product = get_object_or_404(Product, id=product_id)

    if request.method == 'POST':
        form = BatchForm(request.POST)
        if form.is_valid():
            batch = form.save(commit=False)
            batch.product = product
            batch.batch_code = batch.generate_batch_code()
            batch.save()

            # Update product stock quantity
            product.stock_quantity += batch.quantity
            product.save()

            messages.success(request, 'Stock received successfully!')
            return redirect('product_detail', pk=product.id)
    else:
        form = BatchForm()

    return render(request, 'inventory/receive_stock.html', {
        'product': product,
        'form': form,
    })


@login_required(login_url='/auth/login/')
def export_products(request):
    products = Product.objects.all()
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="products.csv"'

    writer = csv.writer(response)
    writer.writerow(['Barcode', 'Name', 'Buying Price', 'Selling Price', 'Stock Quantity', 'Expiry Date', 'Packaging Type', 'Category'])

    for product in products:
        writer.writerow([
            product.barcode,
            product.name,
            product.buying_price,
            product.selling_price,
            product.stock_quantity,
            product.expiry_date,
            product.packaging_type,
            product.category.name if product.category else 'Uncategorized'
        ])

    return response


from datetime import datetime

@login_required(login_url='/auth/login/')
def import_products(request):
    if request.method == 'POST':
        csv_file = request.FILES.get('imported_file')

        if not csv_file.name.endswith('.csv'):
            messages.error(request, 'File is not CSV type')
            return redirect('product_list')

        try:
            data_set = csv_file.read().decode('UTF-8')
            io_string = io.StringIO(data_set)
            next(io_string)  # Skip header row

            with transaction.atomic():
                for row_number, column in enumerate(csv.reader(io_string, delimiter=','), start=2):
                    try:
                        # Validate required fields
                        if len(column) < 4:  # Ensure at least 4 columns (barcode, name, selling_price, stock_quantity)
                            messages.error(request, f"Row {row_number}: Missing required fields.")
                            continue

                        # Parse fields
                        barcode = column[0].strip() if column[0] else None
                        name = column[1].strip()
                        selling_price = float(column[2].strip())
                        stock_quantity = int(column[3].strip())

                        # Optional fields
                        buying_price = float(column[4].strip()) if len(column) > 4 and column[4].strip() else None
                        expiry_date = datetime.strptime(column[5].strip(), '%Y-%m-%d').date() if len(column) > 5 and column[5].strip() else None
                        packaging_type = column[6].strip() if len(column) > 6 else None
                        category_name = column[7].strip() if len(column) > 7 else 'Uncategorized'

                        # Get or create category
                        category, _ = Category.objects.get_or_create(name=category_name)

                        # Create or update product
                        Product.objects.update_or_create(
                            barcode=barcode,
                            defaults={
                                'name': name,
                                'buying_price': buying_price,
                                'selling_price': selling_price,
                                'stock_quantity': stock_quantity,
                                'expiry_date': expiry_date,
                                'packaging_type': packaging_type,
                                'category': category,
                            }
                        )
                    except Exception as e:
                        messages.error(request, f"Error on row {row_number}: {str(e)}")
                        continue

            messages.success(request, 'Products imported successfully!')
        except Exception as e:
            messages.error(request, f"Error processing file: {str(e)}")

    return redirect('product_list')


@login_required(login_url='/auth/login/')
def product_live_search(request):
    query = request.GET.get('q', '').strip()  # Get the query
    if query:
        try:
            # Filter for products matching the query in barcode or name
            products = Product.objects.filter(
                Q(name__icontains=query) | Q(barcode__icontains=query)
            ).values('id', 'name', 'barcode', 'selling_price', 'stock_quantity')
            results = list(products)  # Convert queryset to a list of dictionaries
        except Exception as e:
            results = []  # Handle any unexpected errors gracefully
    else:
        results = []

    return JsonResponse(results, safe=False)


@require_POST
@login_required(login_url='/auth/login/')
def delete_selected_products(request):
    try:
        product_ids = request.POST.get('selected_products', '')
        if product_ids:
            ids = product_ids.split(',')
            Product.objects.filter(id__in=ids).delete()
            messages.success(request, "Selected products have been deleted successfully.")
        else:
            messages.error(request, "No products were selected for deletion.")
    except Exception as e:
        messages.error(request, f"An error occurred: {str(e)}")

    return redirect('product_list')



@login_required(login_url='/auth/login/')
def print_barcodes(request):
    # Retrieve selected product IDs from the request (assuming they're passed in via GET or POST)
    product_ids = request.GET.getlist('ids', [])  # or use request.POST.getlist('ids') if you are using POST
    quantity = int(request.GET.get('quantity', 1))  # Number of barcodes to print per product, default to 1

    products = Product.objects.filter(id__in=product_ids)

    # Create PDF response
    response = HttpResponse(content_type='application/pdf')
    response['Content-Disposition'] = 'attachment; filename="barcodes.pdf"'

    # Initialize ReportLab PDF canvas
    c = canvas.Canvas(response, pagesize=letter)
    width, height = letter

    # Y position for the first barcode
    y_position = height - 100

    for product in products:
        # Generate the barcode for the product (using Code39 as an example)
        barcode = code39.Extended39(product.barcode, barHeight=20, barWidth=0.5, checksum=False)

        # Draw the barcodes multiple times, if specified
        for _ in range(quantity):
            if y_position < 50:  # Start a new page if we've reached the bottom of the current one
                c.showPage()
                y_position = height - 100

            # Draw the barcode on the canvas
            barcode.drawOn(c, 100, y_position)
            c.drawString(100, y_position - 20, f"{product.name} - {product.barcode}")
            y_position -= 100  # Move down for the next barcode

    # Save the canvas
    c.save()
    return response