"""PR body 템플릿 경로 해석.

release-handoff 단계에서 lead 가 PR 본문을 작성할 때 사용하는 마크다운
템플릿의 경로를 결정한다. 우선순위:

    1. per-run override (okstra-run Step 6 에서 입력)
    2. project: <project_root>/.okstra/project.json 의 ``prTemplatePath``
    3. global:  ~/.okstra/config.json 의 ``prTemplatePath``
    4. default: ``$OKSTRA_SKILLS_DIR/okstra-run/templates/pr-body.template.md``
       (env 가 설정된 경우에만) → ``$OKSTRA_HOME/templates/pr/pr-body.template.md``

경로는 절대경로 또는 ``~`` 시작 경로를 권장한다. 상대경로일 경우 project
스코프는 ``project_root`` 기준, override 는 호출자 cwd 기준으로 해석한다.
global 스코프에서 상대경로는 모호하므로 거절한다.
"""
from __future__ import annotations

import json
import os
from dataclasses import dataclass
from pathlib import Path

from okstra_project.dirs import okstra_home, project_json_path
from .json_boundary import JsonBoundaryError, load_owned_object

_DEFAULT_FILENAME = "pr-body.template.md"


class PrTemplateError(Exception):
    """invalid PR template configuration — surface to user."""


@dataclass(frozen=True)
class ResolvedPrTemplate:
    path: Path
    source: str  # "override" | "project" | "global" | "default"


def _default_template_candidates() -> list[Path]:
    """디폴트 템플릿 후보 경로들 (우선순위 순)."""
    out: list[Path] = []
    env_dir = os.environ.get("OKSTRA_SKILLS_DIR", "").strip()
    if env_dir:
        out.append(Path(env_dir) / "okstra-run" / "templates" / _DEFAULT_FILENAME)
    out.append(okstra_home() / "templates" / "pr" / _DEFAULT_FILENAME)
    return out


def _read_json_field(path: Path, field: str) -> str:
    if not path.is_file():
        return ""
    try:
        data = load_owned_object(path, artifact="PR template configuration")
    except JsonBoundaryError:
        return ""
    if not isinstance(data, dict):
        return ""
    return str(data.get(field) or "").strip()


def _resolve_project_relative(value: str, project_root: Path) -> Path:
    p = Path(value).expanduser()
    return p if p.is_absolute() else (Path(project_root) / p).resolve()


def resolve_pr_template_path(
    project_root: Path,
    override_path: str = "",
) -> ResolvedPrTemplate:
    """우선순위에 따라 PR 템플릿 경로를 해석한다.

    Raises:
      PrTemplateError: 설정된 경로가 존재하지 않거나, 디폴트조차 찾지 못한 경우.
    """
    project_root = Path(project_root)

    # 1) per-run override
    ov = (override_path or "").strip()
    if ov:
        p = _resolve_project_relative(ov, project_root)
        if not p.is_file():
            raise PrTemplateError(f"override PR template not found: {p}")
        return ResolvedPrTemplate(path=p, source="override")

    # 2) project.json
    pj_path = project_json_path(project_root)
    pj_val = _read_json_field(pj_path, "prTemplatePath")
    if pj_val:
        p = _resolve_project_relative(pj_val, project_root)
        if not p.is_file():
            raise PrTemplateError(
                f"project.json prTemplatePath points to missing file: {p} "
                f"(configured in {pj_path})"
            )
        return ResolvedPrTemplate(path=p, source="project")

    # 3) global config
    gc_path = okstra_home() / "config.json"
    gv = _read_json_field(gc_path, "prTemplatePath")
    if gv:
        p = Path(gv).expanduser()
        if not p.is_absolute():
            raise PrTemplateError(
                f"global config prTemplatePath must be absolute or start with '~/': got {gv!r} "
                f"(configured in {gc_path})"
            )
        if not p.is_file():
            raise PrTemplateError(
                f"global prTemplatePath missing: {p} (configured in {gc_path})"
            )
        return ResolvedPrTemplate(path=p, source="global")

    # 4) default
    for cand in _default_template_candidates():
        if cand.is_file():
            return ResolvedPrTemplate(path=cand, source="default")
    raise PrTemplateError(
        "no PR template available: default template not found. "
        f"Searched: {', '.join(str(c) for c in _default_template_candidates())}. "
        "Reinstall okstra (`npx okstra install`) or set prTemplatePath in "
        "project.json / ~/.okstra/config.json."
    )
