"""Shared spending helpers used by the dashboard and the spreadsheet export."""

import re

NCM_CATEGORIES = [
    ('hortifruti', 'Hortifruti', ['07', '08']),
    ('carnes', 'Carnes e Frios', ['02', '16']),
    ('laticinios', 'Laticínios', ['04']),
    ('padaria', 'Padaria e Massas', ['19']),
    ('mercearia', 'Mercearia', ['09', '10', '11', '12', '15', '20', '21', '25']),
    ('doces', 'Doces e Chocolates', ['17', '18']),
    ('bebidas', 'Bebidas', ['22']),
    ('higiene', 'Higiene e Beleza', ['33', '56', '96']),
    ('limpeza', 'Limpeza', ['28', '34', '38']),
    ('farmaceuticos', 'Farmacêuticos', ['30']),
    ('petshop', 'Pet Shop', ['23']),
    ('casa', 'Casa e Utilidades', ['39', '44', '48', '63', '68', '69', '70', '73', '76', '82', '94', '95']),
    ('vestuario', 'Vestuário', ['42', '61', '62', '64', '65']),
    ('eletronicos', 'Eletrônicos', ['84', '85']),
    ('fumo', 'Fumo', ['24']),
    ('combustiveis', 'Combustíveis', ['27', '220710']),
]


def categorize_ncm(ncm):
    """Categorize an NCM code, preferring the longest prefix match."""
    ncm_padded = str(ncm).zfill(8)
    best_id, best_name, best_len = 'outros', 'Outros', 0
    for cat_id, cat_name, prefixes in NCM_CATEGORIES:
        for p in prefixes:
            if ncm_padded.startswith(p) and len(p) > best_len:
                best_id, best_name, best_len = cat_id, cat_name, len(p)
    return best_id, best_name


def add_months(dt, months):
    """Return the first day of the month `months` away from `dt`."""
    y = dt.year + (dt.month - 1 + months) // 12
    m = (dt.month - 1 + months) % 12 + 1
    return dt.replace(year=y, month=m, day=1, hour=0, minute=0, second=0, microsecond=0)


def _parse_installments_from_payment_form(text: str):
    tokens = []
    if not text:
        return tokens
    for m in re.finditer(r'(\d+)\s*x\s*(\d{1,3}(?:\.\d{3})*,\d{2})', text, flags=re.IGNORECASE):
        try:
            n = int(m.group(1))
        except Exception:
            continue
        val = m.group(2).replace('.', '').replace(',', '.')
        try:
            amt = float(val)
        except Exception:
            continue
        if n >= 1 and amt > 0:
            tokens.append((n, amt))
    return tokens


def order_installment_amount_for_month(order_obj, month_dt):
    """
    Amount of this order that should count in the given month
    (month_dt = first day of month). If not installment, counts the full
    total_paid only in the sale month.
    """
    sale = order_obj.sale_date
    if not sale:
        return 0.0
    sale_month = sale.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
    parcelas = int(getattr(order_obj, 'numero_parcelas', 1) or 1)
    parcelas = max(parcelas, 1)
    if not bool(getattr(order_obj, 'contem_parcelamento', False)) or parcelas == 1:
        return float(order_obj.total_paid or 0) if sale_month == month_dt else 0.0

    parsed = _parse_installments_from_payment_form(getattr(order_obj, 'payment_form', '') or '')
    if parsed:
        month_offset = (month_dt.year - sale_month.year) * 12 + (month_dt.month - sale_month.month)
        if month_offset < 0:
            return 0.0
        total = 0.0
        for n, per_amt in parsed:
            if month_offset < n:
                total += per_amt
        return round(total, 2) if total > 0 else 0.0

    for i in range(parcelas):
        if add_months(sale_month, i) == month_dt:
            return round(float(order_obj.total_paid or 0) / parcelas, 2)
    return 0.0
