#!/usr/bin/env python3
"""Сверка B2B-дохода: AlfaCRM (b2b pay_item'ы) ↔ Финтабло «Выручка b2b».

Зачем: после перевода B2B на CRM выручка выросла с ~1,75М (Финтабло) до ~2,37М (CRM).
Скрипт локализует расхождение: помесячный гэп + пооперационный матчинг по сумме,
а для CRM-операций, не нашедшихся в Финтабло-b2b, показывает, под какой статьёй
эта сумма лежит в Финтабло (income) — т.е. куда «уехали» деньги.

CRM-сторона берётся из кэша .sdd/pays_raw.jsonl (быстро). Финтабло тянется живьём.
Запуск: python3 reconcile_b2b.py
"""
import socket; socket.setdefaulttimeout(40)
import os, json, collections
import margins as m

HERE = m.HERE
RAW = os.path.join(HERE, ".sdd", "pays_raw.jsonl")
MONTHS = m.months_to_now()                       # с января по текущий месяц


def crm_b2b_ops():
    """список CRM b2b-операций из кэша: (month, date, item, amount)."""
    tok = m.alfa_login()
    names = m._alfa_pay_items(tok)
    b2b_ids = {i for i, n in names.items() if "b2b" in (n or "").lower()}
    ops = []; seen = set()
    for line in open(RAW):
        x = json.loads(line)
        if x["id"] in seen:
            continue
        seen.add(x["id"])
        if x["pi"] not in b2b_ids:
            continue
        dd = x["d"] or ""
        if len(dd) < 10:
            continue
        ops.append({"month": dd[3:], "date": dd, "item": names[x["pi"]],
                    "amt": round(float(x["v"] or 0), 2)})
    return [o for o in ops if o["month"] in MONTHS]


def ft_income_all():
    """все income-операции Финтабло 2026 (факт): (month, date, value, category, descr)."""
    cats = m.ft_categories()
    return [{"month": t.get('factMonth'), "date": t.get('date'),
             "amt": round(float(t.get('value') or 0), 2),
             "cat": cats.get(t.get('categoryId'), '') or '(без статьи)',
             "descr": (t.get('description') or t.get('comment') or '')[:40]}
            for t in m.ft_transactions(group='income') if not t.get('isPlan')]


def main():
    crm = crm_b2b_ops()
    ft = ft_income_all()
    ftb2b = [t for t in ft if m._norm(t["cat"]) == m._norm("Выручка b2b")]

    # индекс всех income Финтабло по округлённой сумме -> список статей
    ft_by_amt = collections.defaultdict(list)
    for t in ft:
        ft_by_amt[t["amt"]].append(t)

    # ---- помесячный гэп ----
    cm = collections.Counter(); fm = collections.Counter()
    for o in crm: cm[o["month"]] += o["amt"]
    for t in ftb2b: fm[t["month"]] += t["amt"]
    print("=== ПОМЕСЯЧНЫЙ ГЭП (CRM b2b − Финтабло b2b) ===")
    print(f"  {'мес':8}{'CRM':>14}{'Финтабло':>14}{'гэп':>14}")
    tc = tf = 0
    for mo in MONTHS:
        c, f = cm[mo], fm[mo]; tc += c; tf += f
        print(f"  {mo:8}{c:>14,.0f}{f:>14,.0f}{c-f:>+14,.0f}".replace(",", " "))
    print(f"  {'ИТОГО':8}{tc:>14,.0f}{tf:>14,.0f}{tc-tf:>+14,.0f}".replace(",", " "))

    # ---- пооперационный матчинг по сумме (CRM b2b -> Финтабло b2b) ----
    # каждую CRM-операцию гасим ровно одной Финтабло-b2b операцией той же суммы
    pool = collections.Counter(round(t["amt"], 2) for t in ftb2b)
    unmatched = []
    for o in sorted(crm, key=lambda x: (x["month"], -x["amt"])):
        a = o["amt"]
        if pool[a] > 0:
            pool[a] -= 1
        else:
            unmatched.append(o)

    print(f"\n=== CRM b2b-операции, НЕ нашедшиеся в Финтабло «Выручка b2b» ({len(unmatched)} шт) ===")
    print("     где эта сумма всё же лежит в Финтабло (income) — вот куда 'уехало':")
    gap = 0
    for o in unmatched:
        gap += o["amt"]
        cands = [t for t in ft_by_amt.get(o["amt"], []) if m._norm(t["cat"]) != m._norm("Выручка b2b")]
        labels = sorted({t["cat"] + " (" + (t["month"] or "?") + ")" for t in cands})
        where = "; ".join(labels) or "НЕТ такой суммы в Финтабло income → возможно не занесено / другая сумма"
        print(f"  {o['date']}  {o['item']:16} {o['amt']:>11,.0f}".replace(",", " ") + f"  →  {where}")
    print(f"\n  сумма несматчившихся CRM b2b: {gap:,.0f}".replace(",", " "))

    # ---- обратная сторона: Финтабло b2b, которых нет в CRM ----
    poolc = collections.Counter(round(o["amt"], 2) for o in crm)
    rev = []
    for t in sorted(ftb2b, key=lambda x: (x["month"] or "", -x["amt"])):
        if poolc[t["amt"]] > 0:
            poolc[t["amt"]] -= 1
        else:
            rev.append(t)
    if rev:
        print(f"\n=== Финтабло b2b-операции, которых НЕТ в CRM b2b ({len(rev)} шт) ===")
        for t in rev:
            print(f"  {t['date']}  {t['amt']:>11,.0f}  {t['descr']}".replace(",", " "))


if __name__ == "__main__":
    main()
