"""Customer-facing spending analytics — built entirely from their own real
order history (pickker_orders / pickker_order_items), so a customer or
business can see what they've spent, what they buy most, and budget ahead."""

from collections import defaultdict
from datetime import datetime, timedelta, timezone
from decimal import Decimal

from accounts.supabase_client import get_client

_EXCLUDED_STATUSES = {'cancelled', 'pending_payment'}  # not real completed spend yet


def _since_iso(days):
    return (datetime.now(timezone.utc) - timedelta(days=days)).isoformat()


def _customer_orders(customer_id, days, orders=None):
    """`orders` lets a caller that already fetched a wider window (see
    get_spending_dashboard) reuse it instead of a fresh round trip — this
    function still does its own query when called standalone."""
    if orders is None:
        resp = (
            get_client()
            .table('pickker_orders')
            .select('id, total_amount, created_at, status')
            .eq('customer_id', customer_id)
            .gte('created_at', _since_iso(days))
            .execute()
        )
        orders = resp.data
    cutoff = _since_iso(days)
    return [o for o in orders if o['status'] not in _EXCLUDED_STATUSES and o['created_at'] >= cutoff]


def get_spending_summary(customer_id, days=30, orders=None):
    orders = _customer_orders(customer_id, days, orders)
    total = sum((Decimal(str(o['total_amount'])) for o in orders), Decimal('0'))
    count = len(orders)
    return {
        'total_spent': total,
        'order_count': count,
        'avg_order': (total / count) if count else Decimal('0'),
        'days': days,
    }


def get_top_products(customer_id, days=90, limit=5, orders=None):
    """Ranks products this customer has bought by total spend, so they can
    see what they spend the most on and budget/plan around it."""
    orders = _customer_orders(customer_id, days, orders)
    order_ids = [o['id'] for o in orders]
    if not order_ids:
        return []

    items = (
        get_client()
        .table('pickker_order_items')
        .select('product_name, quantity, line_total, order_id')
        .in_('order_id', order_ids)
        .execute()
        .data
    )

    agg = defaultdict(lambda: {'quantity': 0, 'spend': Decimal('0'), 'order_count': 0})
    orders_per_product = defaultdict(set)
    for it in items:
        name = it['product_name']
        agg[name]['quantity'] += it['quantity']
        agg[name]['spend'] += Decimal(str(it['line_total']))
        orders_per_product[name].add(it['order_id'])

    ranked = sorted(agg.items(), key=lambda kv: kv[1]['spend'], reverse=True)
    return [
        {
            'name': name,
            'quantity': data['quantity'],
            'spend': data['spend'],
            'order_frequency': len(orders_per_product[name]),
        }
        for name, data in ranked[:limit]
    ]


def get_spending_trend(customer_id, weeks=8, orders=None):
    """Total spend per week for the last N weeks, oldest first — a simple
    trend so a customer/business can see whether their spending is rising."""
    days = weeks * 7
    orders = _customer_orders(customer_id, days, orders)

    now = datetime.now(timezone.utc)
    buckets = []
    for i in range(weeks - 1, -1, -1):
        week_start = now - timedelta(days=(i + 1) * 7)
        week_end = now - timedelta(days=i * 7)
        buckets.append({'label': week_start.strftime('%d %b'), 'start': week_start, 'end': week_end, 'total': Decimal('0')})

    for order in orders:
        created = datetime.fromisoformat(order['created_at'].replace('Z', '+00:00'))
        for bucket in buckets:
            if bucket['start'] <= created < bucket['end']:
                bucket['total'] += Decimal(str(order['total_amount']))
                break

    return [{'label': b['label'], 'total': b['total']} for b in buckets]


def get_spending_dashboard(customer_id, days=30, weeks=8):
    """The /spending/ page needs the summary, top-products, and trend all at
    once, each over a different (overlapping) window — this used to mean 3
    separate round trips to pickker_orders. Fetching once for the largest
    window and filtering in Python cuts that to 1."""
    widest_days = max(days, 90, weeks * 7)
    resp = (
        get_client()
        .table('pickker_orders')
        .select('id, total_amount, created_at, status')
        .eq('customer_id', customer_id)
        .gte('created_at', _since_iso(widest_days))
        .execute()
    )
    orders = resp.data
    return {
        'summary': get_spending_summary(customer_id, days, orders=orders),
        'top_products': get_top_products(customer_id, days=max(days, 90), orders=orders),
        'trend': get_spending_trend(customer_id, weeks=weeks, orders=orders),
    }
