# sales/filters.py
"""
Comprehensive filtering for Sales with support for all filter types
ENHANCED VERSION - Now supports brand, model, and category filters
"""
from django_filters import rest_framework as filters
from django.db.models import Q
from datetime import datetime, timedelta
from django.utils import timezone
from decimal import Decimal

from .models import Sale


class SaleFilter(filters.FilterSet):
    """
    Comprehensive filter for Sales supporting all frontend filter options
    ENHANCED: Now supports brand, model, and category product filters
    """
    
    # Date range filters
    date_range = filters.CharFilter(method='filter_date_range', label='Date Range')
    start_date = filters.DateTimeFilter(method='filter_start_date', label='Start Date')
    end_date = filters.DateTimeFilter(method='filter_end_date', label='End Date')
    
    # Status filters
    status = filters.CharFilter(method='filter_status', label='Sale Status')
    payment_status = filters.CharFilter(method='filter_payment_status', label='Payment Status')
    
    # Payment method filter
    payment_method = filters.CharFilter(method='filter_payment_method', label='Payment Method')
    
    # Amount range filters
    min_amount = filters.NumberFilter(field_name='total_amount', lookup_expr='gte', label='Minimum Amount')
    max_amount = filters.NumberFilter(field_name='total_amount', lookup_expr='lte', label='Maximum Amount')
    
    # Related entity filters
    customer_id = filters.CharFilter(method='filter_customer', label='Customer ID')
    agent_id = filters.CharFilter(method='filter_agent', label='Sales Agent ID')
    location_id = filters.CharFilter(method='filter_location', label='Location ID')
    
    # Product filters - ENHANCED
    product_id = filters.CharFilter(method='filter_product', label='Product ID')
    brand = filters.CharFilter(method='filter_brand', label='Product Brand')
    model = filters.CharFilter(method='filter_model', label='Product Model')
    category = filters.CharFilter(method='filter_category', label='Product Category')
    
    # Discount filter
    has_discount = filters.BooleanFilter(method='filter_has_discount', label='Has Discount')
    
    # Search
    search = filters.CharFilter(method='filter_search', label='Search')
    
    class Meta:
        model = Sale
        fields = [
            'date_range', 'start_date', 'end_date',
            'status', 'payment_status', 'payment_method',
            'min_amount', 'max_amount',
            'customer_id', 'agent_id', 'location_id', 
            'product_id', 'brand', 'model', 'category',
            'has_discount', 'search'
        ]
    
    def filter_date_range(self, queryset, name, value):
        """Filter by predefined date ranges"""
        now = timezone.now()
        
        if value == 'today':
            start = now.replace(hour=0, minute=0, second=0, microsecond=0)
            end = now
        elif value == 'yesterday':
            start = (now - timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0)
            end = now.replace(hour=0, minute=0, second=0, microsecond=0)
        elif value == 'last_7_days':
            start = now - timedelta(days=7)
            end = now
        elif value == 'last_30_days':
            start = now - timedelta(days=30)
            end = now
        elif value == 'this_month':
            start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
            end = now
        elif value == 'last_month':
            first_current = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
            end = first_current - timedelta(seconds=1)
            start = end.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
        elif value == 'custom':
            return queryset
        else:
            return queryset
        
        return queryset.filter(sale_date__range=[start, end])
    
    def filter_start_date(self, queryset, name, value):
        """Filter sales from start date"""
        if value:
            if isinstance(value, str):
                value = datetime.fromisoformat(value.replace('Z', '+00:00'))
            return queryset.filter(sale_date__gte=value)
        return queryset
    
    def filter_end_date(self, queryset, name, value):
        """Filter sales up to end date"""
        if value:
            if isinstance(value, str):
                value = datetime.fromisoformat(value.replace('Z', '+00:00'))
            return queryset.filter(sale_date__lte=value)
        return queryset
    
    def filter_status(self, queryset, name, value):
        """Filter by sale status"""
        if not value or value == 'all':
            return queryset
        
        status_map = {
            'completed': 'COMPLETED',
            'pending': 'PENDING',
            'cancelled': 'CANCELLED',
            'partially_returned': 'PARTIALLY_RETURNED',
            'fully_returned': 'FULLY_RETURNED',
        }
        
        backend_status = status_map.get(value.lower(), value.upper())
        return queryset.filter(status=backend_status)
    
    def filter_payment_status(self, queryset, name, value):
        """Filter by payment status"""
        if not value or value == 'all':
            return queryset
        
        value_lower = value.lower()
        
        if value_lower == 'completed':
            return queryset.filter(amount_due=0)
        elif value_lower == 'pending':
            return queryset.filter(amount_due__gt=0)
        elif value_lower == 'failed':
            return queryset.filter(status='CANCELLED')
        elif value_lower == 'refunded':
            return queryset.filter(
                Q(status='PARTIALLY_RETURNED') | Q(status='FULLY_RETURNED')
            )
        
        return queryset
    
    def filter_payment_method(self, queryset, name, value):
        """Filter by payment method"""
        if not value or value == 'all':
            return queryset
        
        method_map = {
            'cash': 'CASH',
            'mpesa': 'MPESA',
            'card': 'CARD',
            'bank': 'BANK',
            'bank_transfer': 'BANK',
            'credit': 'CREDIT',
            'mobile': 'MPESA',
        }
        
        payment_mode = method_map.get(value.lower(), value.upper())
        
        if payment_mode:
            return queryset.filter(
                payments__payment_mode__name=payment_mode
            ).distinct()
        elif value.lower() == 'split':
            from django.db.models import Count
            return queryset.annotate(
                payment_count=Count('payments')
            ).filter(payment_count__gt=1)
        
        return queryset
    
    def filter_customer(self, queryset, name, value):
        """Filter sales by customer"""
        if not value:
            return queryset
        
        try:
            return queryset.filter(customer__id=value)
        except Exception as e:
            print(f"Error filtering by customer_id={value}: {e}")
            return queryset.none()
    
    def filter_agent(self, queryset, name, value):
        """Filter sales by sales agent"""
        if not value:
            return queryset
        
        try:
            return queryset.filter(salesperson__id=value)
        except Exception as e:
            print(f"Error filtering by agent_id={value}: {e}")
            return queryset.none()
    
    def filter_location(self, queryset, name, value):
        """Filter sales by location"""
        if not value:
            return queryset
        
        try:
            return queryset.filter(location__id=value)
        except Exception as e:
            print(f"Error filtering by location_id={value}: {e}")
            return queryset.none()
    
    def filter_product(self, queryset, name, value):
        """Filter sales containing a specific product by ID"""
        if not value:
            return queryset
        
        try:
            return queryset.filter(
                Q(items__inventory_item__id=value) | 
                Q(items__accessory_item__id=value)
            ).distinct()
        except Exception as e:
            print(f"Error filtering by product_id={value}: {e}")
            return queryset.none()
    
    def filter_brand(self, queryset, name, value):
        """
        ENHANCED: Filter sales by product brand
        Works for both InventoryItem and Accessory brands
        """
        if not value:
            return queryset
        
        try:
            # Filter by inventory item brand or accessory brand
            return queryset.filter(
                Q(items__inventory_item__brand__iexact=value) |
                Q(items__accessory_item__brand__iexact=value)
            ).distinct()
        except Exception as e:
            print(f"Error filtering by brand={value}: {e}")
            return queryset.none()
    
    def filter_model(self, queryset, name, value):
        """
        ENHANCED: Filter sales by product model
        Works for both InventoryItem and Accessory models
        """
        if not value:
            return queryset
        
        try:
            # Filter by inventory item model or accessory model
            return queryset.filter(
                Q(items__inventory_item__model__iexact=value) |
                Q(items__accessory_item__model__iexact=value)
            ).distinct()
        except Exception as e:
            print(f"Error filtering by model={value}: {e}")
            return queryset.none()
    
    def filter_category(self, queryset, name, value):
        """
        ENHANCED: Filter sales by product category
        Works for both InventoryItem and Accessory categories
        """
        if not value:
            return queryset
        
        try:
            # Filter by inventory item category or accessory category
            return queryset.filter(
                Q(items__inventory_item__category__iexact=value) |
                Q(items__accessory_item__category__iexact=value)
            ).distinct()
        except Exception as e:
            print(f"Error filtering by category={value}: {e}")
            return queryset.none()
    
    def filter_has_discount(self, queryset, name, value):
        """Filter sales with or without discounts"""
        if value is None:
            return queryset
        
        if value:
            return queryset.filter(discount_amount__gt=0)
        else:
            return queryset.filter(discount_amount=0)
    
    def filter_search(self, queryset, name, value):
        """
        Search across multiple fields
        ENHANCED: Now includes brand, model, and category in search
        """
        if not value:
            return queryset
        
        search_query = Q()
        search_term = value.strip()
        
        # Search sale number
        search_query |= Q(sale_number__icontains=search_term)
        
        # Search customer
        search_query |= Q(customer__name__icontains=search_term)
        search_query |= Q(customer__phone__icontains=search_term)
        
        # Search product details - ENHANCED
        search_query |= Q(items__item_name__icontains=search_term)
        search_query |= Q(items__inventory_item__brand__icontains=search_term)
        search_query |= Q(items__inventory_item__model__icontains=search_term)
        search_query |= Q(items__inventory_item__category__icontains=search_term)
        search_query |= Q(items__inventory_item__serial_number__icontains=search_term)
        
        # Search accessory items
        search_query |= Q(items__accessory_item__name__icontains=search_term)
        search_query |= Q(items__accessory_item__brand__icontains=search_term)
        search_query |= Q(items__accessory_item__model__icontains=search_term)
        search_query |= Q(items__accessory_item__category__icontains=search_term)
        
        return queryset.filter(search_query).distinct()


