"""Final-report i18n dictionary loader + Jinja2 lookup function.

사전은 ``templates/reports/i18n/<lang>.json`` 에 둔다. ChainableUndefined
환경에서도 누락 키가 silent 로 빈 문자열이 되지 않도록 lookup 함수가
직접 raise 한다.
"""
from __future__ import annotations

import json
import os
from pathlib import Path
from typing import Any, Callable

from .json_boundary import JsonBoundaryError, load_owned_object

SUPPORTED_LANGS = ("en", "ko")
DICTIONARY_REL = ("templates", "reports", "i18n")
# The human HTML has its own fixed strings — table headings, empty states,
# enum labels — that the AI-handoff Markdown never renders. Same loader, its
# own dictionary, so a heading added to one does not have to exist in the other.
HTML_DICTIONARY_REL = ("templates", "reports", "html", "i18n")


class I18nError(RuntimeError):
    """사전 lookup 실패 또는 사전 로드 실패."""


def _i18n_dir(rel: tuple[str, ...] = DICTIONARY_REL) -> Path:
    okstra_home = os.environ.get("OKSTRA_HOME")
    if okstra_home:
        candidate = Path(okstra_home).joinpath(*rel)
        if candidate.is_dir():
            return candidate
    here = Path(__file__).resolve()
    for parent in [here, *here.parents]:
        candidate = parent.joinpath(*rel)
        if candidate.is_dir():
            return candidate
    joined = "/".join(rel)
    raise I18nError(
        f"could not locate {joined}/. Set OKSTRA_HOME or "
        f"run from a checkout that contains {joined}/."
    )


def load_dictionary(lang: str, rel: tuple[str, ...] = DICTIONARY_REL) -> dict[str, Any]:
    if lang not in SUPPORTED_LANGS:
        raise I18nError(
            f"unsupported reportLanguage {lang!r}; supported: {SUPPORTED_LANGS}"
        )
    path = _i18n_dir(rel) / f"{lang}.json"
    try:
        return load_owned_object(path, artifact="i18n dictionary")
    except JsonBoundaryError as exc:
        raise I18nError(f"failed to load {path}: {exc}") from exc


def lookup(dictionary: dict[str, Any], dotted_key: str) -> str:
    parts = dotted_key.split(".")
    cur: Any = dictionary
    for i, part in enumerate(parts):
        if not isinstance(cur, dict):
            raise I18nError(
                f"i18n key {dotted_key!r}: segment {'.'.join(parts[:i]) or '<root>'!r} "
                f"is not a dict (got {type(cur).__name__})"
            )
        if part not in cur:
            raise I18nError(f"i18n key {dotted_key!r} not found in dictionary")
        cur = cur[part]
    if not isinstance(cur, str):
        raise I18nError(
            f"i18n key {dotted_key!r} resolved to {type(cur).__name__}, expected str"
        )
    return cur


def make_jinja_global(dictionary: dict[str, Any]) -> Callable[[str], str]:
    def t(dotted_key: str) -> str:
        return lookup(dictionary, dotted_key)
    return t
