"""Text generator.

Primary mode: pick a random desire statement from user-managed files in
`~/.statements/`.

Fallback: template-grammar sentence generator (original behavior) when no
statements are found.
"""

import os
import random
from pathlib import Path
import re
from dataclasses import dataclass
from datetime import date
from typing import Optional

from desire_cli.words import get_pools


STATEMENTS_DIR = Path(os.path.expanduser("~/.statements"))
DAILY_DIR = STATEMENTS_DIR / "daily"

_STATEMENT_BAG = None
_STATEMENT_IDX = 0


@dataclass(frozen=True)
class StatementEntry:
    text: str
    source_id: Optional[str] = None
    source_label: Optional[str] = None


def _norm_spaces(s):
    return " ".join(s.split()).strip()


def _extract_statement_payload(line):
    line = re.sub(r"^\s*\d+:\s+", "", line).strip()
    if not line:
        return ""

    low = line.lower()
    if any(x in low for x in ("last=", "typed=", "wpm", "acc")):
        return ""

    # If the line contains an explicit statement marker (common in logs), take
    # everything after the last occurrence.
    d_matches = list(re.finditer(r"\bD:\s*", line))
    if d_matches:
        line = line[d_matches[-1].end():].strip()
    else:
        # Ignore other log-like prefixes such as "I: ...", "X: ...".
        if re.match(r"^[A-Z]:\s+", line):
            return ""

    return _norm_spaces(line)


def _is_noise_statement(s):
    low = s.lower()
    noise_substrings = (
        "desire statement",
        "(pool",
        "pool)",
        "readme",
        "license",
        "https",
        "ssh",
        "github",
        "cli",
        "wpm",
        "acc",
        "typed=",
        "gepel",
        "gépel",
        "ctrl",
        "esc",
        "tab",
        "theme",
        "typing",
    )
    if any(x in low for x in noise_substrings):
        return True

    if re.fullmatch(r"[A-Z]{2,}(\s+[A-Z]{2,})+", s.strip()):
        return True

    return False


def _parse_statements(text):
    """Parse statements from a file.

    - Default: one non-empty line = one statement.
    - Multi-line: end a line with a trailing '\\' to continue on next line.
    - Lines starting with '#' are ignored.
    - Common pasted line-number prefixes like '12: ' are stripped.
    """
    statements = []
    buf = []
    for raw in text.splitlines():
        line = raw.strip()
        if not line:
            continue
        if line.startswith("#"):
            continue

        line = _extract_statement_payload(line)
        if not line:
            continue

        if line.endswith("\\"):
            buf.append(line[:-1].rstrip())
            continue

        if buf:
            buf.append(line)
            s = _norm_spaces(" ".join(buf))
            if s:
                statements.append(s)
            buf = []
        else:
            statements.append(_norm_spaces(line))

    if buf:
        s = _norm_spaces(" ".join(buf))
        if s:
            statements.append(s)
    return statements


def _load_statements(dir_path=STATEMENTS_DIR):
    if not dir_path.exists() or not dir_path.is_dir():
        return []

    entries = []
    for p in sorted(dir_path.iterdir()):
        if not p.is_file():
            continue
        try:
            text = p.read_text(encoding="utf-8")
        except OSError:
            continue
        for stmt in _parse_statements(text):
            if not stmt or not stmt.split() or _is_noise_statement(stmt):
                continue
            entries.append(StatementEntry(text=stmt, source_id=p.name, source_label=p.name))
    return entries


def _daily_file_path(day):
    if not isinstance(day, date):
        raise TypeError("day must be datetime.date")
    return DAILY_DIR / day.strftime("%m_%d.md")


def _daily_source(day):
    file_name = day.strftime("%m_%d.md")
    return f"daily/{file_name}", file_name


def _normalize_daily_text(raw_text):
    return _norm_spaces(raw_text.replace("\r\n", "\n").replace("\r", "\n"))


def load_daily_statement(day, ensure=False):
    path = _daily_file_path(day)
    source_id, source_label = _daily_source(day)
    created = False

    if ensure:
        path.parent.mkdir(parents=True, exist_ok=True)
        if not path.exists():
            path.write_text("", encoding="utf-8")
            created = True

    if not path.exists() or not path.is_file():
        return StatementEntry(text="", source_id=source_id, source_label=source_label), "", created

    try:
        raw_text = path.read_text(encoding="utf-8")
    except OSError:
        raw_text = ""
    text = _normalize_daily_text(raw_text)
    return StatementEntry(text=text, source_id=source_id, source_label=source_label), raw_text, created


def save_daily_statement(day, raw_text):
    path = _daily_file_path(day)
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(raw_text, encoding="utf-8")
    text = _normalize_daily_text(raw_text)
    source_id, source_label = _daily_source(day)
    return StatementEntry(text=text, source_id=source_id, source_label=source_label)


# ── slot filler ──────────────────────────────────────────────────────────────

class _Filler:
    """Mapping that picks a fresh random word for each unique slot.

    Slot names like 'noun1', 'noun2' both resolve to the 'noun' category
    but get independent random picks.
    """
    def __init__(self, pools):
        self._pools = pools
        self._cache = {}

    def __getitem__(self, key):
        if key in self._cache:
            return self._cache[key]
        cat = key.rstrip("0123456789")
        pool = self._pools.get(cat)
        if not pool:
            raise KeyError(f"unknown category: {cat}")
        val = random.choice(pool)
        self._cache[key] = val
        return val


