#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
export_registry_v2.py — реестр (xlsx) → JSON по уездам для atlas.html.

Запуск:  python3 export_registry_v2.py ../Реестр_..._v54.xlsx ../data/

Отличие от export_registry_v1.py: тот выгружал ТОЛЬКО Лихвинский уезд
одним файлом dachi.json. Этот выгружает все уезды, разложив их по файлам,
плюс лёгкий индекс, по которому атлас ищет и фильтрует, не качая всё.

Схема записи и правила цвета/флагов берутся из export_registry_v1.py
импортом, а не копией — чтобы они не разъехались. Если реестр изменит
заголовки столбцов, v1 остановится с внятной ошибкой, и остановит нас.

Выход:
  registry_<код>.json   — полные записи, ключ = ID (А, Б, БП, К, Л, О)
  registry_index.json   — [{id,u,n,nm,k,c}] по всем записям, для поиска
  registry_manifest.json— список уездов с числами и путями
  dachi.json            — как раньше, только Лихвин (для старых страниц)
  gidronimy.json, meta.json — как раньше
"""
import json
import re
import sys
from collections import Counter
from pathlib import Path

import openpyxl

sys.path.insert(0, str(Path(__file__).resolve().parent))
from export_registry_v1 import COLS as COLS1, s, num, flags_and_color, VERSION

# Графы, которые export_registry_v1.py не забирал. Проверка на v55 показала,
# что мимо атласа проходил 21 столбец с данными — в том числе «Листы ПК»
# у 1257 записей и «Датировка источника» у всех 3894. Для краеведа ссылка
# на лист писцовой книги важнее многого: она датирует селение.
COLS_EXTRA = {
    "topo_class":  "Топонимический класс",
    "datirovka":   "Датировка источника",
    "chast_plana": "Часть плана",
    "list_skana":  "Лист скана",
    "listy_pk":    "Листы ПК",
    "tip_sovpad":  "Тип совпадения",
    "polozh_stana":"Положение стана",
    "stan_pk_a":   "Стан по ПК Алексина (справочно)",
    "pochemu":     "Почему выделено",
    "pomety":      "Пометы в исходном файле",
    "sovp_ep_a":   "Совпадение в ЭП Алексина (ПГМ)",
    "kachestvo":   "Качество чтения рукописи",
    "tip_sved":    "Тип сведения",
    "privyazka":   "Привязка к месту",
    "ist_info":    "Источник информации",
    "nadezhnost":  "Надёжность сведения",
    "ep_vlad_n":   "ЭП Лихвина: владельцев",
    "ep_ugodya":   "ЭП Лихвина: угодья (дес.–саж.)",
    "ep_proverka": "ЭП Лихвина: проверка суммы",
    "ep_list":     "ЭП Лихвина: лист скана ЭП",
    "metka":       "Метка строки",
    # добавлено при сверке с «Алфавитом дач Лихвинского уезда»
    "alf_stroeniya": "Алфавит: строения (свод)",
    "alf_vladelcy":  "Алфавит: владельцы (разбор)",
    # возраст селения: год самого раннего документального упоминания
    "ranee_god":     "Раннее упоминание (год)",
    "ranee_ist":     "Раннее упоминание (источник)",
    # вторая писцовая книга: 1650 г. Отдельный источник, не смешивать
    # с книгами 1620–30-х — у него своя дата и свой стан.
    "pk50_status":   "ПК 1650: статус",
    "pk50_zapis":    "ПК 1650: запись дословно",
    "pk50_stan":     "ПК 1650: стан и разряд",
    "pk50_pometa":   "ПК 1650: помета источника",
    # Экономические примечания Белёвского уезда 1792 г. — отдельный разбор,
    # не путать с лихвинскими: у них своя графа и свой источник
    "bel_vlad":      "ЭП Белёва: владельцы",
    "bel_opis":      "ЭП Белёва: описание",
    "bel_dvor":      "ЭП Белёва: дворов",
    "bel_dm":        "ЭП Белёва: душ муж.",
    "bel_dzh":       "ЭП Белёва: душ жен.",
    "bel_zem":       "ЭП Белёва: всего земли",
    # Генеральный алфавит — опись межевого архива: кто и когда межевал,
    # площади удобной и неудобной земли, души по ревизии. Источник
    # самостоятельный, не путать с Экономическими примечаниями.
    "ga_num":        "Ген. алфавит: № очередной",
    "ga_zvanie":     "Ген. алфавит: звание дач и владельцов (дословно)",
    "ga_vlad":       "Ген. алфавит: владельцы",
    "ga_dush":       "Ген. алфавит: душ м.п. по ревизии",
    "ga_udob":       "Ген. алфавит: удобной (дес.–саж.)",
    "ga_neudob":     "Ген. алфавит: неудобной (дес.–саж.)",
    "ga_vsego":      "Ген. алфавит: всего (дес.–саж.)",
    "ga_mezh":       "Ген. алфавит: когда и кем межевана",
    "ga_utv":        "Ген. алфавит: межи утверждены",
    "ga_otm":        "Ген. алфавит: отметка",
    "ga_list":       "Ген. алфавит: лист дела",
    "ga_kach":       "Ген. алфавит: качество чтения",
    "ga_sverka":     "Ген. алфавит: сверка имени с ЭП",
    "ga_delo":       "Ген. алфавит: дело",
}
COLS = dict(COLS1)
COLS.update(COLS_EXTRA)

UEZD_NAME = {
    "А": "Алексинский",
    "Б": "Белёвский",
    "БП": "Белёв (писцовые)",
    "К": "Козельский",
    "Л": "Лихвинский",
    "О": "Одоевский",
    # добавлены в v65: Орловская губерния и записи о Смутном времени
    "БХ": "Болховский",
    "Д": "Дешкинский",
    "СМ": "Смутное время",
}
UEZD_ORDER = ["А", "Б", "БП", "БХ", "Д", "К", "Л", "О", "СМ"]


def build_record(r, rid, color, flags):
    """Ровно та же структура, что кладёт export_registry_v1.py."""
    gidro = [g.strip() for g in re.split(r"[;,]", r["gidro"])] if r["gidro"] else []
    return {
        "id": rid,
        "uezd": r["uezd"],
        "uezd_code": rid.split("-")[0],
        "n_plan": num(r["n_plan"]),
        "name": r["name"],
        "name_var": r["name_var"],
        "kind": r["kind"],
        "stan": r["stan"],
        "lat": None,
        "lon": None,
        "coord_src": None,
        "coord_conf": None,
        "color": color,
        "flags": flags,
        "epohi": {
            "I": r["e1"], "II": r["e2"], "III": r["e3"],
            "IV": r["e4"], "V": r["e5"], "VI": r["e6"],
        },
        "ep": {
            "zvanie": r["ep_zvanie"], "vladelcy": r["ep_vlad"],
            "dvorov": num(r["ep_dvor"]), "dush_m": num(r["ep_m"]),
            "dush_zh": num(r["ep_zh"]), "desyatin": num(r["ep_des"]),
            "stroeniya": r["ep_stroeniya"],
            "stroeniya_zav": r["stroeniya_zav"],
            "primechanie": r["ep_text"], "spornye": r["ep_spor"],
        },
        "gidronimy": [g for g in gidro if g],
        "propavshie": r["propavshie"],
        "pustosh": {"ocenka": r["pust_ocenka"], "obosnovanie": r["pust_obosn"]},
        "istochnik": r["istochnik"],
        "doslovno": r["doslovno"],
        "otozhdestvlenie": r["otozhd"],
        "svyaz": r["svyaz"],
        "sobytie": r["sobytie"],
        "sobytie_data": r["sob_data"],
        "alfavit": r["alfavit"],
        "primechanie": r["primech"],
        # писцовые книги: где именно это записано
        "pk": {
            "listy": r["listy_pk"],
            "stan": r["stan_pk_a"],
            "polozhenie": r["polozh_stana"],
        },
        # где искать на плане межевания
        "plan": {
            "chast": r["chast_plana"],
            "list_skana": r["list_skana"],
            "ep_list": r["ep_list"],
        },
        "datirovka": r["datirovka"],
        "galf": ({"num": r["ga_num"], "zvanie": r["ga_zvanie"], "vlad": r["ga_vlad"],
                  "dush": r["ga_dush"], "udob": r["ga_udob"], "neudob": r["ga_neudob"],
                  "vsego": r["ga_vsego"], "mezh": r["ga_mezh"], "utv": r["ga_utv"],
                  "otm": r["ga_otm"], "list": r["ga_list"], "kach": r["ga_kach"],
                  "sverka": r["ga_sverka"], "delo": r["ga_delo"]}
                 if r["ga_zvanie"] else None),
        "belev": ({"vlad": r["bel_vlad"], "opis": r["bel_opis"],
                   "dvor": r["bel_dvor"], "dm": r["bel_dm"], "dzh": r["bel_dzh"],
                   "zem": r["bel_zem"]}
                  if (r["bel_vlad"] or r["bel_opis"] or r["bel_zem"]) else None),
        "pk1650": ({"status": r["pk50_status"], "zapis": r["pk50_zapis"],
                    "stan": r["pk50_stan"], "pometa": r["pk50_pometa"],
                    "zh": bool(r["pk50_status"] and "жил" in str(r["pk50_status"]).lower()),
                    "vopros": bool(r["pk50_status"]
                                   and "предположительн" in str(r["pk50_status"]).lower())}
                   if r["pk50_status"] else None),
        "alf": {"stroeniya": r["alf_stroeniya"], "vladelcy": r["alf_vladelcy"]},
        "ranee": ({"god": int(float(r["ranee_god"])), "ist": r["ranee_ist"]}
                  if r["ranee_god"] else None),
        "topo_class": r["topo_class"],
        "ugodya": r["ep_ugodya"],
        "ep_proverka": r["ep_proverka"],
        "ep_vlad_n": num(r["ep_vlad_n"]),
        "kachestvo": r["kachestvo"],
        "nadezhnost": r["nadezhnost"],
        "tip_sved": r["tip_sved"],
        "ist_info": r["ist_info"],
        "privyazka": r["privyazka"],
        "pochemu": r["pochemu"],
        "pomety": r["pomety"],
        "tip_sovpad": r["tip_sovpad"],
        "sovp_ep_a": r["sovp_ep_a"],
        "metka": r["metka"],
    }


def main(xlsx_path, out_dir):
    xlsx_path, out_dir = Path(xlsx_path), Path(out_dir)
    out_dir.mkdir(parents=True, exist_ok=True)

    wb = openpyxl.load_workbook(xlsx_path, read_only=True, data_only=True)
    ws = wb["Реестр"]
    it = ws.iter_rows(values_only=True)
    hdr = [s(c) for c in next(it)]

    missing = [h for h in COLS.values() if h not in hdr]
    if missing:
        sys.exit("ОСТАНОВ: в листе «Реестр» нет столбцов: %s\n"
                 "Реестр изменился — поправьте COLS в export_registry_v1.py, "
                 "не правьте данные." % missing)
    idx = {k: hdr.index(v) for k, v in COLS.items()}

    chunks = {c: {} for c in UEZD_ORDER}
    index, colors, unknown = [], Counter(), Counter()

    for row in it:
        if not row or not row[idx["id"]]:
            continue
        rid = str(row[idx["id"]]).strip()
        code = rid.split("-")[0]
        if code not in chunks:
            unknown[code] += 1
            continue
        r = {k: s(row[i]) for k, i in idx.items()}
        color, flags = flags_and_color(r)
        colors[color] += 1
        chunks[code][rid] = build_record(r, rid, color, flags)
        item = {"id": rid, "u": code, "n": num(r["n_plan"]),
                "nm": r["name"], "k": r["kind"], "c": color}
        if r["e1"] or r["listy_pk"]:
            item["pk"] = 1          # есть след в писцовых книгах
        index.append(item)

    if unknown:
        print("ВНИМАНИЕ: ID с неизвестным кодом уезда пропущены: %s" % dict(unknown))

    manifest = []
    for code in UEZD_ORDER:
        p = out_dir / ("registry_%s.json" % code)
        p.write_text(json.dumps(chunks[code], ensure_ascii=False), encoding="utf-8")
        manifest.append({"code": code, "name": UEZD_NAME[code],
                         "count": len(chunks[code]),
                         "file": "/data/registry_%s.json" % code,
                         "size_mb": round(p.stat().st_size / 1048576, 2)})

    (out_dir / "registry_manifest.json").write_text(
        json.dumps(manifest, ensure_ascii=False), encoding="utf-8")
    (out_dir / "registry_index.json").write_text(
        json.dumps(index, ensure_ascii=False), encoding="utf-8")

    # совместимость со старыми страницами: только Лихвин, списком
    (out_dir / "dachi.json").write_text(
        json.dumps(list(chunks["Л"].values()), ensure_ascii=False), encoding="utf-8")

    wg = wb["Гидронимы Лихвина"]
    gidronimy = [{"raw": [c for c in (s(x) for x in row) if c], "geometry": None}
                 for row in wg.iter_rows(values_only=True) if any(row)]
    (out_dir / "gidronimy.json").write_text(
        json.dumps(gidronimy, ensure_ascii=False), encoding="utf-8")

    meta = {
        "export_version": VERSION,
        "source_file": xlsx_path.name,
        "strok_v_reestre": len(index),
        "listov": len(wb.sheetnames),
        "dach_lihvin": len(chunks["Л"]),
        "po_uezdam": {c: len(chunks[c]) for c in UEZD_ORDER},
        "cvet": dict(colors),
        "s_koordinatami": 0,
        "gidronimov_strok": len(gidronimy),
    }
    (out_dir / "meta.json").write_text(
        json.dumps(meta, ensure_ascii=False, indent=1), encoding="utf-8")

    print("строк реестра: %d" % len(index))
    for m in manifest:
        print("  %-3s %-20s %5d  %.2f МБ" % (m["code"], m["name"], m["count"], m["size_mb"]))
    print("цвета: %s" % dict(colors))
    print("гидронимов строк: %d, листов в книге: %d" % (len(gidronimy), len(wb.sheetnames)))


if __name__ == "__main__":
    if len(sys.argv) != 3:
        sys.exit(__doc__)
    main(sys.argv[1], sys.argv[2])
