"""Язык интерфейса: каталог переводов и выбор языка один раз при первом запуске.

Три правила, на которых всё держится.

1. **По умолчанию русский.** Настройки нет — вывод русский, ничего не спрашиваем.
2. **Переводится интерфейс, не данные.** В каталоге живут строки, которые печатает
   сам инструмент. Заголовок прогона, бриф, вывод чужой команды — слова владельца;
   перевести их значит соврать. Нет строки в каталоге — отдаём как есть.
3. **Вопрос о языке — только человеку за терминалом.** Неинтерактивным входом
   живут панель (`/api/cli`), хуки и чужие агенты: вопрос повесит их насмерть.
   Решение принимается через `shell.is_interactive` до всякой печати.

Выбор хранится в `~/.config/chekan/config.json`, ключ `lang`, — рядом с прочими
настройками инструмента. Файл дописывается, а не переписывается: соседние ключи
(например `fixed_port` панели) обязаны пережить запись языка.
"""

from __future__ import annotations

import json
import os
from pathlib import Path

RUSSIAN = "ru"
ENGLISH = "en"
LANGUAGES = (RUSSIAN, ENGLISH)
DEFAULT = RUSSIAN

LANG_ENV = "CHEKAN_LANG"
CONFIG_ENV = "CHEKAN_CONFIG"
CONFIG_KEY = "lang"

