"""Phase 2.4 — router-generated synthetic RU corpus → data/cpt_raw/synth/

Generates raw natural-Russian prose/dialogue (NOT JSON, NOT {response,mood}
persona data — that is Phase 6, a different dataset) to cover registers the open
corpora lack: smart-home / assistant / conversational RU.

Uses an OpenAI-compatible endpoint = the user's free-provider router. Configure:
    export ROUTER_BASE_URL="https://your-router/v1"
    export ROUTER_API_KEY="..."
    export ROUTER_MODELS="model-a,model-b,model-c"   # rotated for diversity

HARD CAP: stops at 10% of TARGET_TOKENS (plan §2.4 / hard rule 2). Synthetic-heavy
CPT collapses. Rotate models so it isn't one model's fingerprint.

Every generation is filtered here (langID + mixed-script) before write; the full
cleaning + cross-corpus dedup still happens in corpus_clean.py.

Usage:  python corpus_synth.py
"""
import os
import json
import random
import itertools

from openai import OpenAI

from corpus_common import (
    RAW, TARGET_TOKENS, SYNTH_CAP,
    has_mixed_script, cyrillic_ratio, lang_prob,
)

CHARS_PER_TOK = 3.5
TOPICS_FILE = os.path.join(os.path.dirname(__file__), "data", "topics.txt")

STYLES = [
    "живой разговорный монолог",
    "диалог двух людей",
    "подробное объяснение",
    "личная история от первого лица",
    "инструкция или совет по бытовой теме",
]
# domains open corpora under-represent — the reason synth exists at all.
EXTRA_TOPICS = [
    "умный дом: свет, розетки, сценарии", "голосовой помощник в квартире",
    "напоминания и списки дел на день", "погода и планы на выходные",
    "готовка ужина и покупки продуктов", "музыка и подкасты дома",
    "будильники, таймеры и распорядок дня", "уборка и бытовые заботы",
]

PROMPT = (
    "Напиши развёрнутый естественный текст на русском языке на тему: «{topic}». "
    "Стиль: {style}. Объём 400–800 слов. Только связный русский текст без разметки, "
    "без английских слов, без списков-маркеров и без JSON."
)


def load_topics():
    topics = list(EXTRA_TOPICS)
    if os.path.exists(TOPICS_FILE):
        with open(TOPICS_FILE, encoding="utf-8") as f:
            topics += [ln.strip() for ln in f if ln.strip()]
    random.shuffle(topics)
    return topics


def accept(text: str) -> bool:
    """Local gate before write (cheap). Full clean+dedup is corpus_clean.py."""
    if len(text) < 400:
        return False
    if has_mixed_script(text):
        return False
    if cyrillic_ratio(text) < 0.85:
        return False
    try:
        lang, p = lang_prob(text)
    except FileNotFoundError:
        lang, p = "ru", 1.0   # langID optional here; corpus_clean.py enforces it
    return lang == "ru" and p >= 0.65


def main():
    base_url = os.environ.get("ROUTER_BASE_URL")
    api_key = os.environ.get("ROUTER_API_KEY", "none")
    models = [m for m in os.environ.get("ROUTER_MODELS", "").split(",") if m]
    if not base_url or not models:
        raise SystemExit("set ROUTER_BASE_URL and ROUTER_MODELS (see module docstring)")

    client = OpenAI(base_url=base_url, api_key=api_key)
    budget_chars = int(TARGET_TOKENS * SYNTH_CAP * CHARS_PER_TOK)  # hard cap
    out_path = RAW / "synth" / "shard-000.jsonl"
    out_path.parent.mkdir(parents=True, exist_ok=True)

    topics = load_topics()
    model_cycle = itertools.cycle(models)
    written = kept = tried = 0

    with open(out_path, "w", encoding="utf-8") as f:
        for topic in itertools.cycle(topics):
            if written >= budget_chars:
                break
            style = random.choice(STYLES)
            model = next(model_cycle)
            tried += 1
            try:
                resp = client.chat.completions.create(
                    model=model,
                    messages=[{"role": "user",
                               "content": PROMPT.format(topic=topic, style=style)}],
                    temperature=0.9, max_tokens=1200,
                )
                text = (resp.choices[0].message.content or "").strip()
            except Exception as e:
                print(f"[synth] gen error on {model}: {e}")
                continue
            if not accept(text):
                continue
            f.write(json.dumps({"text": text, "model": model}, ensure_ascii=False) + "\n")
            written += len(text)
            kept += 1
            if kept % 50 == 0:
                print(f"[synth] kept {kept}/{tried} tried, "
                      f"{written/1e6:.1f}M / {budget_chars/1e6:.1f}M chars cap")

    print(f"[synth] DONE kept {kept}/{tried}, {written/1e6:.1f}M chars -> {out_path}")


if __name__ == "__main__":
    main()
