#!/usr/bin/env python3
"""Публикует посчитанную матрицу (.sdd/full_year_matrix.json) в Google Sheet
в виде управленческого P&L.

Структура листа:
  ДОХОДЫ (операционные), ₽ — направления (CRM) + прочий операц. доход + ИТОГО
  ПРЯМЫЕ РАСХОДЫ, ₽         — прямые расходы по направлениям
  МАРЖА направлений         — по каждому направлению две строки: ₽, затем %
  ОБЩИЕ РАСХОДЫ (overhead)  — сгруппированы (Помещение, ФОТ, Налоги…) + ИТОГО
  ОПЕРАЦИОННАЯ ПРИБЫЛЬ       = доход − прямые − overhead
  ФИНАНСИРОВАНИЕ            — кредиты/займы (+) и проценты (−), отдельным блоком
  ЧИСТЫЙ РЕЗУЛЬТАТ          = операц. прибыль + финансирование

Всё считается из уже готовой матрицы, повторный сбор данных не нужен.
Запуск: python3 push_to_gsheet.py
"""
import json, os
import gspread
import core

HERE = os.path.dirname(os.path.abspath(__file__))
SA_FILE = os.path.join(HERE, "gsheets_sa.json")
SHEET_ID = "1C0z2ONr-QrLozztfTCjkFqBteKLZyxFW5yg9RIhbLvA"
DIRS = ["1-1", "Групповые", "Хор", "B2B", "Внутренние мероприятия", "Съёмки", "Аренда"]

# --- классификация статей ---
# финансовые ПРИТОКИ (получение денег в долг / ввод собственника) — вниз, не в доход
FIN_IN = {"Получение кредита", "Получение займа", "Ввод средств", "ввод"}
# финансовые ОТТОКИ (проценты, тело) — вниз, не в overhead
FIN_OUT = {"% по кредитам", "% по займу", "% и комиссия по овердрафту",
           "Проценты по кредиту", "Выплата тела кредита", "Проценты по займу"}

# группы overhead: группа -> точные имена статей
OVERHEAD_GROUPS = [
    ("Помещение", ["Аренда и содержание здания (ком.услуги)", "Хозяйственные расходы",
                   "Мебель и оборудование", "Ремонт"]),
    ("ФОТ бэк-офиса и подряд", ["Зарплаты бэк-офис", "Зарплаты администраторов",
                                "Аутсорс", 'Комиссия "рабочие руки"']),
    ("Налоги", ["Налог ИП (наличка)", "Налоги", "Взносы в фонды",
                "Налоги на доходы (прибыль)", "Налоги за сотрудников", "НДФЛ"]),
    ("Банк и эквайринг", ["Банковское обслуживание", "Комиссия эквайринг"]),
    ("Сервисы и IT", ["Сервисы"]),
    ("Маркетинг", ["Дизайн", "Расходы на мероприятия (внешние)", "Реклама",
                   "Типография", "Контент и создание контента", "Маркетинг"]),
    ("Бухгалтерия и админ.", ["Бухгалтерское обслуживание"]),
]


def overhead_group(name):
    for g, names in OVERHEAD_GROUPS:
        if name in names:
            return g
    if name.lower().startswith("возврат"):
        return "Возвраты"
    return "Прочее"


# палитра (минимализм в духе Linear)
C_WHITE = {"red": 1, "green": 1, "blue": 1}
C_HEADER_BG = {"red": 0.12, "green": 0.14, "blue": 0.18}
C_SECTION_BG = {"red": 0.87, "green": 0.90, "blue": 0.93}
C_GROUP_BG = {"red": 0.945, "green": 0.955, "blue": 0.965}
C_SUBTOTAL_BG = {"red": 0.93, "green": 0.95, "blue": 0.96}
C_TOTALCOL_BG = {"red": 0.97, "green": 0.97, "blue": 0.98}
C_PCT = {"red": 0.42, "green": 0.45, "blue": 0.50}
C_NEG = {"red": 0.72, "green": 0.11, "blue": 0.11}
C_HAIRLINE = {"red": 0.78, "green": 0.80, "blue": 0.84}


