"""Shared slot load/save and Jinja2 preview helpers for template config APIs."""

from typing import TypeVar

from jinja2 import StrictUndefined, TemplateSyntaxError, UndefinedError
from jinja2.sandbox import SandboxedEnvironment
from sqlmodel import SQLModel, select
from sqlmodel.ext.asyncio.session import AsyncSession

S = TypeVar("S", bound=SQLModel)


async def load_slots(
    session: AsyncSession,
    slot_model: type[S],
    config_id: int,
) -> dict[str, dict[str, str]]:
    """Load all template slots for a config as {slot_name: {locale: template}}.

    Works for both TemplateSlot and CommandTemplateSlot — they share the same
    column names (config_id, slot_name, locale, template).
    """
    result = await session.exec(
        select(slot_model).where(slot_model.config_id == config_id)  # type: ignore[attr-defined]
    )
    slots: dict[str, dict[str, str]] = {}
    for s in result.all():
        slots.setdefault(s.slot_name, {})[s.locale] = s.template  # type: ignore[attr-defined]
    return slots


async def save_slots(
    session: AsyncSession,
    slot_model: type[S],
    config_id: int,
    slots: dict[str, dict[str, str]],
) -> None:
    """Create or update template slots for a config (locale-aware).

    Works for both TemplateSlot and CommandTemplateSlot.
    """
    for slot_name, locale_map in slots.items():
        for locale, template_text in locale_map.items():
            result = await session.exec(
                select(slot_model).where(
                    slot_model.config_id == config_id,  # type: ignore[attr-defined]
                    slot_model.slot_name == slot_name,  # type: ignore[attr-defined]
                    slot_model.locale == locale,  # type: ignore[attr-defined]
                )
            )
            existing = result.first()
            if existing:
                existing.template = template_text  # type: ignore[attr-defined]
                session.add(existing)
            else:
                session.add(slot_model(
                    config_id=config_id,
                    slot_name=slot_name,
                    locale=locale,
                    template=template_text,
                ))


def render_template_preview(template: str, context: dict) -> dict:
    """Two-pass Jinja2 render: syntax check, then strict render.

    Returns a dict with either ``{"rendered": str}`` on success, or
    ``{"rendered": None, "error": str, ...}`` on failure.
    """
    # Pass 1: syntax check (default Undefined — catches parse errors only)
    try:
        env = SandboxedEnvironment(autoescape=False)
        env.from_string(template)
    except TemplateSyntaxError as e:
        return {
            "rendered": None,
            "error": e.message,
            "error_line": e.lineno,
        }

    # Pass 2: render with StrictUndefined to catch unknown variables
    try:
        strict_env = SandboxedEnvironment(autoescape=False, undefined=StrictUndefined)
        tmpl = strict_env.from_string(template)
        rendered = tmpl.render(**context)
        return {"rendered": rendered}
    except UndefinedError as e:
        return {"rendered": None, "error": str(e), "error_line": None, "error_type": "undefined"}
    except Exception as e:
        return {"rendered": None, "error": str(e), "error_line": None}