from rest_framework import filters as drf_filters

class SalesFilterBackend(drf_filters.BaseFilterBackend):
    """
    Custom filter backend for advanced sales filtering
    ENHANCED: Supports brand, model, and category filters
    """
    
    def filter_queryset(self, request, queryset, view):
        """Apply all filters from query parameters"""
        # Get filter parameters
        date_range = request.query_params.get('date_range')
        start_date = request.query_params.get('start_date')
        end_date = request.query_params.get('end_date')
        status = request.query_params.get('status')
        payment_status = request.query_params.get('payment_status')
        payment_method = request.query_params.get('payment_method')
        min_amount = request.query_params.get('min_amount')
        max_amount = request.query_params.get('max_amount')
        customer_id = request.query_params.get('customer_id')
        agent_id = request.query_params.get('agent_id')
        location_id = request.query_params.get('location_id')
        product_id = request.query_params.get('product_id')
        brand = request.query_params.get('brand')  # ENHANCED
        model = request.query_params.get('model')  # ENHANCED
        category = request.query_params.get('category')  # ENHANCED
        has_discount = request.query_params.get('has_discount')
        search = request.query_params.get('search')
        
        # Apply date range filter
        queryset = self._apply_date_filter(
            queryset, date_range, start_date, end_date
        )
        
        # Apply status filters
        if status and status != 'all':
            status_map = {
                'completed': 'COMPLETED',
                'pending': 'PENDING',
                'cancelled': 'CANCELLED',
                'partially_returned': 'PARTIALLY_RETURNED',
                'fully_returned': 'FULLY_RETURNED',
            }
            backend_status = status_map.get(status.lower(), status.upper())
            queryset = queryset.filter(status=backend_status)
        
        # Apply payment status filter
        if payment_status and payment_status != 'all':
            queryset = self._apply_payment_status_filter(queryset, payment_status)
        
        # Apply payment method filter
        if payment_method and payment_method != 'all':
            queryset = self._apply_payment_method_filter(queryset, payment_method)
        
        # Apply amount range filters
        if min_amount:
            try:
                queryset = queryset.filter(total_amount__gte=Decimal(min_amount))
            except (ValueError, TypeError):
                pass
        
        if max_amount:
            try:
                queryset = queryset.filter(total_amount__lte=Decimal(max_amount))
            except (ValueError, TypeError):
                pass
        
        # Apply entity filters
        if customer_id:
            try:
                queryset = queryset.filter(customer__id=customer_id)
            except Exception as e:
                print(f"Error filtering by customer_id: {e}")
        
        if agent_id:
            try:
                queryset = queryset.filter(salesperson__id=agent_id)
            except Exception as e:
                print(f"Error filtering by agent_id: {e}")
        
        if location_id:
            try:
                queryset = queryset.filter(location__id=location_id)
            except Exception as e:
                print(f"Error filtering by location_id: {e}")
        
        # Apply product filters - ENHANCED
        if product_id:
            try:
                queryset = queryset.filter(
                    Q(items__inventory_item__id=product_id) |
                    Q(items__accessory_item__id=product_id)
                ).distinct()
            except Exception as e:
                print(f"Error filtering by product_id: {e}")
        
        # Brand filter
        if brand:
            try:
                queryset = queryset.filter(
                    Q(items__inventory_item__brand__iexact=brand) |
                    Q(items__accessory_item__brand__iexact=brand)
                ).distinct()
            except Exception as e:
                print(f"Error filtering by brand: {e}")
        
        # Model filter
        if model:
            try:
                queryset = queryset.filter(
                    Q(items__inventory_item__model__iexact=model) |
                    Q(items__accessory_item__model__iexact=model)
                ).distinct()
            except Exception as e:
                print(f"Error filtering by model: {e}")
        
        # Category filter
        if category:
            try:
                queryset = queryset.filter(
                    Q(items__inventory_item__category__iexact=category) |
                    Q(items__accessory_item__category__iexact=category)
                ).distinct()
            except Exception as e:
                print(f"Error filtering by category: {e}")
        
        # Apply discount filter
        if has_discount is not None:
            has_discount_bool = has_discount.lower() in ['true', '1', 'yes']
            if has_discount_bool:
                queryset = queryset.filter(discount_amount__gt=0)
            else:
                queryset = queryset.filter(discount_amount=0)
        
        # Apply search filter
        if search:
            queryset = self._apply_search_filter(queryset, search)
        
        return queryset
    
    def _apply_date_filter(self, queryset, date_range, start_date, end_date):
        """Apply date filtering logic"""
        now = timezone.now()
        
        if date_range and date_range != 'custom':
            if date_range == 'today':
                start = now.replace(hour=0, minute=0, second=0, microsecond=0)
                end = now
            elif date_range == 'yesterday':
                start = (now - timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0)
                end = now.replace(hour=0, minute=0, second=0, microsecond=0)
            elif date_range == 'last_7_days':
                start = now - timedelta(days=7)
                end = now
            elif date_range == 'last_30_days':
                start = now - timedelta(days=30)
                end = now
            elif date_range == 'this_month':
                start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
                end = now
            elif date_range == 'last_month':
                first_current = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
                end = first_current - timedelta(seconds=1)
                start = end.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
            else:
                return queryset
            
            queryset = queryset.filter(sale_date__range=[start, end])
        
        elif start_date or end_date:
            if start_date:
                try:
                    start_dt = datetime.fromisoformat(start_date.replace('Z', '+00:00'))
                    queryset = queryset.filter(sale_date__gte=start_dt)
                except (ValueError, AttributeError):
                    pass
            
            if end_date:
                try:
                    end_dt = datetime.fromisoformat(end_date.replace('Z', '+00:00'))
                    queryset = queryset.filter(sale_date__lte=end_dt)
                except (ValueError, AttributeError):
                    pass
        
        return queryset
    
    def _apply_payment_status_filter(self, queryset, payment_status):
        """Apply payment status filtering"""
        if payment_status == 'completed':
            return queryset.filter(amount_due=0)
        elif payment_status == 'pending':
            return queryset.filter(amount_due__gt=0)
        elif payment_status == 'failed':
            return queryset.filter(status='CANCELLED')
        elif payment_status == 'refunded':
            return queryset.filter(
                Q(status='PARTIALLY_RETURNED') | Q(status='FULLY_RETURNED')
            )
        return queryset
    
    def _apply_payment_method_filter(self, queryset, payment_method):
        """Apply payment method filtering"""
        method_map = {
            'cash': 'CASH',
            'mpesa': 'MPESA',
            'card': 'CARD',
            'bank': 'BANK',
            'bank_transfer': 'BANK',
            'credit': 'CREDIT',
            'mobile': 'MPESA',
        }
        
        payment_mode = method_map.get(payment_method.lower(), payment_method.upper())
        
        if payment_method.lower() == 'split':
            from django.db.models import Count
            return queryset.annotate(
                payment_count=Count('payments')
            ).filter(payment_count__gt=1)
        else:
            return queryset.filter(
                payments__payment_mode__name=payment_mode
            ).distinct()
    
    def _apply_search_filter(self, queryset, search):
        """
        Apply search across multiple fields
        ENHANCED: Includes brand, model, category
        """
        search_query = Q()
        search_term = search.strip()
        
        search_query |= Q(sale_number__icontains=search_term)
        search_query |= Q(customer__name__icontains=search_term)
        search_query |= Q(customer__phone__icontains=search_term)
        search_query |= Q(items__item_name__icontains=search_term)
        search_query |= Q(items__inventory_item__brand__icontains=search_term)
        search_query |= Q(items__inventory_item__model__icontains=search_term)
        search_query |= Q(items__inventory_item__category__icontains=search_term)
        search_query |= Q(items__inventory_item__serial_number__icontains=search_term)
        search_query |= Q(items__accessory_item__name__icontains=search_term)
        search_query |= Q(items__accessory_item__brand__icontains=search_term)
        search_query |= Q(items__accessory_item__model__icontains=search_term)
        search_query |= Q(items__accessory_item__category__icontains=search_term)
        
        return queryset.filter(search_query).distinct()