def a1col(n):  # 1-based → буква(ы)
    s = ""
    while n > 0:
        n, r = divmod(n - 1, 26)
        s = chr(65 + r) + s
    return s


def _load():
    d = json.load(open(os.path.join(HERE, ".sdd/full_year_matrix.json")))
    return d["months"], d["matrix"]


def _union_sorted(names_by_month):
    tot = {}
    for dd in names_by_month:
        for name, val in dd.items():
            tot[name] = tot.get(name, 0.0) + val
    return [n for n, _ in sorted(tot.items(), key=lambda x: -x[1])]


def build():
    months, M = _load()
    yy = str(core.FIN_YEAR)[2:]
    ml = [m[:2] + "." + yy for m in months]
    ncol = len(months) + 1            # данные: месяцы + ИТОГО
    total_cols = ncol + 1             # + подпись
    last = a1col(total_cols)

    io = {m: M[m]["income_other"] for m in months}       # прочие доходы постатейно
    oh = {m: M[m]["overhead_detail"] for m in months}    # overhead постатейно
    idet = {m: M[m].get("income_detail", {}) for m in months}  # {направление: {статья CRM: сумма}}

    # имена статей
    io_all = _union_sorted([io[m] for m in months])
    oh_all = _union_sorted([oh[m] for m in months])
    io_op = [n for n in io_all if n not in FIN_IN]       # операционный прочий доход
    fin_in = [n for n in io_all if n in FIN_IN]          # финансовые притоки
    oh_op = [n for n in oh_all if n not in FIN_OUT]      # операц. overhead
    fin_out = [n for n in oh_all if n in FIN_OUT]        # финансовые оттоки (проценты)

    # overhead по группам (в порядке OVERHEAD_GROUPS, затем Возвраты, Прочее)
    grouped = {}
    for n in oh_op:
        grouped.setdefault(overhead_group(n), []).append(n)
    order = [g for g, _ in OVERHEAD_GROUPS] + ["Возвраты", "Прочее"]
    group_order = [g for g in order if g in grouped]

    rows = [["Направление / месяц"] + ml + [f"ИТОГО {core.FIN_YEAR}"]]
    sec, grp, pct, sub, red = [], [], [], [], []   # red = строки, где минус = «плохо» (подсветить)

    def row(label, vals):
        rows.append([label] + vals + [sum(vals)])
        return len(rows)

    def section(t):
        rows.append([t] + [""] * ncol)
        sec.append(len(rows))

    def blank():
        rows.append([""] * total_cols)

    def sum_dir(m, pick):
        return sum(M[m][pick][d] for d in DIRS)

    def stat_vals(src, names, sign=1):
        return [round(sign * sum(src[m].get(n, 0) for n in names)) for m in months]

    # ---------- ДОХОДЫ (операционные) ----------
    section("ДОХОДЫ (операционные), ₽")
    for d in DIRS:
        row(d, [round(M[m]["revenue"][d]) for m in months])
        names = _union_sorted([idet[m].get(d, {}) for m in months])   # статьи CRM направления (в сумме = строка)
        for n in names:
            row("   " + n, [round(idet[m].get(d, {}).get(n, 0)) for m in months])
    for n in io_op:
        row("   " + n, [round(io[m].get(n, 0)) for m in months])
    inc_tot = [round(sum_dir(m, "revenue") + sum(io[m].get(n, 0) for n in io_op)) for m in months]
    sub.append(row("ИТОГО ДОХОД", inc_tot))
    blank()

    # ---------- ПРЯМЫЕ РАСХОДЫ (со знаком минус) ----------
    section("ПРЯМЫЕ РАСХОДЫ, ₽")
    for d in DIRS:
        row(d, [-round(M[m]["direct"][d]) for m in months])
    blank()

    # ---------- МАРЖА (₽ и % чередуются) ----------
    section("МАРЖА направлений  (₽ и % от выручки)")
    for d in DIRS:
        red.append(row(d, [round(M[m]["margin"][d]) for m in months]))
        cells, num, den = [], 0.0, 0.0
        for m in months:
            rev, mar = M[m]["revenue"][d], M[m]["margin"][d]
            num += mar; den += rev
            cells.append(round(mar / rev, 4) if rev else "")
        rows.append([d + "  %"] + cells + [round(num / den, 4) if den else ""])
        pct.append(len(rows))
    blank()

    # ---------- ОБЩИЕ РАСХОДЫ (overhead), сгруппировано, со знаком минус ----------
    section("ОБЩИЕ РАСХОДЫ (overhead), ₽")
    for g in group_order:
        names = grouped[g]
        grp.append(row(g, stat_vals(oh, names, -1)))      # строка-группа с подытогом (минус)
        for n in sorted(names, key=lambda x: -sum(oh[m].get(x, 0) for m in months)):
            row("   " + n, [-round(oh[m].get(n, 0)) for m in months])
    sub.append(row("ИТОГО OVERHEAD", stat_vals(oh, oh_op, -1)))
    blank()

    # ---------- ОПЕРАЦИОННАЯ ПРИБЫЛЬ ----------
    opprofit = [round(sum_dir(m, "margin") + sum(io[m].get(n, 0) for n in io_op)
                      - sum(oh[m].get(n, 0) for n in oh_op)) for m in months]
    red.append(sub_append_red := row("ОПЕРАЦИОННАЯ ПРИБЫЛЬ", opprofit)); sub.append(sub_append_red)
    blank()

    # ---------- ФИНАНСИРОВАНИЕ (кредиты/займы + проценты) ----------
    section("ФИНАНСИРОВАНИЕ (кредиты, займы, %), ₽")
    for n in fin_in:
        row("   + " + n, [round(io[m].get(n, 0)) for m in months])
    for n in fin_out:
        row("   − " + n, [-round(oh[m].get(n, 0)) for m in months])
    fin_net = [round(sum(io[m].get(n, 0) for n in fin_in)
                     - sum(oh[m].get(n, 0) for n in fin_out)) for m in months]
    sub.append(row("ИТОГО ФИНАНСИРОВАНИЕ", fin_net))
    blank()

    # ---------- ЧИСТЫЙ РЕЗУЛЬТАТ ----------
    result = [opprofit[i] + fin_net[i] for i in range(len(months))]
    res_row = row("ЧИСТЫЙ РЕЗУЛЬТАТ  (операц. прибыль + финансирование)", result)
    sub.append(res_row); red.append(res_row)

    meta = {"total_cols": total_cols, "nrow": len(rows), "last": last,
            "nmonths": len(months), "sec": sec, "grp": grp, "pct": pct, "sub": sub, "red": red}
    return rows, meta