# ── templates ────────────────────────────────────────────────────────────────

EASY_TEMPLATES = [
    # simple subject-verb-object
    "The {noun1} {v_past1} the {noun2}.",
    "{pron1} {v_past1} a {adj1} {noun1}.",
    "The {adj1} {noun1} was very {adj2}.",
    "I {v_past1} the {noun1} {prep1} the {noun2}.",
    "{pron1} {v_past1} {adv1} and {v_past2} away.",
    "The {noun1} {v_past1} {prep1} the {adj1} {noun2}.",
    "A {adj1} {noun1} sat {prep1} the {noun2}.",
    "{pron1} could see the {adj1} {noun1}.",
    "The {adj1} {noun1} {v_past1} {adv1}.",
    "We {v_past1} the {noun1} and the {noun2}.",
    "She {v_past1} the {adj1} {noun1} {prep1} the {noun2}.",
    "He {v_past1} a {noun1} and {v_past2} home.",
    "The {nouns1} were {adj1} and {adj2}.",
    "It was a {adj1} day in the {noun1}.",
    "The {noun1} fell {prep1} the {adj1} {noun2}.",
    "A {noun1} and a {noun2} sat {prep1} the {noun3}.",
    "They {v_past1} the {adj1} {noun1} {time1}.",
    "Every {noun1} had a {adj1} {noun2}.",
    "The {adj1} {nouns1} {v_past1} {prep1} the {noun1}.",
    "She liked the {adj1} {noun1} very much.",
    "It was {adj1} and {adj2} outside.",
    "The {noun1} {v_past1} but the {noun2} did not.",
    "He {v_past1} {adv1} {prep1} the {noun1}.",
    "The old {noun1} stood {prep1} the {noun2}.",
    "{pron1} {v_past1} the door and {v_past2} inside.",
    "We could hear the {adj1} {noun1} from here.",
    "A {adj1} {noun1} {v_past1} down the {noun2}.",
    "The {noun1} was {adj1} but the {noun2} was {adj2}.",
    "{pron1} {v_past1} and then {v_past2}.",
    "She {v_past1} the {noun1} with a {adj1} {noun2}.",
    "He did not {v_base1} the {adj1} {noun1}.",
    "All the {nouns1} were {adj1} that day.",
    "The {noun1} looked {adj1} in the {noun2}.",
    "{pron1} always {v_past1} the {nouns1}.",
    "They {v_past1} the {noun1} and {v_past2} the {noun2}.",
    "The {adj1} {noun1} {v_past1} the {adj2} {noun2}.",
    "No one {v_past1} the {adj1} {noun1}.",
    "The {noun1} did not {v_base1}.",
    "I {v_past1} a {adj1} {noun1} on the {noun2}.",
    "The {noun1} and the {noun2} {v_past1} together.",
    "The {noun1} was {adv1} {adj1}.",
    "{pron1} {v_past1} a {noun1} for the {noun2}.",
    "Did the {adj1} {noun1} {v_base1} the {noun2}?",
    "The {noun1} and the {noun2} were both {adj1}.",
    "What a {adj1} {noun1} it was!",
    "{pron1} {v_past1} every {noun1} in the {noun2}.",
    "There was a {adj1} {noun1} on the {noun2}.",
    "The {noun1} {v_past1} and the {noun2} {v_past2}.",
    "Why did the {noun1} {v_base1} the {adj1} {noun2}?",
    "{pron1} found a {adj1} {noun1} by the {noun2}.",
    "The {adj1} {noun1} could not {v_base1} at all.",
    "Every {noun1} in the {noun2} was {adj1}.",
    "She {v_past1} the {noun1}, but he {v_past2} the {noun2}.",
    "How {adj1} the {noun1} looked that day!",
    "{pron1} {adv1} {v_past1} a {noun1}.",
    "The {nouns1} {v_past1} while the {noun1} {v_past2}.",
    "A {noun1} {v_past1} over the {adj1} {noun2}.",
    "He gave the {noun1} a {adj1} {noun2}.",
    # present tense
    "The {noun1} {v_pres1} the {adj1} {noun2} every day.",
    "A {adj1} {noun1} {v_pres1} {prep1} the {noun2}.",
    "She {v_pres1} the {noun1} and he {v_pres2} the {noun2}.",
    "The {adj1} {noun1} {v_pres1} {adv1}.",
    "Every {noun1} {v_pres1} the {adj1} {noun2}.",
    "He {v_pres1} the {nouns1} {prep1} the {noun1}.",
    "They {v_base1} the {noun1} and {v_base2} the {noun2}.",
    "We {v_base1} {adj1} {nouns1} {prep1} the {noun1}.",
    "The {noun1} does not {v_base1} the {adj1} {noun2}.",
    # passive voice
    "The {noun1} was {v_past1} by the {adj1} {noun2}.",
    "The {adj1} {nouns1} were {v_past1} {prep1} the {noun1}.",
    "A {adj1} {noun1} was {v_past1} {prep1} the {noun2}.",
    # questions and exclamations
    "Where did the {adj1} {noun1} {v_base1}?",
    "How did the {noun1} {v_base1} the {adj1} {noun2}?",
    "What {adj1} {noun1} could {v_base1} so {adv1}?",
    "Is the {noun1} still {v_ing1} {prep1} the {noun2}?",
    "Who {v_past1} the {adj1} {noun1} {prep1} the {noun2}?",
    # gerund-led
    "{v_ing1} the {noun1}, the {noun2} {v_past1} {adv1}.",
    "{v_ing1} {adv1}, the {adj1} {noun1} {v_past1}.",
    # lists, comparisons
    "The {noun1} was more {adj1} than the {noun2}.",
    "The {noun1} {v_past1} {adv1}, like a {adj1} {noun2}.",
    "Both the {noun1} and the {noun2} {v_past1} {adv1}.",
    "The {noun1}, the {noun2}, and the {noun3} were all {adj1}.",
    "Neither the {noun1} nor the {noun2} {v_past1}.",
    # conditionals
    "If the {noun1} {v_past1}, the {noun2} would {v_base1}.",
    # more simple variety
    "The {adj1} {noun1} {v_past1} the {noun2} {time1}.",
    "{pron1} {v_past1} the {adj1} {noun1} and left.",
    "I could see the {nouns1} {prep1} the {adj1} {noun1}.",
    "There were {adj1} {nouns1} all {prep1} the {noun1}.",
    "The {noun1} had a {adj1} {noun2} {prep1} its {noun3}.",
    "It grew {adj1} and the {nouns1} {v_past1}.",
    "We {v_past1} the {adj1} {noun1} with {adj2} {nouns1}.",
    "The {noun1} was too {adj1} to {v_base1}.",
    "She {v_past1} it {prep1} the {adj1} {noun1}.",
    "He {v_past1} the {noun1}, and it was {adj1}.",
    "A {noun1} {v_past1} and a {noun2} {v_past2}.",
    "The {adj1} {noun1} {v_past1} right {prep1} the {noun2}.",
    "{pron1} never {v_past1} the {adj1} {noun1}.",
    "The {nouns1} and the {nouns2} were {adj1}.",
    "Something {v_past1} {prep1} the {adj1} {noun1}.",
    "The {noun1} {v_past1} as the {noun2} {v_past2}.",
    "I {v_past1} the {noun1} before the {noun2} could {v_base1}.",
]

