"""team_lock.py — 팀별 뮤텍스 (fcntl.flock 기반).

작성자: 토르 (dev2-team backend)
날짜: 2026-03-24
"""

import fcntl
import os

__all__ = ["TeamLock"]

LOCKS_DIR = "/home/jay/workspace/orchestrator/locks"


class TeamLock:
    """fcntl.flock 기반 팀별 배타적 접근 제어.

    Anu와 auto_orch 중 하나만 동일 팀에 dispatch 가능.
    Context manager로 사용.

    Usage:
        with TeamLock("dev1-team"):
            # 이 팀에 대한 독점 접근
            dispatch_to_team(...)
        # 자동 해제

        # 가용 여부만 확인
        if TeamLock.is_team_available("dev1-team"):
            ...
    """

    def __init__(self, team_id: str, locks_dir: str = LOCKS_DIR):
        self._team_id = team_id
        self._locks_dir = locks_dir
        self._lock_path = os.path.join(locks_dir, f"{team_id}.lock")
        self._fh: int | None = None

    def __enter__(self) -> "TeamLock":
        """LOCK_EX | LOCK_NB 획득. 실패 시 BlockingIOError 발생."""
        os.makedirs(self._locks_dir, exist_ok=True)
        self._fh = os.open(self._lock_path, os.O_CREAT | os.O_RDWR)
        try:
            fcntl.flock(self._fh, fcntl.LOCK_EX | fcntl.LOCK_NB)
        except (BlockingIOError, OSError):
            os.close(self._fh)
            self._fh = None
            raise BlockingIOError(f"Team '{self._team_id}' is currently locked by another process.")
        return self

    def __exit__(self, exc_type, exc_val, exc_tb) -> None:
        """락 해제 + 파일 디스크립터 닫기."""
        if self._fh is not None:
            try:
                fcntl.flock(self._fh, fcntl.LOCK_UN)
            finally:
                os.close(self._fh)
                self._fh = None

    @staticmethod
    def is_team_available(team_id: str, locks_dir: str = LOCKS_DIR) -> bool:
        """비블로킹 시도로 팀 가용 여부를 반환한다.

        LOCK_NB 시도 → 성공하면 즉시 해제하고 True, 실패하면 False.
        """
        lock_path = os.path.join(locks_dir, f"{team_id}.lock")
        os.makedirs(locks_dir, exist_ok=True)
        fd = os.open(lock_path, os.O_CREAT | os.O_RDWR)
        try:
            fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
            fcntl.flock(fd, fcntl.LOCK_UN)
            return True
        except (BlockingIOError, OSError):
            return False
        finally:
            os.close(fd)
