"""중앙 락과 task-lock 파일명. 다른 okstra_ctl 모듈에 의존하지 않는다."""
from __future__ import annotations

import contextlib
import fcntl

from .ids import _escape_segment_for_join, _safe_fs_segment


@contextlib.contextmanager
def central_lock(home):
    """~/.okstra/.lock 위에 fcntl LOCK_EX. okstra-central.sh 의
    okstra_central_with_lock 과 같은 파일을 사용하므로 record_start 와
    상호 직렬화된다.
    """
    from pathlib import Path as _Path
    home_p = _Path(home)
    home_p.mkdir(parents=True, exist_ok=True)
    lockfile = home_p / ".lock"
    if not lockfile.exists():
        lockfile.touch()
    f = lockfile.open("r+")
    try:
        fcntl.flock(f.fileno(), fcntl.LOCK_EX)
        yield
    finally:
        f.close()


@contextlib.contextmanager
def worktree_provision_mutex(home, project_id: str, task_group: str,
                             task_id: str):
    """task-key 단위 worktree 프로비저닝 임계구역
    (`<home>/.locks/worktree-provision/<task-key>.lock` 위 fcntl LOCK_EX).

    사전검사(registry lookup / 경로·브랜치 존재)·stage 선택·`git worktree add`·
    registry reserve 가 락 없이 인터리브되면: 동시 `--stage auto` run 둘이 같은
    stage 를 선택해 후발이 "reused" 경로로 같은 worktree 에 들어가고, 같은
    경로·브랜치를 둔 git 경쟁이 비결정적으로 실패하며, 한쪽 rollback
    (`git worktree remove --force`)이 다른 쪽 worktree 를 지울 수 있다.
    stage worktree 도 같은 task-key 락을 공유한다 — 브랜치 네임스페이스와
    common-anchor 계산이 task 단위로 묶여 있기 때문.

    flock 은 재진입 불가: 이 락을 쥔 채 다시 acquire 하면 self-deadlock 이므로
    provision 함수 내부가 아니라 호출자(run.py orchestration)가 1회 감싼다.
    """
    from pathlib import Path as _Path
    locks_dir = _Path(home) / ".locks" / "worktree-provision"
    locks_dir.mkdir(parents=True, exist_ok=True)
    parts = [_escape_segment_for_join(_safe_fs_segment(s))
             for s in (project_id, task_group, task_id)]
    lockfile = locks_dir / ("-".join(parts) + ".lock")
    lockfile.touch(exist_ok=True)
    with lockfile.open("r+") as f:
        fcntl.flock(f.fileno(), fcntl.LOCK_EX)
        try:
            yield
        finally:
            fcntl.flock(f.fileno(), fcntl.LOCK_UN)


def task_lock_filename(project_id: str, task_group: str, task_id: str,
                       task_type: str) -> str:
    """task-key + task-type 단위 mutex 파일명. ~/.okstra/.locks/ 아래에 위치한다.
    okstra 의 RUN_DIR 가 task-type 별로 분리되므로 mutex 도 같은 입자에 둔다.
    각 세그먼트는 먼저 fs-safe 슬러그로 정규화해 `/` 나 `..` 가 .locks 디렉터리
    밖으로 mutex 를 escape 시키는 것을 막고, 이어서 `-` 를 `--` 로 escape 한
    뒤 `-` 로 조인해 슬래시 경계를 보존한다(('p','feature-8','email','x') 와
    ('p','feature','8-email','x') 가 같은 파일명으로 매핑되어 mutex 가 공유
    되는 문제 방지).
    """
    parts = [_escape_segment_for_join(_safe_fs_segment(s))
             for s in (project_id, task_group, task_id, task_type)]
    return "-".join(parts) + ".lock"
