#!/usr/bin/env python3
"""Устойчивый пересчёт матрицы маржинальности с чекпоинтами.

Долгие процессы в этом окружении иногда убиваются без ошибки. Чтобы каждый
перезапуск доделывал с места гибели:
  - приходы CRM качаются постранично, каждая страница пишется в pays_raw.jsonl,
    прогресс (branch, page) — в pays_prog.json;
  - P&L считается помесячно, matrix_partial.json сохраняется после каждого месяца.
Повторный запуск подхватывает готовое. По завершении пишет full_year_matrix.json.

Запуск: python3 rebuild_matrix.py   (можно повторять до строки OK)
Сброс:  python3 rebuild_matrix.py --fresh
"""
import socket; socket.setdefaulttimeout(30)
import margins as mm
import json, os, sys

S = os.path.join(mm.HERE, ".sdd")
RAW = os.path.join(S, "pays_raw.jsonl")
PROG = os.path.join(S, "pays_prog.json")
PART = os.path.join(S, "matrix_partial.json")
OUT = os.path.join(S, "full_year_matrix.json")

if "--fresh" in sys.argv:
    for f in (RAW, PROG, PART):
        if os.path.exists(f):
            os.remove(f)
    print("caches cleared", flush=True)

months = mm.months_to_now()
tok = mm.alfa_login()
cats = mm.ft_categories()
pay_names = mm._alfa_pay_items(tok)

# ---------- 1) приходы CRM постранично, с чекпоинтом ----------
prog = json.load(open(PROG)) if os.path.exists(PROG) else {"br": {}, "done": []}
rawf = open(RAW, "a")
for br in mm.BRANCHES:
    if br in prog["done"]:
        print(f"branch {br}: готов", flush=True)
        continue
    page = prog["br"].get(str(br), -1) + 1
    date_from, date_to = mm.year_range_alfa()
    while True:
        r = mm.alfa(f"/v2api/{br}/pay/index",
                    {"pay_type_id": 1, "date_from": date_from, "date_to": date_to,
                     "pageSize": 500, "page": page}, tok)
        items = r.get("items", [])
        total = r.get("total")
        for x in items:
            rawf.write(json.dumps({"id": x.get("id"), "d": x.get("document_date"),
                                   "pi": x.get("pay_item_id"), "v": x.get("income")}) + "\n")
        rawf.flush()
        prog["br"][str(br)] = page
        json.dump(prog, open(PROG, "w"))
        print(f"br {br} page {page}: {len(items)} опер.", flush=True)
        if not items or (total is not None and (page + 1) * 500 >= total):
            break
        page += 1
        if page > 200:
            raise RuntimeError(f"br={br}: больше 200 страниц — данные могли обрезаться")
    prog["done"].append(br)
    json.dump(prog, open(PROG, "w"))
rawf.close()

# ---------- собрать доход по направлениям из сырых страниц ----------
res = {m: {d: 0.0 for d in mm.DIRECTIONS} for m in months}
crm_other = {m: {} for m in months}
inc_detail = {m: {d: {} for d in mm.DIRECTIONS} for m in months}   # {направление: {статья CRM: сумма}}
seen = set()
for line in open(RAW):
    x = json.loads(line)
    pid = x["id"]
    if pid in seen:
        continue
    seen.add(pid)
    dd = x["d"] or ""
    if len(dd) < 10:
        continue
    mo = dd[3:]
    if mo not in res:
        continue
    name = pay_names.get(x["pi"])
    val = float(x["v"] or 0)
    d = mm.income_direction(name)     # b2b-фолбэк внутри income_direction
    if d:
        res[mo][d] += val
        k = (name or "").strip()
        inc_detail[mo][d][k] = inc_detail[mo][d].get(k, 0.0) + val
    else:
        k = (name or "").strip() or "Прочий доход CRM"
        crm_other[mo][k] = crm_other[mo].get(k, 0.0) + val
for mo in months:
    res[mo]["__crm_other__"] = crm_other[mo]
    res[mo]["__income_detail__"] = inc_detail[mo]
print("income собран из", len(seen), "операций", flush=True)

# ---------- 2) P&L помесячно, с чекпоинтом ----------
matrix = {}
if os.path.exists(PART):
    try:
        pv = json.load(open(PART))
        if pv.get("months") == months:
            matrix = pv.get("matrix", {})
    except Exception:
        matrix = {}
for x in months:
    if x in matrix:
        print(f"skip {x}", flush=True)
        continue
    matrix[x] = mm.month_pnl(x, res[x], cats, tok)
    json.dump({"months": months, "matrix": matrix}, open(PART, "w"), ensure_ascii=False)
    print(f"done {x}", flush=True)

json.dump({"months": months, "matrix": matrix}, open(OUT, "w"), ensure_ascii=False)
print("OK", len(matrix), "мес", flush=True)