# Каталог: русская строка → английская. Ключ — ровно та строка, которую печатает
# инструмент, знак в знак. Разбит по местам, чтобы пропажу было видно глазом.
CATALOG: dict[str, str] = {
    # --- разговорный режим ---
    "Команды — полностью или короткой формой. Выход: «выход».":
        'Commands — in full or short form. To leave: "exit".',
    "Кавычки не закрыты — команда не разобрана.":
        "Unclosed quotes — the command was not parsed.",
    "Нет такой команды: {cmd}": "No such command: {cmd}",
    "Может быть, «{near}»? Весь список: «?»": 'Did you mean "{near}"? Full list: "?"',
    "Весь список команд: «?»": 'Full list of commands: "?"',
    "прервано": "interrupted",
    # --- выбор языка ---
    "Язык интерфейса выбран: {lang}. Поменять — {path}.":
        "Interface language set to {lang}. To change it, edit {path}.",
    # --- пояснения команд (истина — разбор аргументов CLI) ---
    "завести прогон": "start a run",
    "где мы и что делать дальше": "where we are and what to do next",
    "остановить прогон: новое в работу не берётся":
        "hold the run: nothing new is taken into work",
    "продолжить прогон после паузы": "resume the run after a hold",
    "спросить владельца и ждать ответа: прогон стоит":
        "ask the owner and wait: the run stands still",
    "ответить на вопрос прогона": "answer a question of the run",
    "перейти к следующей фазе": "move on to the next phase",
    "краткая сводка": "short summary",
    "требования": "requirements",
    "таски": "tasks",
    "прогнать команду и записать доказательство":
        "run a command and record the proof",
    "проверить гейт": "check a gate",
    "приёмка профильным проверяльщиком": "acceptance by a matching checker",
    "почему прогон стоит: что не сходится, чем чинится, кто чинит":
        "why the run is stuck: what fails, how to fix it, who fixes it",
    "итоговый отчёт": "final report",
    "что установлено на этой машине": "what is installed on this machine",
    "обзор прогона в терминале: прогресс, задачи, проверки":
        "run overview in the terminal: progress, tasks, checks",
    "разложить скилл и указатель по другим агентам":
        "lay the skill and the pointer out to other agents",
    "передать прогон другому агенту: полный ввод в курс с нуля":
        "hand the run to another agent: full briefing from scratch",
    "пересобрать manifest.md из состояния": "rebuild manifest.md from the state",
    "панель прогона в браузере": "run dashboard in the browser",
    "синхронизировать канон и скиллы между машинами":
        "sync the canon and the skills between machines",
    "генератор изолированных промптов для слепых субагентов":
        "generator of isolated prompts for blind subagents",
    "собрать бриф задачи одним файлом": "collect the task brief into a single file",
    "скиллы машины: чем можно делать задачу":
        "skills of this machine: what the task can be done with",

    # --- логотип и общие фразы ---
    "    К О Д О Ц Е Х   ·   Чекан": "    K O D O C E H   ·   Chekan",
    "Прогон:": "Run:",
    "Фаза:": "Phase:",
    "Режим:": "Mode:",
    "Масштаб:": "Scale:",
    "Требования:": "Requirements:",
    "Блокирует:": "Blocking:",
    " ⟵ доказано": " ⟵ proven",
    " ⚠ БЕЗ ДОКАЗАТЕЛЬСТВА": " ⚠ WITHOUT PROOF",
    "Фаз больше нет. Закрывайте прогон: `chekan report`":
        "No more phases. Close the run: `chekan report`",
    "Прогон продолжается. Время простоя в длительности задач не попадёт.":
        "Run continues. Idle time is not counted towards task durations.",
    "Прогон и так не на паузе.": "Run is not paused.",

    # --- фазы и масштабы ---
    "Подготовка": "Preflight",
    "Вход": "Intake",
    "Спецификация": "Specification",
    "План": "Plan",
    "Сборка": "Build",
    "Проверка": "Verification",
    "Приёмка": "Acceptance",
    "малая": "small",
    "средняя": "medium",
    "крупная": "large",

    # --- гейты и why ---
    "Требования разобраны": "Requirements parsed",
    "Спека покрывает бриф": "Spec covers brief",
    "План прослеживается в обе стороны": "Plan traces both ways",
    "Приёмка доказана прогоном": "Acceptance proven by run",
    "НЕ ПРОЙДЕН": "FAILED",
    "пройден": "passed",
    "не проверялся": "not checked",
    "неприменим": "inapplicable",
    "при масштабе": "at scale",
    "оркестратор": "orchestrator",
    "исполнитель": "executor",
    "Стоим на претензиях гейтов:": "Standing on gate claims:",
    "Красных гейтов нет: стоять не на чем.": "No red gates: nothing to stand on.",
    "Пройдены:": "Passed:",
    "Претензия гейта — список работы, а не остановка. Закрыли — `chekan gate`.":
        "Gate claim is a work list, not a halt. Resolved — `chekan gate`.",
    "чинит:": "fixes:",
    "команда:": "command:",
    "Следующий шаг:": "Next step:",
    "манифест пуст: бриф не атомизирован в требования":
        "manifest is empty: brief is not atomized into requirements",
    "нет вердикта слепого приёмщика. Запустите субагента, дав ему ТОЛЬКО brief.md и репозиторий (спеку и таски не давать)":
        "no blind acceptor verdict. Run a subagent giving it ONLY brief.md and repository (do not give spec and tasks)",
    "приёмка не проводилась: нет ни одной записи с kind=accept в журнале":
        "acceptance was not performed: no records with kind=accept in journal",
    "живой сценарий не подтверждён: скилл-проверяльщик не вызывался":
        "live scenario unconfirmed: checker skill was not called",

    # --- таски, прогоны и доказательства ---
    "зелено": "green",
    "расход не измерен": "spend not measured",
    "модель не названа": "model not named",
    "Прогнать по-настоящему: тот же вызов с --no-reuse":
        "Run for real: same call with --no-reuse",
    "Рычаг порчи не гонялся: сама проверка красная — ломать нечего.":
        "Corruption lever was not run: the check itself is red — nothing to break.",

    # --- доска (board) ---
    "ЗАДАЧИ": "TASKS",
    "ФАЗЫ": "PHASES",
    "ГЕЙТЫ": "GATES",
    "ЧТО СДЕЛАНО": "WHAT IS DONE",
    "ТРЕБОВАНИЯ": "REQUIREMENTS",
    "В ОТРЫВЕ": "DETACHED",
    "ПОСЛЕДНИЕ ПРОВЕРКИ": "LATEST CHECKS",
    "Не пускает дальше:": "Blocking progression:",
    "доказана": "proven",
    "идёт": "in-progress",
    "ждёт": "pending",
    "на ревью": "review",
    "ремонт": "repair",
    "закрыта": "done",
    "провалена": "failed",
    "открыто": "open",
    "в спеке": "in-spec",
    "в задаче": "in-task",
    "закрыто": "done",
    "нужны данные": "needs data",
    "отложено": "deferred",
    "снято": "dropped",
    " (chekan gate — подробно)": " (chekan gate — details)",

    # --- отчёт (report) ---
    "## Расход": "## Spend",
    "## Сделано": "## Done",
    "## Что не вошло": "## What is not included",
    "## Доказательства": "## Evidence",
    "## Гейты": "## Gates",
    "## Нужны ваши данные": "## Your data needed",
    "Задач без расхода нет — сумма полная.": "No tasks without spend — total is complete.",
    "Закрытых задач нет — считать нечего.": "No closed tasks — nothing to calculate.",
}

