# core/dashboard_views.py

from django.utils import timezone
from django.db.models import Sum
from django.shortcuts import render
from datetime import timedelta, datetime, date as date_type, time as time_type
from decimal import Decimal
from .models import (
    StockLevel, TransactionItem, ProfitRecord,
    Location, LocationCashBalance, Transaction,
    Expense, SalesTarget, Purchase,
    MatatuIncome, MatatuExpense,
)
from django.core.exceptions import PermissionDenied


def dashboard_view(request):
    if not request.user.is_authenticated or not request.user.is_superuser:
        raise PermissionDenied

    period       = request.GET.get('period', 'daily')
    custom_start = request.GET.get('start')
    custom_end   = request.GET.get('end')

    # ✅ Nairobi local time — all date logic is in Nairobi time
    now = timezone.localtime(timezone.now())

    period_start, period_end, period_label, period_range = get_period_range(
        period, now, custom_start, custom_end
    )

    # ── Date boundaries ───────────────────────────────────────────────────
    # d_start / d_end  → plain date objects for DateField queries (__gte / __lte)
    # dt_start / dt_end → aware datetimes for DateTimeField queries (__gte / __lt)
    #
    # period_start = midnight on first day  (aware, Nairobi)
    # period_end   = midnight on last day   (aware, Nairobi)  ← same as start for daily
    #
    # For DateTimeField we need dt_end = start of the NEXT day so __lt catches
    # everything up to 23:59:59 of the last day.

    d_start  = period_start.date()
    d_end    = period_end.date()                        # last day (inclusive) for DateField
    dt_start = period_start                             # midnight, first day
    dt_end   = period_end + timedelta(days=1)           # midnight, day AFTER last day

    # =====================================================================
    # 📊 SALES & PROFIT  (DateTimeField → __gte / __lt with dt_end)
    # =====================================================================
    total_sales_period = (
        TransactionItem.objects.filter(
            transaction__created_at__gte=dt_start,
            transaction__created_at__lt=dt_end,         # ✅ next-day midnight
        ).aggregate(total=Sum('line_total'))['total'] or 0
    )

    gross_profit_period = (
        ProfitRecord.objects.filter(
            created_at__gte=dt_start,
            created_at__lt=dt_end,                      # ✅ next-day midnight
        ).aggregate(total=Sum('profit'))['total'] or 0
    )

    total_profit_overall = (
        ProfitRecord.objects.aggregate(total=Sum('profit'))['total'] or 0
    )

    # =====================================================================
    # 💸 SHOE-STORE EXPENSES  (DateField → __gte / __lte)
    # =====================================================================
    business_expenses_period = (
        Expense.objects.filter(
            date__gte=d_start,
            date__lte=d_end,
            expense_type='business',
        ).aggregate(total=Sum('amount'))['total'] or Decimal('0.00')
    )

    personal_expenses_period = (
        Expense.objects.filter(
            date__gte=d_start,
            date__lte=d_end,
            expense_type='personal',
        ).aggregate(total=Sum('amount'))['total'] or Decimal('0.00')
    )

    total_expenses_period = business_expenses_period + personal_expenses_period

    expense_by_category = (
        Expense.objects.filter(
            date__gte=d_start,
            date__lte=d_end,
        )
        .values('category__name', 'expense_type')
        .annotate(total_amount=Sum('amount'))
        .order_by('-total_amount')
    )
    business_expense_breakdown = [e for e in expense_by_category if e['expense_type'] == 'business']
    personal_expense_breakdown  = [e for e in expense_by_category if e['expense_type'] == 'personal']

    # =====================================================================
    # 🚐 MATATU  (DateField → __gte / __lte)
    # =====================================================================
    matatu_income_period = (
        MatatuIncome.objects.filter(
            date__gte=d_start,
            date__lte=d_end,
        ).aggregate(total=Sum('amount'))['total'] or Decimal('0.00')
    )

    matatu_expense_period = (
        MatatuExpense.objects.filter(
            date__gte=d_start,
            date__lte=d_end,
        ).aggregate(total=Sum('amount'))['total'] or Decimal('0.00')
    )

    matatu_net_period = matatu_income_period - matatu_expense_period

    matatu_expense_by_cat = (
        MatatuExpense.objects.filter(
            date__gte=d_start,
            date__lte=d_end,
        )
        .values('category')
        .annotate(total=Sum('amount'))
        .order_by('-total')
    )
    matatu_cat_labels = dict(MatatuExpense.CATEGORY_CHOICES)
    matatu_expense_breakdown = [
        {
            'category_label': matatu_cat_labels.get(row['category'], row['category']),
            'total': row['total'],
        }
        for row in matatu_expense_by_cat
    ]

    # All-time matatu totals
    matatu_income_alltime  = MatatuIncome.objects.aggregate(t=Sum('amount'))['t']  or Decimal('0.00')
    matatu_expense_alltime = MatatuExpense.objects.aggregate(t=Sum('amount'))['t'] or Decimal('0.00')
    matatu_net_alltime     = matatu_income_alltime - matatu_expense_alltime

    # =====================================================================
    # 💎 NET PROFIT
    # =====================================================================
    net_profit_period = Decimal(str(gross_profit_period)) - total_expenses_period

    if net_profit_period > 0:
        profit_status       = 'profitable'
        profit_status_text  = '✅ Profitable'
        profit_status_color = '#10b981'
    elif net_profit_period == 0:
        profit_status       = 'breakeven'
        profit_status_text  = '⚖️ Break Even'
        profit_status_color = '#f59e0b'
    else:
        profit_status       = 'loss'
        profit_status_text  = '🔴 Operating at Loss'
        profit_status_color = '#ef4444'

    # =====================================================================
    # 🎯 SALES TARGETS
    # =====================================================================
    location_targets = {}
    all_locations = Location.objects.all()

    for location in all_locations:
        target = SalesTarget.objects.filter(location=location, is_active=True).first()
        if target:
            current_sales = target.get_current_sales()
            bonus         = target.calculate_bonus()
            location_targets[location.name] = {
                'target_amount':    target.target_amount,
                'current_sales':    current_sales,
                'bonus_amount':     bonus,
                'bonus_percentage': target.bonus_percentage,
                'target_met':       current_sales >= target.target_amount,
                'progress':         target.get_progress_percentage(),
                'remaining':        max(target.target_amount - current_sales, Decimal('0.00')),
            }

    # =====================================================================
    # 📦 STOCK VALUATION
    # =====================================================================
    stock_valuation_by_location = {}
    for location in all_locations:
        stocks      = StockLevel.objects.filter(location=location).select_related('product')
        total_value = sum(
            s.quantity * (s.product.buying_price or Decimal('0.00'))
            for s in stocks
        )
        paid_p   = Purchase.objects.filter(destination_location=location, payment_status='paid').aggregate(t=Sum('total_amount'))['t']   or Decimal('0.00')
        credit_p = Purchase.objects.filter(destination_location=location, payment_status='credit').aggregate(t=Sum('total_amount'))['t'] or Decimal('0.00')
        stock_valuation_by_location[location.name] = {
            'total_value':      total_value,
            'paid_purchases':   paid_p,
            'credit_purchases': credit_p,
            'total_units':      sum(s.quantity for s in stocks),
        }

    # =====================================================================
    # 💳 CREDIT PURCHASES (this calendar month in Nairobi time)
    # =====================================================================
    month_start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
    credit_qs   = Purchase.objects.filter(payment_status='credit', purchase_date__gte=month_start)

    total_credit_amount = credit_qs.aggregate(t=Sum('total_amount'))['t'] or Decimal('0.00')
    total_interest      = sum(
        (p.total_amount - p.total_amount / Decimal('1.01'))
        for p in credit_qs
    )

    credit_summary = {
        'total_credit':   total_credit_amount,
        'total_interest': total_interest,
        'net_credit':     total_credit_amount - total_interest,
        'count':          credit_qs.count(),
        'purchases':      credit_qs.select_related('destination_location', 'paid_from_location'),
    }

    # =====================================================================
    # 💰 CASH BALANCES
    # =====================================================================
    cash_by_location = {}
    grand_total_cash = LocationCashBalance.objects.aggregate(t=Sum('balance'))['t'] or Decimal('0.00')

    for location in all_locations:
        balances = LocationCashBalance.objects.filter(location=location)
        if not balances.exists():
            continue
        cash_by_location[location.name] = {
            'total': balances.aggregate(t=Sum('balance'))['t'] or Decimal('0.00'),
            'icon':  '🚚' if location.is_truck else '🏪',
            'methods': [
                {
                    'method': b.get_payment_method_display(),
                    'icon':   {'cash': '💵', 'mpesa_paybill': '📱', 'mpesa_till': '🏪', 'bank_transfer': '🏦'}.get(b.payment_method, '💳'),
                    'amount': b.balance,
                }
                for b in balances
            ],
        }

    # =====================================================================
    # OTHER
    # =====================================================================
    profit_per_product = (
        ProfitRecord.objects.filter(
            created_at__gte=dt_start,
            created_at__lt=dt_end,                      # ✅ next-day midnight
        )
        .values('product__id', 'product__name')
        .annotate(total_profit=Sum('profit'))
        .order_by('-total_profit')
    )

    stock_levels = (
        StockLevel.objects
        .select_related('product', 'location')
        .order_by('location__name', 'product__name')
    )

    global_stock = (
        StockLevel.objects
        .values('product__id', 'product__name')
        .annotate(total_qty=Sum('quantity'))
        .order_by('product__name')
    )

    # =====================================================================
    # CONTEXT
    # =====================================================================
    context = {
        # Period
        'current_period': period,
        'period_label':   period_label,
        'period_range':   period_range,
        'custom_start':   custom_start or '',
        'custom_end':     custom_end   or '',

        # Sales
        'total_sales_today':    float(total_sales_period),
        'gross_profit_today':   float(gross_profit_period),
        'total_profit_overall': float(total_profit_overall),

        # Shoe-store expenses
        'business_expenses':          float(business_expenses_period),
        'personal_expenses':          float(personal_expenses_period),
        'total_expenses':             float(total_expenses_period),
        'business_expense_breakdown': business_expense_breakdown,
        'personal_expense_breakdown': personal_expense_breakdown,

        # Net profit
        'net_profit':          float(net_profit_period),
        'profit_status':       profit_status,
        'profit_status_text':  profit_status_text,
        'profit_status_color': profit_status_color,

        # 🚐 Matatu — period
        'matatu_income':            float(matatu_income_period),
        'matatu_expense':           float(matatu_expense_period),
        'matatu_net':               float(matatu_net_period),
        'matatu_expense_breakdown': matatu_expense_breakdown,
        # 🚐 Matatu — all-time
        'matatu_income_alltime':  float(matatu_income_alltime),
        'matatu_expense_alltime': float(matatu_expense_alltime),
        'matatu_net_alltime':     float(matatu_net_alltime),

        # Targets & valuation
        'location_targets':            location_targets,
        'stock_valuation_by_location': stock_valuation_by_location,
        'credit_summary':              credit_summary,

        # Cash
        'cash_by_location': cash_by_location,
        'grand_total_cash': grand_total_cash,

        # Other
        'profit_per_product': profit_per_product,
        'stock_levels':       stock_levels,
        'global_stock':       global_stock,
        'last_sync':          now,
    }

    return render(request, 'admin/dashboard.html', context)


