"""Report-language resolution for the report-writer prompt.

Both dispatchers stamp `**Report Language:**` into the report-writer prompt, so
the precedence (project config → global config → inferred from the task brief)
lives here rather than in whichever dispatcher happened to need it first.
"""
from __future__ import annotations

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

from .worker_prompt_body import instruction_path
from .json_boundary import JsonBoundaryError, load_owned_object

REPORT_LANGUAGE_VALUES = {"en", "ko", "auto"}

# Below this many Hangul syllables a brief reads as incidental Korean in an
# otherwise English document.
_HANGUL_THRESHOLD = 10
_BRIEF_SCAN_CHARS = 16000


class ReportLanguageError(ValueError):
    """A config file declares a reportLanguage outside the allowed values."""


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 == "auto":
            return _infer_report_language(project_root, manifest, active_context)
        if value:
            return value
    return _infer_report_language(project_root, manifest, active_context)


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 isinstance(value, str) and value in REPORT_LANGUAGE_VALUES:
        return value
    raise ReportLanguageError(
        f"{label} config reportLanguage must be en, ko, or auto: {path}"
    )


def _infer_report_language(
    project_root: Path,
    manifest: Mapping[str, Any],
    active_context: Mapping[str, Any],
) -> str:
    task_brief = instruction_path(manifest, active_context, "taskBriefPath")
    if not task_brief:
        return "en"
    path = Path(task_brief)
    if not path.is_absolute():
        path = project_root / path
    if not path.is_file():
        return "en"
    text = path.read_text(encoding="utf-8", errors="replace")[:_BRIEF_SCAN_CHARS]
    hangul = sum(1 for char in text if "가" <= char <= "힣")
    return "ko" if hangul >= _HANGUL_THRESHOLD else "en"