MEDIUM_TEMPLATES = [
    # compound sentences, commas, varied structure
    "{pron1} {v_past1} the {adj1} {noun1}, and the {noun2} {v_past2} {adv1}.",
    "The {adj1} {noun1} {v_past1} {prep1} the {noun2}, but no one {v_past2}.",
    "After the {noun1} {v_past1}, the {adj1} {noun2} began to {v_base1}.",
    "{time1}, {pron1} {v_past1} {prep1} the {adj1} {noun1} and {v_past2}.",
    "The {nouns1} {v_past1} {adv1}, and the {adj1} {noun1} {v_past2} {prep1} the {noun2}.",
    "{pron1} {v_past1} the {noun1} that {v_past2} {prep1} the {adj1} {noun2}.",
    "Before the {noun1} could {v_base1}, {pron1} {v_past1} the {adj1} {noun2}.",
    "The {noun1} {v_past1} {prep1} the {noun2}; it was {adv1} {adj1}.",
    "{pron1} had never {v_past1} such a {adj1} {noun1} before.",
    "With a {adj1} {noun1}, {pron1} {v_past1} {prep1} the {noun2}.",
    "She {v_past1} the {adj1} {noun1}, hoping to {v_base1} the {noun2}.",
    "The {adj1} {noun1} and the {adj2} {noun2} {v_past1} together.",
    "He {v_past1} {adv1}, for the {noun1} was {adj1} and {adj2}.",
    "As the {noun1} {v_past1}, the {nouns1} began to {v_base1}.",
    "They {v_past1} the {adj1} {noun1}, which {v_past2} {prep1} the {noun2}.",
    "{pron1} {v_past1} that the {noun1} would {v_base1} {time1}.",
    "Despite the {adj1} {noun1}, {pron1} {v_past1} to {v_base1}.",
    "Every {noun1} in the {noun2} {v_past1} {adv1} that day.",
    "The {noun1} {v_past1}, and the {adj1} {noun2} {v_past2} {prep1} the {noun3}.",
    "{pron1} {v_past1} {prep1} the {noun1}, {v_ing1} the {adj1} {noun2}.",
    "It seemed that the {adj1} {noun1} had {v_past1} the {noun2} {adv1}.",
    "The {nouns1} could not {v_base1}; they {v_past1} {adv1} instead.",
    "{time1}, the {adj1} {noun1} finally {v_past1}.",
    "While {v_ing1} {prep1} the {noun1}, {pron1} {v_past1} a {adj1} {noun2}.",
    "She {v_past1} the {noun1} and {v_past2} it {prep1} the {adj1} {noun2}.",
    "If the {noun1} had {v_past1}, the {noun2} would have {v_past2}.",
    "Somewhere {prep1} the {noun1}, a {adj1} {noun2} was {v_ing1}.",
    "The {adj1} {noun1} {v_past1} without a {noun2} to {v_base1}.",
    "{pron1} could hear {nouns1} {v_ing1} {prep1} the {adj1} {noun1}.",
    "{pron1} often {v_past1} the {adj1} {nouns1} that {v_past2} {prep1} the {noun1}.",
    "Although the {noun1} was {adj1}, the {noun2} {v_past1} it {adv1}.",
    "A {adj1} {noun1} {v_past1} the {noun2}, and {pron1} {v_past2} {adv1}.",
    "The {noun1} {v_past1} so {adv1} that the {nouns1} {v_past2}.",
    "Nobody could {v_base1} why the {adj1} {noun1} had {v_past1}.",
    "She {v_past1} {prep1} the {noun1} and into the {adj1} {noun2}.",
    "He wanted to {v_base1} the {noun1}, but the {noun2} {v_past1} first.",
    "There was a {adj1} {noun1} {v_ing1} {prep1} the {noun2}.",
    "{pron1} believed the {adj1} {noun1} would never {v_base1}.",
    "It was {adj1}; the {nouns1} {v_past1} and the {noun1} grew {adj2}.",
    "The {noun1} slowly {v_past1}, and the {adj1} {noun2} {v_past2}.",
    "Was it the {adj1} {noun1} that {v_past1}, or had the {noun2} {v_past2} {adv1}?",
    "Neither the {noun1} nor the {adj1} {noun2} could {v_base1} the {noun3} that {v_past1} {prep1} the {noun4}.",
    "{pron1} {v_past1} the {adj1} {noun1}; yet the {noun2} remained {adv1} {adj2}.",
    "How {adv1} the {adj1} {noun1} had {v_past1}, {pron1} could not quite {v_base1}.",
    "Once the {noun1} {v_past1}, the {adj1} {nouns1} began {v_ing1} {prep1} the {noun2}.",
    "The {noun1} that {pron1} {v_past1} was {adv1} more {adj1} than the {noun2}.",
    "Without the {adj1} {noun1}, the {noun2} could not have {v_past1} the {noun3}.",
    "Every {noun1} {prep1} the {adj1} {noun2} seemed to {v_base1} {adv1}.",
    "What had {pron1} {v_past1} {prep1} the {adj1} {noun1}?",
    "As the {noun1} {v_past1} the {noun2}, the {adj1} {noun3} {v_past2} {prep1} the {noun4}.",
    "The {adj1} {noun1} {v_past1}, leaving the {nouns1} to {v_base1} {adv1}.",
    "Far {prep1} the {noun1}, a {adj1} {noun2} was {v_ing1} in the {noun3}.",
    "The {noun1} never {v_past1} why the {adj1} {noun2} {v_past2} so {adv1}.",
    "Until the {noun1} {v_past1}, the {adj1} {nouns1} had been {v_ing1} {prep1} the {noun2}.",
    "So {adj1} was the {noun1} that {pron1} {v_past1} {adv1}.",
    "The {noun1} {v_past1} the {adj1} {noun2}, and soon the {nouns1} {v_past2} as well.",
    "{time1}, a {adj1} {noun1} {v_past1} {prep1} the {noun2} and {v_past2} everything.",
    "Would the {adj1} {noun1} ever {v_base1} again, or had it {v_past1} for the last time?",
    # present tense
    "The {adj1} {noun1} {v_pres1} the {noun2} whenever the {noun3} {v_pres2}.",
    "Every {noun1} {v_pres1} {adv1}, but the {adj1} {noun2} {v_pres2} {adv2}.",
    "{pron1} {v_pres1} the {adj1} {nouns1} that {v_base1} {prep1} the {noun1}.",
    "The {noun1} {v_pres1} {prep1} the {noun2}, {v_ing1} the {adj1} {noun3}.",
    "She {v_pres1} the {noun1} while he {v_pres2} the {adj1} {noun2}.",
    "Whenever the {noun1} {v_pres1}, the {adj1} {nouns1} {v_base1} {adv1}.",
    # passive voice
    "The {adj1} {noun1} was {v_past1} by a {noun2} that had {v_past2} {prep1} the {noun3}.",
    "The {nouns1} were {v_past1} {adv1}, and the {adj1} {noun1} was {v_past2} as well.",
    "The {noun1} had been {v_past1} {prep1} the {adj1} {noun2} for a long time.",
    # questions
    "Why had the {adj1} {noun1} {v_past1} when the {noun2} was still {v_ing1}?",
    "Could the {noun1} {v_base1} the {adj1} {noun2} before the {noun3} {v_past1}?",
    "Had the {adj1} {noun1} truly {v_past1}, or was the {noun2} still {v_ing1}?",
    # gerund-led
    "{v_ing1} the {adj1} {noun1}, {pron1} {v_past1} the {noun2} {adv1}.",
    "{v_ing1} {prep1} the {noun1}, the {adj1} {noun2} {v_past1} {prep2} the {noun3}.",
    "After {v_ing1} the {noun1}, the {adj1} {noun2} {v_past1} {adv1}.",
    # comparisons and lists
    "The {noun1} was more {adj1} than the {noun2}, and the {noun3} was the most {adj2} of all.",
    "The {adj1} {noun1} {v_past1} the {noun2}, the {noun3}, and the {noun4}.",
    "Neither the {adj1} {noun1} nor the {adj2} {noun2} could {v_base1} the {noun3}.",
    # conditionals
    "If the {noun1} {v_past1} the {adj1} {noun2}, then the {nouns1} would {v_base1}.",
    "Unless the {adj1} {noun1} {v_past1}, the {noun2} could not {v_base1}.",
    # more compound variety
    "The {noun1} {v_past1} the {adj1} {noun2}; the {noun3}, however, {v_past2} {adv1}.",
    "She {v_past1} {adv1} and he {v_past2} {adv2}; the {noun1} {v_past3} {prep1} them.",
    "A {adj1} {noun1} and a {adj2} {noun2} {v_past1} {prep1} the {noun3} {time1}.",
    "{pron1} {v_past1} the {noun1} that the {adj1} {noun2} had {v_past2}.",
    "The {adj1} {noun1} {v_past1}, but the {nouns1} {v_past2} {adv1} {prep1} the {noun2}.",
    "They could not {v_base1} the {adj1} {noun1}; it was too {adj2} to {v_base2}.",
    "When the {noun1} {v_past1}, the {adj1} {noun2} began {v_ing1} {adv1}.",
    "The {adj1} {noun1} {v_past1} {prep1} the {noun2} as the {nouns1} {v_past2}.",
    "Across the {adj1} {noun1}, the {nouns1} {v_past1} {adv1} toward the {noun2}.",
    "{time1}, the {nouns1} {v_past1} and the {adj1} {noun1} {v_past2} {prep1} the {noun2}.",
    "The {noun1} {v_past1} the {noun2}, which {v_past2} the {adj1} {noun3} {adv1}.",
    "He {v_past1} every {noun1} he could {v_base1}, yet the {adj1} {noun2} remained.",
    "What the {noun1} {v_past1} was {adj1}; what the {noun2} {v_past2} was {adj2}.",
    "She had {v_past1} the {adj1} {noun1} long before the {noun2} {v_past2}.",
    "The {noun1} {v_past1} {adv1}, and not a single {noun2} {v_past2}.",
    "Between the {adj1} {noun1} and the {noun2}, {pron1} {v_past1} a {adj2} {noun3}.",
    "It was not the {noun1} but the {adj1} {noun2} that {v_past1} {adv1}.",
    "The {adj1} {noun1}, though {adj2}, {v_past1} the {noun2} without {v_ing1}.",
    "Once, the {nouns1} had {v_past1} {adv1}; now, they {v_past2} in {adj1} {noun1}.",
    "The {adj1} {noun1} {v_past1} {prep1} the {noun2}, and the {adj2} {nouns1} {v_past2}.",
    "He {v_past1} the {noun1} while she {v_past2} the {adj1} {noun2} {prep1} the {noun3}.",
    "{pron1} {v_past1} that the {adj1} {noun1} had been {v_ing1} {prep1} the {noun2}.",
]

