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 django.core.exceptions import PermissionDenied
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
import openpyxl
from openpyxl.styles import Font, Alignment
from django.template.loader import render_to_string


@login_required(login_url='/auth/login/')
def category_list(request):
    # Schema separation provides tenant isolation - no tenant filtering needed
    
    # 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):
    # Schema separation provides tenant isolation - no tenant assignment needed
    
    if request.method == 'POST':
        form = CategoryForm(request.POST)
        if form.is_valid():
            # Schema isolation automatically ensures tenant separation
            category = 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):
    # Schema separation provides tenant isolation - no tenant filtering needed
    
    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):
    # Schema separation provides tenant isolation - no tenant filtering needed
    
    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):
    # Schema separation provides tenant isolation - no tenant filtering needed
    
    # Get all categories for the filter dropdown
    categories = Category.objects.all()

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

    # 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"""
    # Schema separation provides tenant isolation - no tenant filtering needed
    
    query = request.GET.get('q', '').strip()

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

    # Schema isolation provides tenant filtering automatically
    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):
    # Schema separation provides tenant isolation - no tenant filtering needed
    
    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):
    # Schema separation provides tenant isolation - no tenant assignment needed
    
    if request.method == 'POST':
        form = ProductForm(request.POST, request.FILES)
        if form.is_valid():
            product = form.save(commit=False)
            
            # Schema isolation automatically ensures tenant separation

            # 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()

    # Schema isolation provides tenant filtering automatically
    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):
    # Schema separation provides tenant isolation - no tenant filtering needed
    
    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)

    # Schema isolation provides tenant filtering automatically
    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):
    # Schema separation provides tenant isolation - no tenant filtering needed
    
    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):
    # Schema separation provides tenant isolation - no tenant filtering needed
    
    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):
    # Schema separation provides tenant isolation - no tenant filtering needed
    
    # Schema isolation automatically provides tenant filtering
    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


@login_required(login_url='/auth/login/')
def product_import(request):
    """Handle product import from Excel file"""
    if request.method == 'POST':
        try:
            excel_file = request.FILES.get('excel_file')
            if not excel_file:
                messages.error(request, 'Please select an Excel file to upload.')
                return render(request, 'inventory/product_import.html')
            
            if not excel_file.name.endswith('.xlsx'):
                messages.error(request, 'Please upload a valid Excel file (.xlsx only).')
                return render(request, 'inventory/product_import.html')
            
            # Read Excel file
            workbook = openpyxl.load_workbook(excel_file)
            worksheet = workbook.active
            
            imported_count = 0
            error_count = 0
            errors = []
            
            # Skip header row (row 1)
            for row_num, row in enumerate(worksheet.iter_rows(min_row=2, values_only=True), start=2):
                try:
                    if not any(row):  # Skip empty rows
                        continue
                        
                    name, barcode, category_name, buying_price, selling_price, stock_quantity, low_stock_threshold, description = row[:8]
                    
                    if not name:
                        errors.append(f"Row {row_num}: Product name is required")
                        error_count += 1
                        continue
                    
                    # Get or create category
                    category = None
                    if category_name:
                        category, created = Category.objects.get_or_create(name=category_name.strip())
                    
                    # Create or update product
                    product_data = {
                        'name': name.strip(),
                        'barcode': barcode.strip() if barcode else '',
                        'category': category,
                        'buying_price': float(buying_price) if buying_price else 0,
                        'selling_price': float(selling_price) if selling_price else 0,
                        'stock_quantity': int(stock_quantity) if stock_quantity else 0,
                        'low_stock_threshold': int(low_stock_threshold) if low_stock_threshold else 5,
                        'description': description.strip() if description else '',
                        'is_active': True
                    }
                    
                    # Check if product exists by name or barcode
                    existing_product = None
                    if barcode:
                        existing_product = Product.objects.filter(barcode=barcode.strip()).first()
                    if not existing_product and name:
                        existing_product = Product.objects.filter(name=name.strip()).first()
                    
                    if existing_product:
                        # Update existing product
                        for key, value in product_data.items():
                            setattr(existing_product, key, value)
                        existing_product.save()
                    else:
                        # Create new product
                        Product.objects.create(**product_data)
                    
                    imported_count += 1
                    
                except Exception as e:
                    errors.append(f"Row {row_num}: {str(e)}")
                    error_count += 1
            
            if imported_count > 0:
                messages.success(request, f'Successfully imported {imported_count} products.')
            if error_count > 0:
                messages.warning(request, f'{error_count} rows had errors. See details below.')
                for error in errors[:10]:  # Show first 10 errors
                    messages.error(request, error)
                    
        except Exception as e:
            messages.error(request, f'Error processing file: {str(e)}')
    
    return render(request, 'inventory/product_import.html')


