#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Звірка біблійних цитат у буклеті з текстом Огієнка.

Те саме для тексту, чим `qa.py` є для геометрії: не «здається правильно», а
перевірено. Урок оголошує свої цитати сам —

    QUOTES = [
        ('p0b4', 'Матвія 1: 21'),        # блок -> посилання
        ('p6b15', 'Матвія 2: 2', 'Де народжений Цар Юдейський?'),
    ]

Перша форма звіряє **весь блок**, друга — лише названий шматок усередині
блока (коли вірш цитується в реченні).

    .venv/bin/python ua_build/quotes_check.py Level4_A12

Що прощається при звірці, а що ні. Прощається обрамлення й розбивка, бо
це верстка, а не текст: лапки «», розриви рядків, подвійні пробіли, тире
різного роду, крапка наприкінці. Не прощається жодне слово й жодна літера —
саме тут і ловиться «переписав по пам'яті».
"""
import argparse
import os
import re
import sys

HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)

from bible import text_of                                     # noqa: E402
from edits import apply_overlay, load_overlay                 # noqa: E402
from text_index import spec_text                              # noqa: E402

DASHES = '‐‑‒–—―−'


def norm(s):
    """Звести до порівнюваного вигляду: лишити слова, прибрати оформлення.

    Регістр теж не порівнюється. Причина не в поблажливості: цитата, яка
    в буклеті відкриває напис, законно починається з великої літери, а в
    вірші те саме слово стоїть посеред речення — «...бо почута молитва
    твоя» проти напису «"Почута молитва твоя."». Доти таке місце давало
    «названого шматка немає в блоці», тобто перевірка сварилася на
    верстку, а не на текст. Різниця в написанні слова від цього не
    ховається: зміна літери — це інша літера, а не інший регістр.
    """
    s = s.replace('\n', ' ').lower()
    for d in DASHES:
        s = s.replace(d, '-')
    s = s.replace('ʼ', "'").replace('’', "'").replace('`', "'")
    s = re.sub(r'[«»"“”]', '', s)
    s = re.sub(r'\s+', ' ', s).strip()
    return s.strip(' .,;:!?-')


def check(stem):
    """-> (розбіжності, скільки цитат). Кожна розбіжність несе `who`:

    `'editor'` — блок змінив Редактор (він є в накладному файлі). Рішення
    замовника від 7 вересня 2026: **правку Редактора приймаємо**, а
    розбіжність із Огієнком лишається попередженням — інструктор пояснить
    дітям різницю, і таких місць за досвідом небагато.

    `'source'` — блок ніхто не правив, отже цитату спотворив розробник у
    файлі перекладу. Це помилка, і вона спиняє збірку.
    """
    mod = __import__('tr_%s' % stem)
    edited = set(load_overlay(stem))

    # Розбіжність, яку ухвалив Редактор, — попередження; розбіжність, яку
    # вніс розробник, — помилка. Звідки береться «ухвалив Редактор»:
    #
    #   * блок є в накладному файлі — Редактор виправив його сам у панелі;
    #   * блок названо в `QUOTES_EDITOR` уроку — Редактор попросив зміну, а
    #     зробити її мусив розробник. Так буває з текстом, якого в панелі
    #     немає: вірш «по крапках» не блок верстки, а малюнок, і виправити
    #     його з панелі неможливо. Без цього переліку така правка
    #     зараховувалася б розробникові й спиняла б збірку.
    by_editor = set(getattr(mod, 'QUOTES_EDITOR', ()))

    def who(key):
        return 'editor' if key in edited or key in by_editor else 'source'

    # Звіряти треба те, що НАДРУКОВАНО, а не те, що написав розробник:
    # поверх файла перекладу лягають постійні правки Редактора, і саме
    # через них цитата може змінитися. Доти перевірка їх не бачила — і
    # пропустила «жадної» -> «жодної» в Луки 1:37, тобто рівно той випадок,
    # заради якого існує. Обіцянка «правку застосовуємо, а звірка її
    # спіймає» без цього рядка була порожньою.
    apply_overlay(mod.TR, stem)
    quotes = getattr(mod, 'QUOTES', None)
    if not quotes:
        return [], 0
    bad = []
    for item in quotes:
        key, ref = item[0], item[1]
        piece = item[2] if len(item) > 2 else None
        # Текст буває двох походжень: блок верстки (`TR`) або напис, який
        # урок МАЛЮЄ сам (`DRAWN_TEXT`) — контурний вірш, пунктир, підпис на
        # сітці. Друге доти не звірялося взагалі: `quotes_check` читав самі
        # специфікації, тож найбільший вірш на сторінці лишався єдиним
        # місцем у буклеті, куди не дивилася жодна перевірка.
        drawn = getattr(mod, 'DRAWN_TEXT', {})
        spec = mod.TR.get(key)
        if spec is None and key in drawn:
            got = drawn[key]
        elif spec is None:
            bad.append((key, ref, 'блока немає в TR', '', who(key)))
            continue
        else:
            got = spec_text(spec)
        if got is None:
            bad.append((key, ref, 'у блоці немає тексту', '', who(key)))
            continue
        want = text_of(ref)
        if piece is not None:
            # Цитата всередині речення. Перевіряємо ДВІЧІ: що названий шматок
            # справді стоїть у блоці — і що він дослівно є у вірші. Порівняти
            # шматок із ЦІЛИМ віршем не можна: у реченні цитують частину.
            if norm(piece) not in norm(got):
                bad.append((key, ref, 'названого шматка немає в блоці',
                            norm(piece), who(key)))
            elif norm(piece) not in norm(want):
                bad.append((key, ref, norm(piece),
                            'цього немає у вірші: ' + norm(want), who(key)))
            continue
        # Блок вірша несе ще й рядок посилання («Матвія 1: 21») — це підпис
        # верстки, а не текст Писання. Прибираємо його, якщо він останній.
        lines = [l for l in got.split('\n') if l.strip()]
        if lines and norm(lines[-1]) == norm(ref):
            got = '\n'.join(lines[:-1])
        if norm(got) != norm(want):
            bad.append((key, ref, norm(got), norm(want), who(key)))
    return bad, len(quotes)


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument('stems', nargs='+', help='напр. Level4_A12')
    a = ap.parse_args()
    rc = 0
    for stem in a.stems:
        bad, n = check(stem)
        if not n:
            print('%s: цитат не оголошено (QUOTES порожній)' % stem)
            continue
        if not bad:
            print('%s: %d цитат — усі збігаються з Огієнком' % (stem, n))
            continue
        mine = [b for b in bad if b[4] == 'source']
        theirs = [b for b in bad if b[4] == 'editor']
        # Правка Редактора важливіша за дослівність (рішення від 7 вересня
        # 2026), тож вона не спиняє збірку — лише попереджає. Спиняє тільки
        # розбіжність у блоці, якого ніхто не правив: там її міг внести
        # хіба що розробник, і це справді помилка.
        if mine:
            rc = 1
        print('%s: %d цитат, розбіжностей %d (помилок %d, попереджень %d)'
              % (stem, n, len(bad), len(mine), len(theirs)))
        for key, ref, got, want, src in bad:
            head = ('ПОМИЛКА' if src == 'source'
                    else 'РОЗБІЖНІСТЬ (правка Редактора)')
            print('  %s %s (%s)' % (head, key, ref))
            print('    у буклеті: %s' % got)
            print('    в Огієнка: %s' % want)
    return rc


if __name__ == '__main__':
    sys.exit(main())
