import datetime

from django.db.models import DecimalField, F, Sum
from django.db.models.functions import Coalesce
from django.utils import timezone
from rest_framework.exceptions import ValidationError

from expenses.models import Expense
from inventory.models import SparePart
from sales.models import Sale, SaleItem

MONEY = DecimalField(max_digits=18, decimal_places=2)


def resolve_date_range(request):
    """Resolve a (from_date, to_date) pair from `from`/`to` or a `period` shortcut."""
    today = timezone.localdate()
    period = request.query_params.get('period')

    if period:
        if period == 'today':
            return today, today
        if period == 'yesterday':
            yesterday = today - datetime.timedelta(days=1)
            return yesterday, yesterday
        if period == 'this_week':
            return today - datetime.timedelta(days=today.weekday()), today
        if period == 'this_month':
            return today.replace(day=1), today
        if period == 'this_year':
            return today.replace(month=1, day=1), today
        raise ValidationError({'period': f'Unknown period: {period}'})

    from_param = request.query_params.get('from')
    to_param = request.query_params.get('to')
    try:
        from_date = datetime.date.fromisoformat(from_param) if from_param else today
        to_date = datetime.date.fromisoformat(to_param) if to_param else today
    except ValueError:
        raise ValidationError({'detail': 'from/to must be in YYYY-MM-DD format.'})
    return from_date, to_date


def _sale_items_in_range(from_date, to_date):
    return SaleItem.objects.filter(
        sale__sold_at__date__gte=from_date,
        sale__sold_at__date__lte=to_date,
    )


def profit_report(from_date, to_date):
    items = _sale_items_in_range(from_date, to_date)
    totals = items.aggregate(
        revenue=Coalesce(Sum(F('quantity') * F('unit_price'), output_field=MONEY), 0, output_field=MONEY),
        cost=Coalesce(Sum(F('quantity') * F('unit_cost'), output_field=MONEY), 0, output_field=MONEY),
    )
    revenue = totals['revenue']
    cost = totals['cost']
    gross_profit = revenue - cost
    expenses_total = Expense.objects.filter(
        expense_date__gte=from_date, expense_date__lte=to_date,
    ).aggregate(total=Coalesce(Sum('amount'), 0, output_field=MONEY))['total']
    number_of_sales = items.values('sale_id').distinct().count()

    return {
        'period': {'from': from_date, 'to': to_date},
        'total_sales': revenue,
        'cost_of_goods_sold': cost,
        'gross_profit': gross_profit,
        'expenses': expenses_total,
        'net_profit': gross_profit - expenses_total,
        'number_of_sales': number_of_sales,
    }


def sales_report(from_date, to_date):
    items = _sale_items_in_range(from_date, to_date)
    daily = (
        items.values('sale__sold_at__date')
        .annotate(
            total_sales=Coalesce(Sum(F('quantity') * F('unit_price'), output_field=MONEY), 0, output_field=MONEY),
            number_of_sales=Sum('quantity'),
        )
        .order_by('sale__sold_at__date')
    )
    return {
        'period': {'from': from_date, 'to': to_date},
        'daily': [
            {
                'date': row['sale__sold_at__date'],
                'total_sales': row['total_sales'],
                'items_sold': row['number_of_sales'],
            }
            for row in daily
        ],
    }


def inventory_report():
    parts = SparePart.objects.all()
    totals = parts.aggregate(
        stock_value=Coalesce(Sum(F('quantity') * F('buying_price'), output_field=MONEY), 0, output_field=MONEY),
        total_quantity=Coalesce(Sum('quantity'), 0),
    )
    low_stock = list(
        parts.filter(quantity__lte=F('minimum_stock')).values('id', 'name', 'part_number', 'quantity', 'minimum_stock')
    )
    return {
        'total_parts': parts.count(),
        'total_quantity': totals['total_quantity'],
        'stock_value': totals['stock_value'],
        'low_stock_count': len(low_stock),
        'low_stock': low_stock,
        'out_of_stock_count': parts.filter(quantity=0).count(),
    }


def top_selling_report(from_date, to_date, limit=10):
    items = _sale_items_in_range(from_date, to_date)
    top = (
        items.values('spare_part_id', 'spare_part__name', 'spare_part__part_number')
        .annotate(
            quantity_sold=Sum('quantity'),
            revenue=Coalesce(Sum(F('quantity') * F('unit_price'), output_field=MONEY), 0, output_field=MONEY),
        )
        .order_by('-quantity_sold')[:limit]
    )
    return {
        'period': {'from': from_date, 'to': to_date},
        'top_selling': [
            {
                'spare_part': row['spare_part_id'],
                'name': row['spare_part__name'],
                'part_number': row['spare_part__part_number'],
                'quantity_sold': row['quantity_sold'],
                'revenue': row['revenue'],
            }
            for row in top
        ],
    }


def dashboard_report():
    today = timezone.localdate()
    month_start = today.replace(day=1)
    today_profit = profit_report(today, today)
    month_profit = profit_report(month_start, today)
    inventory = inventory_report()

    return {
        'today': {
            'sales_amount': today_profit['total_sales'],
            'profit': today_profit['gross_profit'],
            'transactions': today_profit['number_of_sales'],
        },
        'inventory': {
            'total_products': inventory['total_parts'],
            'total_units': inventory['total_quantity'],
            'low_stock': inventory['low_stock_count'],
            'out_of_stock': inventory['out_of_stock_count'],
            'stock_value': inventory['stock_value'],
        },
        'month': {
            'sales': month_profit['total_sales'],
            'gross_profit': month_profit['gross_profit'],
            'expenses': month_profit['expenses'],
            'net_profit': month_profit['net_profit'],
        },
    }


def my_dashboard_report(user):
    """Seller-scoped dashboard: today's own sales, no business-wide profit."""
    today = timezone.localdate()
    my_items_today = SaleItem.objects.filter(sale__sold_by=user, sale__sold_at__date=today)
    totals = my_items_today.aggregate(
        sales_amount=Coalesce(Sum(F('quantity') * F('unit_price'), output_field=MONEY), 0, output_field=MONEY),
    )
    transactions = my_items_today.values('sale_id').distinct().count()

    recent_sales = Sale.objects.filter(sold_by=user).prefetch_related('items')[:5]

    return {
        'today': {
            'sales_amount': totals['sales_amount'],
            'transactions': transactions,
        },
        'recent_sales': [
            {
                'id': sale.id,
                'invoice_number': sale.invoice_number,
                'total_amount': sale.total_amount,
                'sold_at': sale.sold_at,
            }
            for sale in recent_sales
        ],
        'low_stock_count': SparePart.objects.filter(
            is_active=True, quantity__lte=F('minimum_stock'),
        ).count(),
    }
