"""task-key 를 worktree 경로와 브랜치 이름으로 옮긴다.

경로도 브랜치도 파일 시스템과 git 참조 양쪽에서 안전한 문자만 남겨야 하고,
브랜치는 work-category 별 namespace 아래로 들어간다. 순수 문자열 계산이라
디스크도 git 도 보지 않는다 — 그래서 provision 전에 미리 보여 줄 수 있다.
"""
from __future__ import annotations

from pathlib import Path

from okstra_project.dirs import okstra_home

from ..ids import _safe_fs_segment


# Work-category → branch namespace (slash-prefixed). Mirrors the values
# accepted by `--work-category` (bugfix / feature / refactor / ops /
# improvement); `feature` and `improvement` share the `feature/` namespace,
# and any unset/unrecognised category falls back to `task/`.
_WORK_CATEGORY_NAMESPACE = {
    "feature": "feature",
    "improvement": "feature",
    "bugfix": "fix",
    "refactor": "refactor",
    "ops": "ops",
}


def _safe_segment(value: str) -> str:
    """Sanitise a single path/branch segment.

    Forbidden chars (`/`, `:`, spaces, anything outside `[a-z0-9-]`)
    are collapsed to `-`. Empty result becomes `_` so we never create
    an empty path component. Delegates to the canonical slugifier in
    `ids.py` to stay in lock-step with run-id / manifest segmentation.
    """
    return _safe_fs_segment(value)


def _work_category_namespace(work_category: str) -> str:
    key = (work_category or "").strip().lower()
    return _WORK_CATEGORY_NAMESPACE.get(key, "task")


def compute_worktree_path(
    *,
    project_id: str,
    task_group_segment: str,
    task_id_segment: str,
    stage_number: Optional[int] = None,
    group_id: Optional[str] = None,
) -> Path:
    """Pure path computation. One worktree dir per task-key, or the sibling
    `<task-key>--stage-<N>` when stage_number is given (implementation stage
    isolation). Uses `OKSTRA_HOME` when set (test hook), else `~/.okstra`.

    stage worktree 는 task worktree 의 *형제* 다. 종전에는 `<task>/stage-<N>/`
    로 안에 두었는데, 그 트리는 task worktree 의 `git status` 에 `?? stage-N/`
    로 잡히고 프로젝트의 prettier·tsc 가 소스로 읽었다 — 실측(2026-09-06,
    fontsninja-v3-site final-verification 001): `pnpm prettier:check` 가
    `stage-1/.next/**` 1,107건으로, `pnpm build` 가 `stage-1/src/**` 의 중복
    전역 선언으로 실패해 두 검증자가 이를 acceptance blocker 로 합의했다.
    whole-task final-verification 은 판정 뒤까지 stage worktree 를 남기므로
    (`stage_targets`, `teardown=False`) 검증 대상 트리에서 그 디렉터리가 사라질
    길이 없었다. 형제 배치는 git·도구·변이 감사 모두에게 같은 답이 된다."""
    if stage_number is not None and group_id is not None:
        raise ValueError("stage_number and group_id are mutually exclusive")
    base = okstra_home()
    path = (
        base / "worktrees"
        / _safe_segment(project_id)
        / _safe_segment(task_group_segment)
        / _safe_segment(task_id_segment)
    )
    if stage_number is not None:
        path = path.with_name(f"{path.name}--stage-{stage_number}")
    if group_id is not None:
        path = path.with_name(f"{path.name}--group-{_safe_segment(group_id)}")
    return path


def compute_branch_name(
    *,
    work_category: str,
    task_id_segment: str,
    stage_number: Optional[int] = None,
    group_id: Optional[str] = None,
) -> str:
    """One branch per task-key as `<namespace>/<task-id>`, or
    `<namespace>/<task-id>-s<N>` for an implementation stage worktree. The
    namespace is a controlled constant so its slash is preserved; only the
    task-id segment is sanitised."""
    if stage_number is not None and group_id is not None:
        raise ValueError("stage_number and group_id are mutually exclusive")
    name = f"{_work_category_namespace(work_category)}/{_safe_segment(task_id_segment)}"
    if stage_number is not None:
        name = f"{name}-s{stage_number}"
    if group_id is not None:
        name = f"{name}-{group_id}"
    return name