@login_required(login_url='/auth/login/')
def product_export(request):
    """Export products to Excel file"""
    try:
        # Create workbook and worksheet
        workbook = openpyxl.Workbook()
        worksheet = workbook.active
        worksheet.title = "Products"
        
        # Define headers
        headers = [
            'Product Name', 'Barcode', 'Category', 'Buying Price', 'Selling Price',
            'Stock Quantity', 'Low Stock Threshold', 'Description', 'Active', 'Created Date'
        ]
        
        # Add headers to worksheet
        for col_num, header in enumerate(headers, 1):
            cell = worksheet.cell(row=1, column=col_num)
            cell.value = header
            cell.font = Font(bold=True)
            cell.alignment = Alignment(horizontal='center')
        
        # Add product data
        products = Product.objects.select_related('category').order_by('name')
        for row_num, product in enumerate(products, start=2):
            worksheet.cell(row=row_num, column=1, value=product.name)
            worksheet.cell(row=row_num, column=2, value=product.barcode or '')
            worksheet.cell(row=row_num, column=3, value=product.category.name if product.category else '')
            worksheet.cell(row=row_num, column=4, value=float(product.buying_price))
            worksheet.cell(row=row_num, column=5, value=float(product.selling_price))
            worksheet.cell(row=row_num, column=6, value=product.stock_quantity)
            worksheet.cell(row=row_num, column=7, value=product.low_stock_threshold)
            worksheet.cell(row=row_num, column=8, value=product.description or '')
            worksheet.cell(row=row_num, column=9, value='Yes' if product.is_active else 'No')
            worksheet.cell(row=row_num, column=10, value=product.created_at.strftime('%Y-%m-%d'))
        
        # Auto-adjust column widths
        for col in worksheet.columns:
            max_length = 0
            column = col[0].column_letter
            for cell in col:
                try:
                    if len(str(cell.value)) > max_length:
                        max_length = len(str(cell.value))
                except:
                    pass
            adjusted_width = (max_length + 2) * 1.2
            worksheet.column_dimensions[column].width = adjusted_width
        
        # Create response
        response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
        response['Content-Disposition'] = 'attachment; filename="products_export.xlsx"'
        
        workbook.save(response)
        return response
        
    except Exception as e:
        messages.error(request, f'Error exporting products: {str(e)}')
        return redirect('product_list')


@login_required(login_url='/auth/login/')
def download_sample_template(request):
    """Download sample Excel template for product import"""
    try:
        # Create workbook and worksheet
        workbook = openpyxl.Workbook()
        worksheet = workbook.active
        worksheet.title = "Product Import Template"
        
        # Define headers
        headers = [
            'Product Name', 'Barcode', 'Category', 'Buying Price', 'Selling Price',
            'Stock Quantity', 'Low Stock Threshold', 'Description'
        ]
        
        # Add headers to worksheet
        for col_num, header in enumerate(headers, 1):
            cell = worksheet.cell(row=1, column=col_num)
            cell.value = header
            cell.font = Font(bold=True)
            cell.alignment = Alignment(horizontal='center')
        
        # Add sample data
        sample_data = [
            ['Sample Product 1', '1234567890123', 'Electronics', 50.00, 75.00, 100, 10, 'Sample description for product 1'],
            ['Sample Product 2', '1234567890124', 'Clothing', 20.00, 35.00, 50, 5, 'Sample description for product 2'],
            ['Sample Product 3', '', 'Books', 10.00, 15.00, 200, 20, 'Leave barcode empty for auto-generation']
        ]
        
        for row_num, row_data in enumerate(sample_data, start=2):
            for col_num, value in enumerate(row_data, start=1):
                worksheet.cell(row=row_num, column=col_num, value=value)
        
        # Auto-adjust column widths
        for col in worksheet.columns:
            max_length = 0
            column = col[0].column_letter
            for cell in col:
                try:
                    if len(str(cell.value)) > max_length:
                        max_length = len(str(cell.value))
                except:
                    pass
            adjusted_width = (max_length + 2) * 1.2
            worksheet.column_dimensions[column].width = adjusted_width
        
        # Create response
        response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
        response['Content-Disposition'] = 'attachment; filename="product_import_template.xlsx"'
        
        workbook.save(response)
        return response
        
    except Exception as e:
        messages.error(request, f'Error creating template: {str(e)}')
        return redirect('product_list')