def main():
    gc = gspread.service_account(filename=SA_FILE)
    sh = gc.open_by_key(SHEET_ID)
    ws = sh.sheet1
    sid = ws.id

    # ПОЛНЫЙ сброс: ws.clear() чистит только значения, а форматы и условные правила
    # копятся с каждого прогона (отсюда «каша» с цветом). Сбрасываем всё явно.
    md = sh.fetch_sheet_metadata()
    ncond = 0
    for s in md.get("sheets", []):
        if s.get("properties", {}).get("sheetId") == sid:
            ncond = len(s.get("conditionalFormats", []) or [])
            break
    reset = [{"repeatCell": {"range": {"sheetId": sid},
                             "cell": {"userEnteredFormat": {}}, "fields": "userEnteredFormat"}}]
    reset += [{"deleteConditionalFormatRule": {"sheetId": sid, "index": 0}} for _ in range(ncond)]
    sh.batch_update({"requests": reset})

    ws.clear()
    rows, meta = build()
    ws.update(rows, "A1")

    last, nrow = meta["last"], meta["nrow"]
    total_cols, nmonths = meta["total_cols"], meta["nmonths"]

    fmts = []
    fmts.append({"range": f"A1:{last}{nrow}", "format": {"textFormat": {"fontSize": 10}}})
    fmts.append({"range": f"B2:{last}{nrow}",
                 "format": {"numberFormat": {"type": "NUMBER", "pattern": "#,##0"}}})
    fmts.append({"range": f"{last}2:{last}{nrow}",
                 "format": {"backgroundColor": C_TOTALCOL_BG, "textFormat": {"bold": True}}})
    fmts.append({"range": f"A2:A{nrow}", "format": {"textFormat": {"bold": True}}})
    fmts.append({"range": f"A1:{last}1", "format": {
        "backgroundColor": C_HEADER_BG,
        "textFormat": {"bold": True, "fontSize": 10, "foregroundColor": C_WHITE},
        "horizontalAlignment": "CENTER", "verticalAlignment": "MIDDLE"}})
    fmts.append({"range": "A1", "format": {"horizontalAlignment": "LEFT"}})

    for r in meta["sec"]:
        fmts.append({"range": f"A{r}:{last}{r}", "format": {
            "backgroundColor": C_SECTION_BG, "textFormat": {"bold": True, "fontSize": 10}}})
    for r in meta["grp"]:
        fmts.append({"range": f"A{r}:{last}{r}", "format": {
            "backgroundColor": C_GROUP_BG, "textFormat": {"bold": True, "fontSize": 10}}})
    for r in meta["sub"]:
        fmts.append({"range": f"A{r}:{last}{r}", "format": {
            "backgroundColor": C_SUBTOTAL_BG, "textFormat": {"bold": True, "fontSize": 10}}})
    for r in meta["pct"]:
        fmts.append({"range": f"B{r}:{last}{r}",
                     "format": {"numberFormat": {"type": "PERCENT", "pattern": "0%"}}})
        fmts.append({"range": f"A{r}:{last}{r}", "format": {
            "textFormat": {"bold": False, "italic": True, "fontSize": 10, "foregroundColor": C_PCT}}})

    ws.batch_format(fmts)
    ws.freeze(rows=1, cols=1)

    reqs = [
        {"updateDimensionProperties": {
            "range": {"sheetId": sid, "dimension": "COLUMNS", "startIndex": 0, "endIndex": 1},
            "properties": {"pixelSize": 300}, "fields": "pixelSize"}},
        {"updateDimensionProperties": {
            "range": {"sheetId": sid, "dimension": "COLUMNS", "startIndex": 1, "endIndex": nmonths + 1},
            "properties": {"pixelSize": 92}, "fields": "pixelSize"}},
        {"updateDimensionProperties": {
            "range": {"sheetId": sid, "dimension": "COLUMNS",
                      "startIndex": nmonths + 1, "endIndex": total_cols},
            "properties": {"pixelSize": 110}, "fields": "pixelSize"}},
    ]
    # красный-минус ТОЛЬКО там, где минус = «плохо»: маржа направлений (₽) и итоговые
    # строки прибыли. Расходы теперь со знаком минус — это норма, их не красим.
    reqs.append({"addConditionalFormatRule": {"rule": {
        "ranges": [{"sheetId": sid, "startRowIndex": r - 1, "endRowIndex": r,
                    "startColumnIndex": 1, "endColumnIndex": total_cols} for r in sorted(meta["red"])],
        "booleanRule": {
            "condition": {"type": "NUMBER_LESS", "values": [{"userEnteredValue": "0"}]},
            "format": {"textFormat": {"foregroundColor": C_NEG}}}},
        "index": 0}})

    def hline(row_idx0):
        return {"updateBorders": {
            "range": {"sheetId": sid, "startRowIndex": row_idx0, "endRowIndex": row_idx0 + 1,
                      "startColumnIndex": 0, "endColumnIndex": total_cols},
            "top": {"style": "SOLID", "color": C_HAIRLINE}}}
    reqs.append(hline(1))
    for r in meta["sub"]:
        reqs.append(hline(r - 1))

    sh.batch_update({"requests": reqs})
    print("OK →", sh.url)


if __name__ == "__main__":
    main()