HARD_TEMPLATES = [
    # complex clauses, semicolons, parenthetical phrases
    "Although the {adj1} {noun1} had {v_past1} {adv1}, the {noun2} remained {adj2}; no one could {v_base1} it.",
    "The {noun1}, which had been {v_ing1} {prep1} the {adj1} {noun2}, suddenly {v_past1} without {v_ing2}.",
    "{pron1} believed that the {adj1} {noun1} would {v_base1}; however, the {noun2} {v_past1} {adv1}.",
    "Having {v_past1} the {adj1} {noun1} {time1}, {pron1} {v_past2} the {adj2} {noun2} with {adj3} {noun3}.",
    "The {adj1} {noun1}, {adv1} {v_ing1} {prep1} the {noun2}, {v_past1} the {adj2} {noun3} that {v_past2} {prep2} the {noun4}.",
    "It was neither the {adj1} {noun1} nor the {adj2} {noun2} that {v_past1}; rather, it was the {noun3} itself.",
    "While the {nouns1} {v_past1} {adv1} {prep1} the {noun1}, the {adj1} {noun2} {v_past2} the {noun3} that {v_past3} {prep2} the {noun4}.",
    "The {noun1} {v_past1} {adv1}, not because the {noun2} was {adj1}, but because the {adj2} {noun3} had {v_past2}.",
    "{pron1}, who had {adv1} {v_past1} the {adj1} {noun1}, could not {v_base1} why the {noun2} {v_past2} so {adv2}.",
    "Beneath the {adj1} {noun1} lay a {adj2} {noun2}, {v_ing1} {adv1} as the {nouns1} {v_past1} {prep1} it.",
    "Neither the {adj1} {noun1} nor the {adj2} {noun2} could {v_base1} what the {noun3} had {v_past1}.",
    "The {adj1} {noun1} {v_past1} {prep1} the {noun2}, {v_ing1} every {noun3} that {v_past2} in its {noun4}.",
    "{time1}, when the {adj1} {noun1} {v_past1} the {noun2}, {pron1} {v_past2} that something had {v_past3}.",
    "Despite {v_ing1} the {adj1} {noun1} {adv1}, {pron1} {v_past1} that the {noun2} would not {v_base1} without {adj2} {noun3}.",
    "The {noun1} had {v_past1} {adv1}; the {adj1} {noun2}, however, {v_past2} {prep1} the {noun3} as though nothing had {v_past3}.",
    "In the {adj1} {noun1}, where {nouns1} once {v_past1} {adv1}, there now stood a {adj2} {noun2} {v_ing1} in the {noun3}.",
    "{pron1} {v_past1} the {adj1} {noun1} and, without {v_ing1}, {v_past2} {prep1} the {adj2} {noun2} that {v_past3} {prep2} the {noun3}.",
    "The {nouns1}, which had been {v_ing1} {prep1} the {noun1} for some time, {adv1} {v_past1} when the {adj1} {noun2} {v_past2}.",
    "What the {noun1} had {v_past1} was not merely a {adj1} {noun2}; it was the {adj2} {noun3} that {v_past2} the entire {noun4}.",
    "Though the {adj1} {noun1} seemed {adj2}, {pron1} {v_past1} {adv1} that the {noun2} was far more {adj3} than it appeared.",
    "By the time the {adj1} {noun1} had {v_past1}, the {nouns1} were already {v_ing1} {prep1} the {adj2} {noun2}.",
    "The {noun1}, once {adj1} and {adj2}, had {v_past1} into something {adv1} {adj3}; {pron1} could barely {v_base1} it.",
    "{pron1} {v_past1} that the {adj1} {noun1}, despite its {adj2} {noun2}, would eventually {v_base1} the {noun3}.",
    "There existed, {prep1} the {adj1} {noun1}, a {noun2} of such {adj2} {noun3} that even the {noun4} {v_past1}.",
    "So {adj1} was the {noun1} that the {nouns1} could only {v_base1} and {v_base2}, unable to {v_base3} any further.",
    "It was {time1} that {pron1} first {v_past1} the {adj1} {noun1}; since then, the {noun2} had never {v_past2} the same.",
    "Between the {adj1} {noun1} and the {adj2} {noun2}, a {adj3} {noun3} {v_past1}, {v_ing1} {adv1} in the {noun4}.",
    "The {adj1} {noun1} {v_past1} {adv1}, as if it had {v_past2} every {noun2} and {noun3} along the {noun4}.",
    "Had the {noun1} not {v_past1} so {adv1}, the {adj1} {noun2} might have {v_past2} the {noun3} entirely.",
    "At the center of the {adj1} {noun1} stood a {adj2} {noun2}, its {noun3} {v_ing1} {adv1} in the {adj3} {noun4}.",
    "Whether the {adj1} {noun1} had truly {v_past1} or merely {v_past2} remained a {noun2} that no one could {v_base1}.",
    "The {noun1} that {pron1} had {v_past1} {time1} was not the same {noun2} that now {v_past2} {prep1} the {adj1} {noun3}.",
    "{pron1}, {v_ing1} {prep1} the {adj1} {noun1}, {v_past1} the {adj2} {noun2} and then {v_past2} {adv1} {prep2} the {noun3}.",
    "Even the {adj1} {nouns1} that {v_past1} {prep1} the {noun1} could not {v_base1} the {adj2} {noun2} that {v_past2} {prep2} the {noun3}.",
    "The {adj1} {noun1} {v_past1}, {v_past2}, and {v_past3}; still, the {noun2} refused to {v_base1}.",
    "As {pron1} {v_past1} {prep1} the {adj1} {noun1}, the {adj2} {nouns1} {v_past2} {adv1}, {v_ing1} the {noun2} in {adj3} {noun3}.",
    "Not since {time1} had the {adj1} {noun1} {v_past1} so {adv1}; the {noun2} {v_past2} in {adj2} {noun3}.",
    "{pron1} {adv1} {v_past1} the {noun1}, knowing that the {adj1} {noun2} {prep1} the {noun3} would never {v_base1} again.",
    "The {adj1} {noun1} and the {adj2} {noun2} {v_past1} {prep1} one another, neither willing to {v_base1} nor to {v_base2}.",
    "Whatever the {noun1} had {v_past1} {prep1} the {adj1} {noun2} was {adv1} {v_past2} by the {adj2} {noun3} that followed.",
    "Could the {adj1} {noun1}, which had {v_past1} so {adv1} {prep1} the {noun2}, truly {v_base1} what {pron1} had {v_past2}?",
    "The {noun1} that {pron1} {v_past1} {time1} had since {v_past2}; the {adj1} {noun2} {prep1} the {noun3} was all that {v_past3}.",
    "What {pron1} could not {v_base1} was why the {adj1} {noun1}, having {v_past1} the {noun2} {adv1}, would then {v_base2} the {adj2} {noun3}.",
    "In every {noun1} there {v_past1} a {adj1} {noun2}; in every {noun3} there {v_past2} a {adj2} {noun4} waiting to {v_base1}.",
    "The {adj1} {nouns1}, {v_ing1} {adv1} {prep1} the {noun1}, had not {v_past1} that the {adj2} {noun2} would {v_base1} so {adv2}.",
    "How the {adj1} {noun1} had {v_past1} remained unclear; what was certain was that the {noun2} {v_past2} the {adj2} {noun3} {adv1}.",
    "{pron1} {v_past1} {prep1} the {adj1} {noun1}, yet the {noun2}, {adj2} and {adj3}, {v_past2} {adv1} beyond the {noun3}.",
    "Once the {adj1} {noun1} {v_past1}, every {noun2} {prep1} the {noun3} {v_past2}; it was as though the {adj2} {noun4} had never {v_past3}.",
    "Far from {v_ing1} the {adj1} {noun1}, the {noun2} had {adv1} {v_past1} it; the {noun3}, {adj2} as it was, {v_past2} {prep1} the {noun4}.",
    "The {adj1} {noun1} {v_past1} not because the {nouns1} {v_past2}, but because {pron1}, who had {v_past3} the {adj2} {noun2}, could no longer {v_base1}.",
    "If the {adj1} {noun1} had {v_past1} {adv1} {prep1} the {noun2}, then the {noun3} would have {v_past2}; instead, it {v_past3} the {adj2} {noun4}.",
    "Never before had the {adj1} {noun1} {v_past1} so {adv1}, and {pron1}, {v_ing1} {prep1} the {adj2} {noun2}, {v_past2} that this was no {adj3} {noun3}.",
    "To {v_base1} the {adj1} {noun1} was one thing; to {v_base2} the {adj2} {noun2} that {v_past1} {prep1} it was something {adv1} different.",
    "The {noun1}, {adj1} though it was, had {v_past1} the {adj2} {noun2}; the {noun3} {v_past2} that nothing {prep1} the {noun4} would ever {v_base1} again.",
    "Such was the {adj1} {noun1} that {v_past1} {prep1} the {noun2} that even {pron1}, who had {adv1} {v_past2}, could not {v_base1} its {adj2} {noun3}.",
    "While the {adj1} {noun1} {v_past1} and the {nouns1} {v_past2} {adv1}, {pron1} {v_past3} the {adj2} {noun2}, {v_ing1} that the {noun3} would soon {v_base1}.",
    # present tense complex
    "The {adj1} {noun1} {v_pres1} the {noun2} {adv1}, yet the {adj2} {noun3} {v_pres2} as though the {noun4} does not {v_base1}.",
    "Every {noun1} that {v_pres1} {prep1} the {adj1} {noun2} also {v_pres2} the {adj2} {noun3}; such is the {noun4} of the {noun5}.",
    "The {noun1} {v_pres1} what the {adj1} {noun2} cannot {v_base1}; the {adj2} {noun3}, however, {v_pres2} neither.",
    # passive voice complex
    "The {adj1} {noun1} was {v_past1} by the {noun2} that had been {v_ing1} {prep1} the {adj2} {noun3} since {time1}.",
    "The {nouns1}, once {v_past1} {adv1} by the {adj1} {noun1}, were now {v_past2} {prep1} the {adj2} {noun2}.",
    "Having been {v_past1} by the {adj1} {noun1}, the {noun2} was {adv1} {v_past2} {prep1} the {adj2} {noun3}.",
    # questions complex
    "Had the {adj1} {noun1} not {v_past1} the {noun2} so {adv1}, would the {adj2} {noun3} have {v_past2} at all?",
    "What could the {adj1} {noun1} have {v_past1} that the {noun2} and the {noun3} had not already {v_past2}?",
    "Why, then, had the {adj1} {noun1} {v_past1} {adv1}, when every {noun2} {prep1} the {noun3} {v_past2} the opposite?",
    # gerund-led complex
    "{v_ing1} {adv1} {prep1} the {adj1} {noun1}, the {noun2} {v_past1} the {adj2} {noun3} that had {v_past2} {prep2} the {noun4}.",
    "After {v_ing1} the {adj1} {noun1} and {v_ing2} the {noun2}, {pron1} {v_past1} {adv1} {prep1} the {adj2} {noun3}.",
    "By {v_ing1} the {noun1} {adv1}, the {adj1} {noun2} managed to {v_base1} the {adj2} {noun3} that {v_past1} {prep1} the {noun4}.",
    # conditionals complex
    "If the {adj1} {noun1} had not {v_past1} the {noun2}, the {adj2} {nouns1} would never have {v_past2} {prep1} the {noun3}.",
    "Were it not for the {adj1} {noun1} that {v_past1} {prep1} the {noun2}, the {adj2} {noun3} might still be {v_ing1} {adv1}.",
    # more variety
    "The {noun1} {v_past1} {adv1}, {v_ing1} each {noun2} and {noun3} with {adj1} {noun4}; the {adj2} {noun5} alone {v_past2}.",
    "Across the {adj1} {noun1}, where the {nouns1} {v_past1} and the {nouns2} {v_past2}, a {adj2} {noun2} {v_past3} {adv1}.",
    "The {adj1} {noun1} that had {v_past1} the {noun2} {time1} now {v_past2} {adv1}; the {adj2} {noun3} could only {v_base1}.",
    "So {adv1} did the {adj1} {noun1} {v_base1} that the {nouns1} {v_past1}, and the {adj2} {noun2} {v_past2} {prep1} the {noun3}.",
    "The {adj1} {noun1} {v_past1} the {noun2}; the {adj2} {noun3} {v_past2} the {noun4}; and the {adj3} {noun5} {v_past3} them all.",
    "What the {adj1} {noun1} {v_past1} {prep1} the {noun2} was {adv1} less {adj2} than what the {noun3} {v_past2} {prep2} the {noun4}.",
    "Somewhere {prep1} the {adj1} {noun1} and the {adj2} {noun2}, a {noun3} had been {v_ing1}, {adv1} {v_ing2} the {adj3} {noun4}.",
    "The {noun1} {v_past1}, the {noun2} {v_past2}, and the {adj1} {noun3} {v_past3}; none of the {nouns1} could {v_base1} what had {v_past4}.",
    "Although the {nouns1} {v_past1} and the {adj1} {noun1} {v_past2}, the {adj2} {noun2} {v_past3} {adv1} as if nothing had {v_past4}.",
    "It was the {adj1} {noun1}, {adv1} {v_ing1} {prep1} the {noun2}, that {v_past1} the {adj2} {noun3}; the {noun4} merely {v_past2}.",
    "With the {adj1} {noun1} {v_ing1} {prep1} the {noun2} and the {adj2} {noun3} {v_ing2} {prep2} the {noun4}, the {adj3} {noun5} finally {v_past1}.",
    "The {nouns1}, {adj1} and {adj2}, {v_past1} the {noun1} that {pron1} had {adv1} {v_past2}; no {noun2} could {v_base1} the {adj3} {noun3} now.",
    "Long before the {adj1} {noun1} {v_past1} the {noun2}, the {adj2} {noun3} had already {v_past2}; {pron1} {v_past3} too late.",
    "The {adj1} {noun1} {v_past1} {adv1}, as the {nouns1}, {v_ing1} {prep1} the {adj2} {noun2}, refused to {v_base1} the {noun3}.",
    "It was not merely the {adj1} {noun1} that had {v_past1}; it was the {adj2} {noun2} and the {adj3} {noun3} that {v_past2} {adv1} as well.",
    "Through the {adj1} {noun1}, past the {adj2} {noun2}, and beyond the {adj3} {noun3}, the {noun4} {v_past1} {adv1}, {v_ing1} everything in its {noun5}.",
    "Whether the {adj1} {noun1} {v_past1} or the {adj2} {noun2} {v_past2} made little difference; the {noun3} had already {v_past3} {adv1}.",
    "The {noun1} {v_past1}, and with it {v_past2} the {adj1} {noun2} that every {noun3} {prep1} the {adj2} {noun4} had once {v_past3}.",
    "No sooner had the {adj1} {noun1} {v_past1} than the {noun2}, {adj2} and {adj3}, {v_past2} {adv1} {prep1} the {noun3}.",
    "The {noun1} that {v_past1} {prep1} the {adj1} {noun2} was the very same {noun3} that had {v_past2} the {adj2} {noun4} {time1}.",
    "Whichever {noun1} {v_past1} the {adj1} {noun2} first would also {v_base1} the {adj2} {noun3}; this the {nouns1} {v_past2} {adv1}.",
    "Not only did the {adj1} {noun1} {v_base1} the {noun2}, but it also {v_past1} the {adj2} {noun3} that {v_past2} {prep1} the {noun4}.",
    "The {adj1} {noun1} and the {adj2} {noun2} had {v_past1} the same {noun3}; yet only the {noun4} {v_past2} when the {adj3} {noun5} {v_past3}.",
    "As the {adj1} {noun1} {v_past1} {adv1} and the {noun2} {v_past2}, the {nouns1} realized that no {noun3} could {v_base1} the {adj2} {noun4}.",
    "{pron1} had {adv1} {v_past1} the {adj1} {noun1} before the {noun2} {v_past2}; now the {adj2} {noun3} {v_past3} {prep1} the {noun4} alone.",
    "From the {adj1} {noun1} to the {adj2} {noun2}, every {noun3} had {v_past1}; even the {adj3} {nouns1} {v_past2} {adv1}.",
    "The {adj1} {noun1} {v_past1} where the {noun2} had once {v_past2}, and in its {noun3} {v_past3} a {adj2} {noun4} that would {v_base1} the {noun5}.",
    "Scarcely had the {noun1} {v_past1} when the {adj1} {noun2}, {v_ing1} {adv1}, {v_past2} the {adj2} {noun3} {prep1} the {noun4}.",
    "Whereas the {adj1} {noun1} had {v_past1} the {noun2} {adv1}, the {adj2} {noun3} {v_past2} {prep1} it with {adj3} {noun4}.",
    "The {adj1} {noun1}, having {v_past1} both the {noun2} and the {noun3}, {v_past2} {adv1} {prep1} the {adj2} {noun4} that {v_past3} them.",
]

