"""Minimal versioned prompt-template scaffolding (#960).

Полная библиотека промптов приедет вместе с консьюмерами (#956 граддок-extraction,
#957 chat). Здесь — только каркас: версионированные шаблоны + ``render`` с явной
подстановкой переменных. Шаблоны намеренно НЕ содержат данных — данные вставляются
вызывающим в ``SafePayload`` (см. redaction), а не зашиваются в prompt-литералы.

Версионирование: ключ шаблона включает версию (``name@vN``), чтобы изменение
формулировки было трассируемым (важно для воспроизводимости LLM-вывода).
"""

from __future__ import annotations

from dataclasses import dataclass


class PromptNotFoundError(KeyError):
    """Запрошен неизвестный template_name."""


@dataclass(frozen=True, slots=True)
class PromptTemplate:
    """Версионированный шаблон. ``required_vars`` валидируется в render."""

    name: str
    version: int
    template: str
    required_vars: tuple[str, ...] = ()

    @property
    def key(self) -> str:
        return f"{self.name}@v{self.version}"


# Системный шаблон-заглушка. Без данных — только инструкция-роль. Консьюмеры
# добавят свои предметные шаблоны (extraction-schema / chat-system) здесь же.
_SYSTEM_BASE = PromptTemplate(
    name="system_base",
    version=1,
    template=(
        "Ты ассистент в системе анализа недвижимости. Отвечай кратко и по делу. "
        "Не выдумывай факты — если данных недостаточно, скажи об этом."
    ),
)

# Системный шаблон чата по §22-форсайту (#957, Step 2). Версионируется (@vN) — изменение
# формулировки трассируемо. БЕЗ данных: данные приходят секционными tool'ами в SafePayload
# (см. services/chat/{tools,safe_payload}.py), НЕ зашиваются в литерал промпта.
_CHAT_SYSTEM = PromptTemplate(
    name="chat_system",
    version=3,
    template=(
        "Ты — ассистент по инвестиционному форсайт-отчёту земельного участка (РФ). "
        "Отвечай на русском языке, по-деловому, нейтрально, без маркетинга и без emoji.\n\n"
        "ЖЁСТКИЕ ПРАВИЛА:\n"
        "1. Отвечай ТОЛЬКО на основе данных, полученных через инструменты (секции отчёта). "
        "Чтобы получить нужные числа, вызови подходящий инструмент. Для вопросов про сам "
        "участок — адрес, площадь, категорию земель, ВРИ, территориальную зону ПЗЗ и её "
        "код/название, лимиты застройки, ЗОУИТ-обременения — вызови get_parcel_info.\n"
        "2. НИКОГДА не выдумывай числа, классы, доли или выводы. Если в полученной секции "
        "данных нет (или помечено available=false) — честно скажи, что этих данных в "
        "отчёте нет. Не подставляй правдоподобные значения.\n"
        "3. Все числа в ответе бери ВЕРБАТИМ из секций инструментов, ничего не пересчитывай.\n"
        "4. Тон советующий: отчёт помогает принять решение, но НЕ является основанием для "
        "инвестиционного решения. Не давай гарантий доходности.\n"
        "5. Вопросы, выходящие за рамки данных отчёта и паспорта участка (сравнение с другими "
        "участками, юридические заключения, получение разрешений/согласований) — вежливо "
        "скажи, что это вне области отчёта, и предложи вопросы по участку и его форсайту.\n"
        "6. При перечислении квартирографии / сегментов указывай ТИП понятно "
        "(студия, 1-к, 2-к, 3-к, евро-форматы, 80+ м²) и долю в % если она есть. "
        "НЕ нумеруй порядковыми номерами и НЕ склеивай номер с типом через дефис "
        "(никаких «2-1-к»); перечисляй по одному типу в строке или через запятую.\n"
        "Отвечай кратко и по существу заданного вопроса."
    ),
)

_TEMPLATES: dict[str, PromptTemplate] = {
    _SYSTEM_BASE.name: _SYSTEM_BASE,
    _CHAT_SYSTEM.name: _CHAT_SYSTEM,
}


def get_template(name: str) -> PromptTemplate:
    """Вернуть шаблон по имени (последняя версия). PromptNotFoundError если нет."""
    tpl = _TEMPLATES.get(name)
    if tpl is None:
        raise PromptNotFoundError(name)
    return tpl


def render(template_name: str, **vars: object) -> str:
    """Отрендерить шаблон по имени с подстановкой переменных.

    Использует ``str.format`` (не f-string/concat) — placeholder'ы вида ``{var}``.
    Проверяет, что все ``required_vars`` переданы.

    Raises:
        PromptNotFoundError: неизвестное имя.
        KeyError: отсутствует обязательная переменная или placeholder без значения.
    """
    tpl = get_template(template_name)
    missing = [v for v in tpl.required_vars if v not in vars]
    if missing:
        raise KeyError(f"prompt '{tpl.key}' missing required vars: {', '.join(missing)}")
    return tpl.template.format(**vars)


__all__ = ["PromptNotFoundError", "PromptTemplate", "get_template", "render"]
