"""Worktree path 후보 resolver.

ANCHOR-4: worktree 후보 위치 2종을 동시에 grep 한다.
- legacy:  /home/jay/workspace/.worktrees/
- cokacdir: /home/jay/.cokacdir/workspace/<schedule_id>/wt-<task_id>-<team>/

한쪽이 missing 하다는 사실만으로 silent drop 을 단정해서는 안 된다.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from pathlib import Path
from typing import Optional, Sequence


LEGACY_WORKTREE_ROOT = Path("/home/jay/workspace/.worktrees")
COKACDIR_WORKSPACE_ROOT = Path("/home/jay/.cokacdir/workspace")
SCHEDULE_HISTORY_ROOT = Path("/home/jay/.cokacdir/schedule_history")


@dataclass(frozen=True)
class WorktreeCandidates:
    """두 후보 위치 + 매칭된 실제 디렉터리 목록."""

    legacy_root: Path
    cokacdir_root: Path
    schedule_workspace_dir: Optional[Path]
    legacy_matches: tuple[Path, ...] = field(default_factory=tuple)
    cokacdir_matches: tuple[Path, ...] = field(default_factory=tuple)

    @property
    def any_match(self) -> bool:
        return bool(self.legacy_matches or self.cokacdir_matches)

    @property
    def all_matches(self) -> tuple[Path, ...]:
        return tuple(list(self.legacy_matches) + list(self.cokacdir_matches))


def _glob_wt_dirs(root: Path, task_id: str, team_short: Optional[str]) -> tuple[Path, ...]:
    """task_id 기반 wt-/iso- 디렉터리를 glob 한다.

    봇 컨벤션은 다음 4 패턴을 허용한다(과거 호환 포함):
      - wt-<task_id_short>-<team>
      - wt-<task_id_short>
      - iso-<task_id_short>-<team>
      - task-<task_id>-<team>
      - <task_id>-<team>

    여기서 task_id_short 는 'task-' prefix 가 제거된 숫자 부분이다.
    """
    if not root.exists() or not root.is_dir():
        return ()

    short = task_id.removeprefix("task-")
    patterns = [
        f"wt-{short}-*",
        f"wt-{short}",
        f"iso-{short}-*",
        f"iso-wt-{short}-*",
        f"task-{short}-*",
        f"{short}-*",
    ]
    if team_short:
        patterns.extend(
            [
                f"wt-{short}-{team_short}",
                f"iso-{short}-{team_short}",
                f"iso-wt-{short}-{team_short}",
                f"task-{short}-{team_short}",
                f"{short}-{team_short}",
            ]
        )

    seen: dict[str, Path] = {}
    for pattern in patterns:
        for match in root.glob(pattern):
            if match.is_dir():
                seen.setdefault(str(match), match)
    return tuple(sorted(seen.values(), key=lambda p: str(p)))


def resolve_schedule_workspace_dir(
    schedule_id: Optional[str],
    *,
    cokacdir_root: Path = COKACDIR_WORKSPACE_ROOT,
) -> Optional[Path]:
    """schedule_id 기반 workspace 디렉터리 경로(존재 시).

    schedule_id 가 None 또는 빈 문자열이면 None.
    디렉터리가 존재하지 않으면 None.
    """
    if not schedule_id:
        return None
    candidate = cokacdir_root / schedule_id
    return candidate if candidate.exists() and candidate.is_dir() else None


def resolve_worktree_candidates(
    task_id: str,
    team_short: Optional[str],
    schedule_id: Optional[str] = None,
    *,
    legacy_root: Path = LEGACY_WORKTREE_ROOT,
    cokacdir_root: Path = COKACDIR_WORKSPACE_ROOT,
    extra_cokacdir_schedule_ids: Sequence[str] = (),
) -> WorktreeCandidates:
    """두 후보 위치를 동시에 glob 한다.

    cokacdir 측은 schedule_id 가 주어지면 해당 디렉터리 안만 본다.
    schedule_id 가 없으면 모든 schedule_id 디렉터리를 fallback glob
    한다(task-2657 사고 시 ANU 가 schedule_id 컨벤션을 몰라서 누락한
    위치까지 잡아내기 위함).
    """
    legacy_matches = _glob_wt_dirs(legacy_root, task_id, team_short)

    schedule_dir = resolve_schedule_workspace_dir(schedule_id, cokacdir_root=cokacdir_root)

    cokacdir_match_buf: list[Path] = []
    visited: set[str] = set()
    if schedule_dir is not None:
        for m in _glob_wt_dirs(schedule_dir, task_id, team_short):
            key = str(m)
            if key not in visited:
                visited.add(key)
                cokacdir_match_buf.append(m)

    for sid in extra_cokacdir_schedule_ids:
        extra_dir = cokacdir_root / sid
        if extra_dir.exists() and extra_dir.is_dir():
            for m in _glob_wt_dirs(extra_dir, task_id, team_short):
                key = str(m)
                if key not in visited:
                    visited.add(key)
                    cokacdir_match_buf.append(m)

    # schedule_id 자체가 주어지지 않은 경우에만 광역 fallback glob
    # (ANU 가 schedule_id 를 모르고 호출하더라도 false negative 방지)
    if schedule_id is None and cokacdir_root.exists() and cokacdir_root.is_dir():
        for child in cokacdir_root.iterdir():
            if not child.is_dir():
                continue
            for m in _glob_wt_dirs(child, task_id, team_short):
                key = str(m)
                if key not in visited:
                    visited.add(key)
                    cokacdir_match_buf.append(m)

    cokacdir_matches = tuple(sorted(cokacdir_match_buf, key=lambda p: str(p)))

    return WorktreeCandidates(
        legacy_root=legacy_root,
        cokacdir_root=cokacdir_root,
        schedule_workspace_dir=schedule_dir,
        legacy_matches=legacy_matches,
        cokacdir_matches=cokacdir_matches,
    )