TEMPLATES = {
    "easy": EASY_TEMPLATES,
    "medium": MEDIUM_TEMPLATES,
    "hard": HARD_TEMPLATES,
}


# ── shuffle-bag generator ───────────────────────────────────────────────────

_bags = {}


def _generate_from_templates(count=80, difficulty="medium"):
    """Generate natural sentences totaling approximately `count` words.

    Uses a shuffle-bag so every template is used once before any repeats.
    """
    pools = get_pools(difficulty)
    templates = TEMPLATES.get(difficulty, MEDIUM_TEMPLATES)
    sentences = []
    total_words = 0

    # get or create the shuffle bag for this difficulty
    bag, idx = _bags.get(difficulty, ([], 0))
    if not bag or idx >= len(bag):
        bag = list(templates)
        random.shuffle(bag)
        idx = 0

    while total_words < count:
        if idx >= len(bag):
            bag = list(templates)
            random.shuffle(bag)
            idx = 0
        tmpl = bag[idx]
        idx += 1
        filler = _Filler(pools)
        sentence = tmpl.format_map(filler)
        sentence = sentence[0].upper() + sentence[1:]
        sentences.append(sentence)
        total_words += len(sentence.split())

    _bags[difficulty] = (bag, idx)
    return " ".join(sentences)


def generate(count=80, difficulty="medium"):
    """Generate the target text.

    If `~/.statements/` contains any desire statements, pick one at random.
    Otherwise, fall back to the template generator.
    """
    global _STATEMENT_BAG
    global _STATEMENT_IDX

    def _fallback_entry():
        return StatementEntry(
            text=_generate_from_templates(count, difficulty),
            source_id="__generated__",
            source_label="templates",
        )

    statements = _load_statements()
    if not statements:
        return _fallback_entry()

    if (
        _STATEMENT_BAG is None
        or set(_STATEMENT_BAG) != set(statements)
        or _STATEMENT_IDX >= len(_STATEMENT_BAG)
    ):
        _STATEMENT_BAG = list(statements)
        random.shuffle(_STATEMENT_BAG)
        _STATEMENT_IDX = 0

    if _STATEMENT_IDX >= len(_STATEMENT_BAG):
        random.shuffle(_STATEMENT_BAG)
        _STATEMENT_IDX = 0

    entry = _STATEMENT_BAG[_STATEMENT_IDX]
    _STATEMENT_IDX += 1
    if not entry or not entry.text:
        return _fallback_entry()
    return entry
