"""Corpusul istoric: proză românească publicată, datată, 1825–1949.

Sursa e Wikisource în română. Fiecare text primește un an — din pagină dacă îl
declară, altfel din perioada activă a autorului — și intră într-un sfert de
secol. Trei precauții:

  * plafon pe autor și pe interval, ca să nu măsurăm idiolectul lui Eminescu în
    locul limbii epocii;
  * eliminarea paragrafelor repetate, fiindcă Wikisource ține și pagina-părinte
    și capitolele ei;
  * traducerile ies afară, fiindcă anul autorului străin nu spune nimic despre
    anul textului românesc.

Ieșire: raw/hist_bins.json
"""
import bz2, hashlib, json, os, re, sys
from collections import Counter, defaultdict

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from lemma import Lemmatizer, tokenize
from ws_index import clean_wikitext, year_hint, EXCLUDE_AUTHORS, EXCLUDE_TITLE_RE

HERE = os.path.dirname(os.path.abspath(__file__))
RAW = os.path.join(HERE, "..", "raw")

BIN = 25
FIRST, LAST = 1825, 1949   # intervalele complete: 1825-1849 … 1925-1949
# Autori cu cetățenie străină, dar care au scris în românește.
ALLOW_FOREIGN = {"Antim Ivireanul", "Constantin Cantacuzino", "Neofit Cavsocalvițiu",
                 "Dimitrie Cantemir", "Petru Maior", "Samuil Micu"}
# Cât poate contribui un autor într-un sfert de secol. Plafonul e compromisul
# dintre volum și diversitate: fără el, 1875-1899 ar fi în bună parte Eminescu.
CAP_PER_AUTHOR = int(os.environ.get("CAP", 2_000_000))


def assign_year(text, authors):
    au = authors.get(text["author"])
    if not au:
        return None
    if not au.get("romanian") and text["author"] not in ALLOW_FOREIGN:
        return None
    b, d = au.get("born"), au.get("died")
    h = text.get("year_hint")
    if h and b and b + 15 <= h <= (d or b + 90) + 5:
        return h
    if not b:
        return None
    fl = b + 35
    if d:
        fl = min(fl, d)
    return max(fl, b + 22)


def main():
    authors = json.load(open(os.path.join(RAW, "ws_authors.json"), encoding="utf8"))
    print("citesc dumpul Wikisource...")
    blob = bz2.open(os.path.join(RAW, "rows.xml.bz2"), "rt", encoding="utf8").read()
    pages = re.findall(r"<page>.*?</page>", blob, re.S)
    del blob

    docs = []
    for p in pages:
        if "<ns>0</ns>" not in p:
            continue
        m = re.search(r"<text[^>]*>(.*?)</text>", p, re.S)
        if not m:
            continue
        body = m.group(1)
        if body[:20].lower().startswith("#redirect"):
            continue
        title = re.search(r"<title>(.*?)</title>", p).group(1)
        am = re.search(r"\|\s*autor\s*=\s*([^\|\}\n]*)", body)
        author = (am.group(1).strip().replace("[[", "").replace("]]", "")
                  .replace("Autor:", "").strip() if am else "")
        if not author or author in EXCLUDE_AUTHORS or EXCLUDE_TITLE_RE.search(title):
            continue
        prose = clean_wikitext(body)
        if len(prose) < 400:
            continue
        y = assign_year({"author": author, "year_hint": year_hint(body, title)}, authors)
        if y is None or not (FIRST <= y <= LAST):
            continue
        docs.append((title, author, y, prose))
    del pages
    print(f"texte datate în interval: {len(docs)}")

    # ordine deterministă: interval, autor, titlu
    docs.sort(key=lambda d: ((d[2] // BIN) * BIN, d[1], d[0]))

    seen_para = set()
    used = defaultdict(int)
    tok_counts = defaultdict(Counter)
    stats = defaultdict(lambda: {"chars": 0, "tokens": 0, "texts": 0,
                                 "authors": set(), "dup_chars": 0,
                                 "by_author": Counter()})
    capped = Counter()

    for title, author, y, prose in docs:
        b = (y // BIN) * BIN
        key = (b, author)
        if used[key] >= CAP_PER_AUTHOR:
            capped[key] += 1
            continue
        kept = []
        dup = 0
        for para in re.split(r"(?<=[.!?])\s+(?=[„”\"A-ZĂÂÎȘȚ])", prose):
            if len(para) < 40:
                kept.append(para)
                continue
            h = hashlib.blake2b(para.encode("utf8"), digest_size=12).digest()
            if h in seen_para:
                dup += len(para)
                continue
            seen_para.add(h)
            kept.append(para)
        body = " ".join(kept)
        if len(body) < 400:
            continue
        toks = tokenize(body)
        tok_counts[b].update(toks)
        s = stats[b]
        s["chars"] += len(body); s["tokens"] += len(toks); s["texts"] += 1
        s["authors"].add(author); s["dup_chars"] += dup
        s.setdefault("by_author", Counter())[author] += len(body)
        used[key] += len(body)

    print("\nlematizez...")
    L = Lemmatizer()
    out = {"bin_years": BIN, "bins": {}, "cap_per_author": CAP_PER_AUTHOR}
    for b in sorted(tok_counts):
        lem, unknown = L.resolve(tok_counts[b])
        s = stats[b]
        top_author, top_chars = s["by_author"].most_common(1)[0]
        out["bins"][str(b)] = {
            "from": b, "to": b + BIN - 1,
            "chars": s["chars"], "tokens": s["tokens"], "texts": s["texts"],
            "authors": len(s["authors"]), "dup_chars": s["dup_chars"],
            "types": len(tok_counts[b]),
            "unmatched_tokens": unknown,
            "top_author": top_author,
            "top_author_share": round(top_chars / max(s["chars"], 1), 4),
            "lemma_tokens": round(sum(lem.values())),
            "lemmas": {k: round(v, 2) for k, v in lem.items() if v >= 1},
        }
        print(f"  {b}-{b+BIN-1}: {s['texts']:>4} texte, {len(s['authors']):>3} autori, "
              f"{s['tokens']:>10,} cuvinte, {len(tok_counts[b]):>7,} forme, "
              f"nepotrivite {unknown/max(s['tokens'],1)*100:.1f}%, "
              f"cel mai mare autor {top_chars/max(s['chars'],1)*100:.0f}% ({top_author})")
    print(f"\nautori plafonați: {len(capped)} (texte sărite: {sum(capped.values())})")
    with open(os.path.join(RAW, "hist_bins.json"), "w", encoding="utf8") as f:
        json.dump(out, f, ensure_ascii=False)
    print("scris raw/hist_bins.json")


if __name__ == "__main__":
    main()
