"""로스터 worker_id 어휘와 그로부터 나오는 아티팩트 경로."""
from __future__ import annotations

from pathlib import Path
from typing import Any, Mapping

from .domain.role import ROLE_DEFINITIONS
from .models import provider_ids

# worker_id 접미사로 쓰이는 역할 토큰 — 역할 카탈로그가 정본이다.
# 이 모듈은 leaf 로 남아야 한다(`worker_prompt_body` 를 끌어오면
# `run_context` ↔ `path_hints` 순환이 된다). 그래서 고정 worker_id 인
# `report-writer` 는 이름으로 특수 처리하지 않는다 — 마지막 하이픈 뒤가
# `writer` 라 어느 역할 토큰과도 맞지 않아 자연히 접미사 없음으로 떨어진다.
_ROLE_SUFFIX_TOKENS = frozenset(
    definition.id for definition in ROLE_DEFINITIONS
)


def worker_role_suffix(worker_id: str) -> str:
    """worker_id 에서 provider 뒤에 붙은 역할 접미사. 없으면 빈 문자열.

    `claude-verifier` → `verifier`, `claude-verifier-2` → `verifier-2`,
    `claude` → `""`, `report-writer` → `""`.
    """
    stem, separator, tail = worker_id.rpartition("-")
    ordinal = ""
    if separator and tail.isdigit():
        ordinal, worker_id = f"-{tail}", stem
    _, separator, role = worker_id.rpartition("-")
    if not separator or role not in _ROLE_SUFFIX_TOKENS:
        return ""
    return f"{role}{ordinal}"


def worker_provider_id(worker_id: str) -> str:
    """역할 접미사가 붙은 worker_id 에서 provider id 를 되돌린다.

    한 provider 가 로스터에서 두 슬롯 이상을 차지하면 슬롯마다
    `claude-implementer` · `claude-verifier` 처럼 역할이 붙는다. 그 접미사를 떼면
    provider registry 가 아는 id 로 돌아간다 — 접미사가 없는 id 는 그대로 남는다.
    """
    suffix = worker_role_suffix(worker_id)
    return worker_id[: -(len(suffix) + 1)] if suffix else worker_id


def roster_worker_ids(ctx: Mapping[str, Any]) -> list[str]:
    """이 런이 실제로 여는 worker_id 목록.

    같은 provider 가 실행자와 검증자를 겸하면 로스터에 `claude` 와 `claude-2` 가
    함께 서므로, provider registry 목록으로는 두 번째 슬롯의 경로를 만들 수 없다.
    로스터를 싣지 않은 ctx(경로 힌트에서 되살린 레거시 컨텍스트)에서는 registry
    목록으로 돌아간다 — 그 경우 접미사 id 는 애초에 등장하지 않는다.
    """
    roster = [
        part.strip()
        for part in str(ctx.get("RECOMMENDED_ANALYSERS", "") or "").split(",")
        if part.strip()
    ]
    return roster or [*provider_ids("analyser"), "report-writer"]


def worker_artifact_paths(
    ctx: Mapping[str, Any], worker_id: str, *, relative: bool
) -> dict[str, str]:
    """worker_id 하나의 prompt/result/errors 경로.

    경로는 id 에서 직접 만들어지므로 로스터에 없는 id 로도 부를 수 있다 —
    payload 로만 전달된 워커 행을 되살릴 때 필요하다.
    """
    prompt_root = _context_root(ctx, "RUN_PROMPTS", relative)
    result_root = _context_root(ctx, "WORKER_RESULTS", relative)
    task_type = str(ctx.get("TASK_TYPE_SEGMENT", ""))
    prompt_suffix = f"-{task_type}-{ctx.get('RUN_PROMPTS_SEQ', '')}"
    result_suffix = f"-{task_type}-{ctx.get('WORKER_RESULTS_SEQ', '')}"
    return {
        "promptPath": str(prompt_root / f"{worker_id}-worker-prompt{prompt_suffix}.md"),
        "resultPath": str(result_root / f"{worker_id}-worker{result_suffix}.md"),
        "errorsSidecarPath": str(
            result_root / f"{worker_id}-worker-errors{result_suffix}.json"
        ),
    }


def artifacts_from_context(
    ctx: Mapping[str, Any], *, relative: bool
) -> dict[str, dict[str, str]]:
    """이 런의 로스터 worker_id 별 prompt/result/errors 경로."""
    return {
        worker_id: worker_artifact_paths(ctx, worker_id, relative=relative)
        for worker_id in roster_worker_ids(ctx)
    }


def _context_root(ctx: Mapping[str, Any], name: str, relative: bool) -> Path:
    if name == "WORKER_RESULTS":
        key = "WORKER_RESULTS_RELATIVE_PATH" if relative else "WORKER_RESULTS_PATH"
    else:
        suffix = "RELATIVE_PATH" if relative else "DIR"
        key = f"{name}_{suffix}"
    value = str(ctx.get(key, ""))
    if value or relative:
        return Path(value)
    relative_value = str(ctx.get(f"{name}_RELATIVE_PATH", ""))
    return Path(str(ctx.get("PROJECT_ROOT", ""))) / relative_value
