"""Parse a stated page number/range out of a post's title/description (FC-6.2).

Artists often state where an installment sits in a series — "pages 9-12",
"Page 5", "[3/8]". We use that to order chapters and flag missing-page gaps.
This is best-effort: a confident match wins, otherwise we return None and the
caller falls back to capture/post-date order. Keep it conservative — a wrong
page number is worse than no page number — so matches require an explicit
page keyword (page/pg/pp) or a bracketed N/M fraction, never a bare number.

Supported forms (case-insensitive):
  range    "pages 9-12", "pg 9–12", "pp. 9 - 12"        -> (9, 12)
  fraction "page 3 of 8", "pg 3/8", "[3/8]", "(3/8)"     -> (3, 3)
  single   "page 5", "pg 5", "pp 5"                       -> (5, 5)
"""

import re

# Page keyword: page/pages/pg/pgs/pp/pp.  (NOT a bare "p" — too many false hits.)
_KW = r"(?:pages?|pgs?|pp\.?)"
_DASH = r"[-–—]"

_RANGE = re.compile(rf"\b{_KW}\s*(\d{{1,4}})\s*{_DASH}\s*(\d{{1,4}})", re.I)
_OF = re.compile(rf"\b{_KW}\s*(\d{{1,4}})\s*(?:of|/)\s*\d{{1,4}}\b", re.I)
_BRACKET = re.compile(r"[\[(]\s*(\d{1,4})\s*/\s*\d{1,4}\s*[\])]")
_SINGLE = re.compile(rf"\b{_KW}\s*(\d{{1,4}})\b", re.I)


def parse_page_range(text: str | None) -> tuple[int, int] | None:
    """Return (start, end) or None. start <= end; a single page yields (n, n)."""
    if not text:
        return None
    m = _RANGE.search(text)
    if m:
        a, b = int(m.group(1)), int(m.group(2))
        return (a, b) if a <= b else (b, a)
    for rx in (_OF, _BRACKET, _SINGLE):
        m = rx.search(text)
        if m:
            n = int(m.group(1))
            return (n, n)
    return None