# Вопрос задаётся, когда языка ещё нет, — значит на обоих языках сразу.
QUESTION = "Язык интерфейса / Interface language: 1) Русский  2) English  [1]: "
ANSWERS: dict[str, str] = {
    "1": RUSSIAN, "ru": RUSSIAN, "рус": RUSSIAN, "русский": RUSSIAN, "": RUSSIAN,
    "2": ENGLISH, "en": ENGLISH, "eng": ENGLISH, "english": ENGLISH, "англ": ENGLISH,
}

NAMES: dict[str, str] = {RUSSIAN: "русский", ENGLISH: "English"}

_cache: str | None = None


# --- где лежит выбор ---


def config_path() -> Path:
    """`~/.config/chekan/config.json` — рядом с прочими настройками инструмента."""
    override = os.environ.get(CONFIG_ENV)
    if override:
        return Path(override).expanduser()
    base = os.environ.get("XDG_CONFIG_HOME") or (Path.home() / ".config")
    return Path(base).expanduser() / "chekan" / "config.json"


def _read_config() -> dict:
    try:
        data = json.loads(config_path().read_text(encoding="utf-8"))
    except Exception:  # нет файла, битый JSON — настройки просто нет
        return {}
    return data if isinstance(data, dict) else {}


def stored_language() -> str | None:
    """Сохранённый выбор или None. Мусор в файле — как будто выбора не было."""
    value = _read_config().get(CONFIG_KEY)
    return value if value in LANGUAGES else None


def save_language(code: str) -> Path:
    """Записать выбор, сохранив соседние ключи файла настроек."""
    if code not in LANGUAGES:
        raise ValueError(f"неизвестный язык: {code}")
    path = config_path()
    data = _read_config()
    data[CONFIG_KEY] = code
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
    global _cache
    _cache = code
    return path


# --- какой язык сейчас ---


def language() -> str:
    """Переменная окружения поверх сохранённого выбора, иначе русский."""
    global _cache
    env = os.environ.get(LANG_ENV, "").strip().lower()
    if env in LANGUAGES:
        return env
    if _cache is None:
        _cache = stored_language() or DEFAULT
    return _cache


def forget() -> None:
    """Сбросить память процесса о языке: файл настроек мог смениться."""
    global _cache
    _cache = None


def translate(text: str, lang: str | None = None) -> str:
    """Строка инструмента → перевод. Нет в каталоге — русский как есть.

    Данные владельца сюда попадают наравне со строками инструмента и выходят
    нетронутыми ровно потому, что их в каталоге нет и быть не должно.
    """
    lang = lang or language()
    if lang == RUSSIAN:
        return text
    return CATALOG.get(text, text)


# --- выбор при первом запуске ---


def choose_on_first_run(write=None, ask=None, stdin=None, stdout=None) -> str:
    """Спросить язык один раз. Второй запуск и любая труба — молча.

    Возвращает язык, которым дальше говорить. Вопрос задаётся, только если
    выбора ещё нет И вход настоящий терминал: `/api/cli`, хуки и чужие агенты
    ждать ответа не умеют — они повиснут.
    """
    from .shell import is_interactive

    env = os.environ.get(LANG_ENV, "").strip().lower()
    if env in LANGUAGES:
        return env
    saved = stored_language()
    if saved:
        return saved
    if not is_interactive(stdin, stdout):
        return DEFAULT

    write = write or (lambda text: print(text))
    ask = ask or input
    try:
        answer = ask(QUESTION)
    except (EOFError, KeyboardInterrupt):
        return DEFAULT
    code = ANSWERS.get(str(answer).strip().lower(), DEFAULT)
    path = save_language(code)
    write(
        translate("Язык интерфейса выбран: {lang}. Поменять — {path}.", code).format(
            lang=NAMES[code], path=path
        )
    )
    return code
