#!/usr/bin/env python3
"""form_edit.py — безопасные программные правки Form.elem.json обработки 1С.

Формат v8unpack — валидный JSON; v8unpack принимает его переформатированным
Python'ом без потерь. Поэтому правим через json.load/json.dump, НЕ текстом.
Все команды пишут файл на месте (с indent=2, ensure_ascii=False).

Команды:
  add-attribute  --form F --name N --id ID --template-form REF --template-attr A
      Добавить реквизит формы, скопировав ТИП (ОписаниеТипов с GUID'ами) из
      одноимённого/указанного реквизита рабочего образца. Тип должен покрывать
      присваиваемое значение (напр. тип колонки-ссылки таблицы).

  set-table-readonly  --form F --table T --value {0|1}
      Выставить «Только просмотр» таблицы. 1=только просмотр, 0=редактируема.
      Индекс флага по умолчанию 36 (семейство «список счетов»); если структура
      иная — сначала найди индекс через form_inspect.py --readonly и передай --index.

  set-flag  --form F --table T --index I --value V
      Точечно записать raw[I] таблицы (когда индекс известен). Только скаляры.
"""
import json, sys, argparse, copy, re

GUID = re.compile(r'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')

def load(p): return json.load(open(p, encoding="utf-8"))
def save(p, d): json.dump(d, open(p, "w", encoding="utf-8"), ensure_ascii=False, indent=2)

def all_prop_ids(d):
    return {p["id"] for p in (d.get("props") or [])}

def cmd_add_attribute(a):
    d = load(a.form)
    ref = load(a.template_form)
    src = next((p for p in (ref.get("props") or []) if p["name"] == a.template_attr), None)
    if not src:
        sys.exit(f"в образце {a.template_form} нет реквизита {a.template_attr!r}")
    if a.id in all_prop_ids(d):
        sys.exit(f"id {a.id} уже занят в props — выбери свободный (или тот, на который ссылается кнопка)")
    if any(p["name"] == a.name for p in (d.get("props") or [])):
        sys.exit(f"реквизит {a.name!r} уже есть")
    new = copy.deepcopy(src)
    new["name"] = a.name
    new["id"] = a.id
    # raw реквизита: ["9", ["<внутр.id>"], "0", "\"Имя\"", ...тип...]
    raw = new["raw"]
    if not (isinstance(raw, list) and raw and raw[0] == "9"):
        sys.exit("неожиданная структура raw реквизита (ожидал head '9')")
    raw[1] = [a.id]
    # подменить имя-литерал (индекс 3) если присутствует
    for i, el in enumerate(raw[:6]):
        if isinstance(el, str) and el.startswith('"') and el.strip('"') == a.template_attr:
            raw[i] = f'"{a.name}"'
    d.setdefault("props", []).append(new)
    save(a.form, d)
    guids = sorted(set(GUID.findall(json.dumps(new["raw"], ensure_ascii=False))))
    print(f"✅ добавлен реквизит {a.name!r} id={a.id}; тип (GUID документов): {guids}")
    print("   проверь, что эти GUID соответствуют нужным типам в НоваяКонфа/Documents/*.xml")

def get_table(d, table):
    v = (d.get("data") or {}).get(table)
    if not v: sys.exit(f"таблица {table!r} не найдена в data")
    raw = v.get("raw")
    if not (isinstance(raw, list) and raw and raw[0] == "55"):
        sys.exit(f"{table!r} не похожа на таблицу (head не '55')")
    return raw

def cmd_set_table_readonly(a):
    d = load(a.form)
    raw = get_table(d, a.table)
    idx = a.index
    old = raw[idx]
    if not isinstance(old, str):
        sys.exit(f"raw[{idx}] не скаляр ({old!r}); найди верный индекс через form_inspect.py --readonly")
    raw[idx] = str(a.value)
    save(a.form, d)
    state = "только просмотр" if a.value == 1 else "редактируема"
    print(f"✅ таблица {a.table!r}: raw[{idx}] {old}→{a.value}  ({state})")
    if old == str(a.value):
        print("   (значение не изменилось — возможно индекс не тот; сверь через --readonly)")

def cmd_set_flag(a):
    d = load(a.form)
    raw = get_table(d, a.table)
    old = raw[a.index]
    if not isinstance(old, str):
        sys.exit(f"raw[{a.index}] не скаляр ({old!r})")
    raw[a.index] = str(a.value)
    save(a.form, d)
    print(f"✅ {a.table!r}: raw[{a.index}] {old}→{a.value}")

def cmd_set_fields_readonly(a):
    """Поле-уровневый «Только просмотр» = индекс [14] в raw поля-колонки '37'
    (1=только просмотр, 0=редактируемо). Часто именно ОН держит таблицу
    в просмотре (не табличный флаг). Ставит [14] всем колонкам таблицы
    (ключи data, где путь содержит '/<table>/' и raw[0]=='37'); --column для одной."""
    d = load(a.form)
    hits = []
    for k, v in (d.get("data") or {}).items():
        if f"/{a.table}/" not in k:
            continue
        raw = v.get("raw")
        if not (isinstance(raw, list) and raw and raw[0] == "37" and len(raw) > 14):
            continue
        col = k.split("/")[-1]
        if a.column and col != a.column:
            continue
        if raw[14] != str(a.value):
            raw[14] = str(a.value); hits.append(col)
    save(a.form, d)
    state = "только просмотр" if a.value == 1 else "редактируемы"
    print(f"✅ таблица {a.table!r}: {len(hits)} колонок → [14]={a.value} ({state})")
    for c in hits: print("   ", c)
    if not hits: print("   (нечего менять — возможно уже в нужном состоянии, или имя таблицы/колонки не совпало)")

def main():
    ap = argparse.ArgumentParser()
    sub = ap.add_subparsers(dest="cmd", required=True)

    p = sub.add_parser("add-attribute"); p.set_defaults(fn=cmd_add_attribute)
    p.add_argument("--form", required=True); p.add_argument("--name", required=True)
    p.add_argument("--id", required=True)
    p.add_argument("--template-form", required=True); p.add_argument("--template-attr", required=True)

    p = sub.add_parser("set-table-readonly"); p.set_defaults(fn=cmd_set_table_readonly)
    p.add_argument("--form", required=True); p.add_argument("--table", required=True)
    p.add_argument("--value", type=int, choices=[0, 1], required=True)
    p.add_argument("--index", type=int, default=36)

    p = sub.add_parser("set-flag"); p.set_defaults(fn=cmd_set_flag)
    p.add_argument("--form", required=True); p.add_argument("--table", required=True)
    p.add_argument("--index", type=int, required=True); p.add_argument("--value", required=True)

    p = sub.add_parser("set-fields-readonly"); p.set_defaults(fn=cmd_set_fields_readonly)
    p.add_argument("--form", required=True); p.add_argument("--table", required=True)
    p.add_argument("--value", type=int, choices=[0, 1], required=True)
    p.add_argument("--column", help="одну колонку (по умолчанию — все колонки таблицы)")

    a = ap.parse_args()
    a.fn(a)

if __name__ == "__main__":
    main()
