#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Біблія Огієнка: пошук вірша за посиланням, як воно написане в буклеті.

    from bible import verse, verses, text_of
    verse('Матвія 1: 21')          -> 'І вона вродить Сина, ти ж даси Йому…'
    verses('Буття 2: 7-9')         -> ['І створив Господь Бог…', …]
    text_of('Буття 2: 7-9')        -> три вірші, склеєні пробілом

Джерело — `data/bible_ohienko.json` (див. `bible_convert.py`). Посилання
пишемо так само, як у самих уроках: «2 Самуїла 7: 16», «Приповідок 21: 1»,
«1 Хронік 29: 9-17». Пробіл після двокрапки, як у наших буклетах,
необов'язковий.

Рішення замовника від 2 вересня 2026: цитати за **Огієнком** (доти був
Турконяк).

    .venv/bin/python ua_build/bible.py 'Матвія 1: 21'     # подивитися вірш
"""
import json
import os
import re
import sys

HERE = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.dirname(HERE)
PATH = os.path.join(ROOT, 'data', 'bible_ohienko.json')

# ── відомі помилки оцифрування ───────────────────────────────────────────
#
# Файл лишається дослівною копією джерела, а виправлення живуть тут — щоб
# кожне мало видиму причину й щоб дамп можна було звірити з тим, звідки він
# узятий. Додавати сюди можна лише те, що ДОВЕДЕНО, а не «здається дивним».
ERRATA = {
    ('MAT', '1', '21'): (
        'йменна', 'ймення',
        'у корпусі «ймення» 337 разів, «йменна» — двічі, і другий раз '
        'усередині складного слова «нечистойменна» (Єз. 22: 5). Та сама '
        'помилка є в bible.com і old.bibleonline.ru — спільне джерело '
        'оцифровки. Редактор під час коректури написав «ймення».'),
}

_DOC = None
_ALIAS = None

REF_RE = re.compile(
    r'^\s*(?P<book>(?:[1-4]\s+)?[^\d]+?)\s*'
    r'(?P<chapter>\d+)\s*[:.]\s*'
    r'(?P<verses>\d+(?:\s*[-–]\s*\d+)?)\s*$')


def _load():
    global _DOC, _ALIAS
    if _DOC is not None:
        return _DOC
    if not os.path.exists(PATH):
        raise SystemExit(
            'немає %s — зроби його: ua_build/bible_convert.py <дамп> %s'
            % (PATH, PATH))
    with open(PATH, encoding='utf-8') as fh:
        _DOC = json.load(fh)
    _ALIAS = {}
    for code, b in _DOC['books'].items():
        for name in [b['name']] + list(b.get('aliases') or []):
            _ALIAS[_norm(name)] = code
        _ALIAS[_norm(code)] = code
    return _DOC


# Латинські двійники кириличних літер. У дампі назви чотирьох Євангелій
# записані з латинською `i`: «Вiд Матвiя», «Вiд Марка», «Вiд Луки»,
# «Вiд Івана». Наші буклети посилаються коротко («Луки 1:37»), тож це доти
# не заважало — але «Від Луки» з правильною українською «і» не знаходилося
# зовсім. Зводимо обидва написання до одного, щоб пошук не залежав від
# того, яку літеру набрали.
_LOOKALIKE = str.maketrans({
    'i': 'і', 'I': 'І', 'a': 'а', 'A': 'А', 'e': 'е', 'E': 'Е',
    'o': 'о', 'O': 'О', 'p': 'р', 'P': 'Р', 'c': 'с', 'C': 'С',
    'x': 'х', 'X': 'Х', 'y': 'у', 'B': 'В', 'H': 'Н', 'K': 'К',
    'M': 'М', 'T': 'Т',
})


def _norm(s):
    """Назва книги без регістру, апострофів, подвійних пробілів і латиниці."""
    s = s.replace('ʼ', '').replace('’', '').replace("'", '')
    s = s.translate(_LOOKALIKE)
    return re.sub(r'\s+', ' ', s).strip().lower()


def book_code(name):
    _load()
    code = _ALIAS.get(_norm(name))
    if not code:
        raise KeyError('невідома книга: %r' % name)
    return code


def parse(ref):
    """'Буття 2: 7-9' -> ('GEN', '2', ['7','8','9'])."""
    m = REF_RE.match(ref)
    if not m:
        raise ValueError('не схоже на посилання: %r' % ref)
    code = book_code(m.group('book'))
    ch = m.group('chapter')
    v = m.group('verses')
    if re.search(r'[-–]', v):
        a, b = (int(x) for x in re.split(r'\s*[-–]\s*', v))
        nums = [str(i) for i in range(a, b + 1)]
    else:
        nums = [v]
    return code, ch, nums


def verses(ref):
    """Перелік віршів за посиланням. Порожній вірш — це помилка, не пропуск."""
    doc = _load()
    code, ch, nums = parse(ref)
    book = doc['books'][code]
    chap = book['chapters'].get(ch)
    if chap is None:
        raise KeyError('немає розділу %s у книзі %s' % (ch, book['name']))
    out = []
    for n in nums:
        t = chap.get(n)
        if t is None:
            # Об'єднані вірші: «17-18» одним текстом.
            joint = next((k for k in chap
                          if '-' in k and n in k.split('-')), None)
            if joint is None:
                raise KeyError('немає вірша %s %s: %s' % (book['name'], ch, n))
            t = chap[joint]
        fix = ERRATA.get((code, ch, n))
        if fix and fix[0] in t:
            t = t.replace(fix[0], fix[1])
        out.append(t)
    return out


def verse(ref):
    v = verses(ref)
    if len(v) != 1:
        raise ValueError('посилання %r охоплює %d віршів' % (ref, len(v)))
    return v[0]


def text_of(ref):
    return ' '.join(verses(ref))


def main():
    if len(sys.argv) < 2:
        raise SystemExit("вкажи посилання, напр.: bible.py 'Матвія 1: 21'")
    for ref in sys.argv[1:]:
        print(ref)
        for t in verses(ref):
            print('  ', t)


if __name__ == '__main__':
    main()