# ── Period helper ─────────────────────────────────────────────────────────────

def get_period_range(period, now, custom_start=None, custom_end=None):
    """
    `now` is already Africa/Nairobi local time.
    Returns (period_start, period_end, label, date_range_text).

    period_start = midnight on the FIRST day of the period  (aware, Nairobi)
    period_end   = midnight on the LAST day of the period   (aware, Nairobi)

    DateField queries  → use d_start=period_start.date(), d_end=period_end.date()
                          with __gte / __lte
    DateTimeField queries → use dt_end = period_end + timedelta(days=1)
                             with __gte / __lt   (done in dashboard_view)
    """
    if period == 'custom' and custom_start and custom_end:
        try:
            start = timezone.make_aware(
                datetime.strptime(custom_start, '%Y-%m-%d')
                .replace(hour=0, minute=0, second=0, microsecond=0)
            )
            end = timezone.make_aware(
                datetime.strptime(custom_end, '%Y-%m-%d')
                .replace(hour=0, minute=0, second=0, microsecond=0)
            )
            label      = 'Custom Range'
            date_range = f"{start.strftime('%B %d, %Y')} - {end.strftime('%B %d, %Y')}"
            return start, end, label, date_range
        except (ValueError, TypeError):
            period = 'daily'

    if period == 'weekly':
        start      = (now - timedelta(days=now.weekday())).replace(hour=0, minute=0, second=0, microsecond=0)
        end        = start + timedelta(days=6)
        label      = 'This Week'
        date_range = f"{start.strftime('%B %d')} - {end.strftime('%B %d, %Y')}"

    elif period == 'monthly':
        start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
        if start.month == 12:
            end = start.replace(year=start.year + 1, month=1, day=1) - timedelta(days=1)
        else:
            end = start.replace(month=start.month + 1, day=1) - timedelta(days=1)
        label      = now.strftime('%B %Y')
        date_range = f"{start.strftime('%B %d')} - {end.strftime('%B %d, %Y')}"

    elif period == 'quarterly':
        q           = (now.month - 1) // 3 + 1
        first_month = (q - 1) * 3 + 1
        start = now.replace(month=first_month, day=1, hour=0, minute=0, second=0, microsecond=0)
        if q == 4:
            end = now.replace(year=now.year + 1, month=1, day=1) - timedelta(days=1)
        else:
            end = now.replace(month=first_month + 3, day=1) - timedelta(days=1)
        label      = f'Q{q} {now.year}'
        date_range = f"{start.strftime('%B %d')} - {end.strftime('%B %d, %Y')}"

    elif period == 'yearly':
        start      = now.replace(month=1,  day=1,  hour=0, minute=0, second=0, microsecond=0)
        end        = now.replace(month=12, day=31, hour=0, minute=0, second=0, microsecond=0)
        label      = str(now.year)
        date_range = f'January 1 - December 31, {now.year}'

    else:  # daily
        start      = now.replace(hour=0, minute=0, second=0, microsecond=0)
        end        = start          # midnight today; dt_end in view adds +1 day for datetimes
        label      = 'Today'
        date_range = now.strftime('%B %d, %Y')

    return start, end, label, date_range