"""Report-language resolution — the one place the precedence lives.

Two surfaces need the answer and must agree: the `**Report Language:**` line
both dispatchers stamp into the report-writer prompt, and `reportLanguage` in
the run manifest, which report assembly copies into `meta.reportLanguage` and
the HTML renderer reads. When only the prompt called this, a project could ask
for `ko`, get a Korean-instructed writer, and still ship an English report.

The value is a language tag (`en`, `ko`, `fr`, `pt-BR`, …). The Phase 7
translator receives that tag and translates into it; nothing here knows any
particular language. Precedence: project config → global config → `en`.
"""
from __future__ import annotations

import os
import re
from pathlib import Path
from typing import Any, Mapping

from .json_boundary import JsonBoundaryError, load_owned_object

DEFAULT_REPORT_LANGUAGE = "en"

# BCP 47 의 흔한 꼴만 — 소문자 2~3자 언어 코드와 선택적 하위 태그. 대문자
# 언어 코드나 단어(`english`)는 사이드카 파일명과 사전 파일명이 갈리므로 거절.
_LANGUAGE_TAG = re.compile(r"^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$")


class ReportLanguageError(ValueError):
    """A config file declares a reportLanguage that is not a language tag."""


def is_language_tag(value: object) -> bool:
    return isinstance(value, str) and _LANGUAGE_TAG.match(value) is not None


def resolve_report_language(
    project_root: Path,
    manifest: Mapping[str, Any],
    active_context: Mapping[str, Any],
) -> str:
    for value in _report_language_candidates(project_root):
        if value:
            return value
    return DEFAULT_REPORT_LANGUAGE


def _report_language_candidates(project_root: Path) -> list[str]:
    okstra_home = Path(os.environ.get("OKSTRA_HOME") or Path.home() / ".okstra")
    return [
        _read_report_language(project_root / ".okstra" / "project.json", "project"),
        _read_report_language(okstra_home / "config.json", "global"),
    ]


def _read_report_language(path: Path, label: str) -> str:
    if not path.is_file():
        return ""
    try:
        payload = load_owned_object(path, artifact=f"{label} configuration")
    except JsonBoundaryError as exc:
        raise ReportLanguageError(f"{label} config is unreadable: {path}") from exc
    if not isinstance(payload, Mapping):
        raise ReportLanguageError(f"{label} config must be a JSON object: {path}")
    value = payload.get("reportLanguage")
    if value in (None, ""):
        return ""
    if is_language_tag(value):
        return value
    raise ReportLanguageError(
        f"{label} config reportLanguage must be a language tag such as en, ko, "
        f"fr or pt-BR, got {value!r}: {path}"
    )
