"""Deterministic mock LLM adapter for cycle_advancer PoC.

이 모듈은 외부 AI 호출을 절대 수행하지 않는다. 입력 ``task_id``에 대한
고정 매핑을 반환하여 동일 입력 → 동일 출력을 보장한다. 실제 chain
사례에서 도출된 매핑이며 fixture와 1:1 매치한다.

Mappings (task-2488 명세 기준):
    - task-2485    -> task-2486    "CI pull_request SHA payload fallback fix"
    - task-2483    -> task-2484    "close lifecycle external workspace dirty 해소"
    - task-2472+1  -> task-2472+2  "workflow regex task-N+M 호환"
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Final


@dataclass(frozen=True)
class MockProposal:
    """Mock LLM이 반환하는 다음 task 제안 결과.

    Attributes:
        proposed_task_id: 다음 task 식별자 (예: ``task-2486``).
        scope: 제안 범위 한 줄 요약.
        classification: 입력 task의 본질 분류.
        chairman_required: 합의 시 ``False``, 충돌 시 ``True``.
        conflict_summary: 충돌 사유 요약. 합의 시 ``None``.
        affected_files: 다음 task가 건드릴 후보 파일 경로.
        allowed_resources: 격리 리소스 화이트리스트.
        consensus_root_cause: 합의된 root cause 한 줄 요약.
        candidate_root_causes: 마아트(facts) + 외부 AI(strategy) 후보 N개.
    """

    proposed_task_id: str
    scope: str
    classification: str
    chairman_required: bool
    conflict_summary: str | None
    affected_files: tuple[str, ...]
    allowed_resources: tuple[str, ...]
    consensus_root_cause: str
    candidate_root_causes: tuple[str, ...] = field(default_factory=tuple)


# task-2488 명세에 명시된 deterministic 매핑 테이블.
_MOCK_MAPPINGS: Final[dict[str, MockProposal]] = {
    "task-2485": MockProposal(
        proposed_task_id="task-2486",
        scope="CI pull_request SHA payload fallback fix",
        classification="MERGE_PENDING_DEPENDENCY",
        chairman_required=False,
        conflict_summary=None,
        affected_files=(
            ".github/workflows/ci.yml",
            "tests/regression/test_workflow_pull_request_sha.py",
        ),
        allowed_resources=(
            "PR #47 head_sha 58ed25e",
            "tools/poc/cycle_advancer/fixtures/task-2485.json",
        ),
        consensus_root_cause=(
            "pull_request 이벤트 페이로드에서 SHA 추출 경로가 단일 키에 의존하여 "
            "fallback 부재 — workflow infrastructure defect"
        ),
        candidate_root_causes=(
            "마아트(facts): ci.yml의 SHA 추출이 github.event.pull_request.head.sha 단일 경로에만 의존",
            "외부 AI(strategy): pull_request 이벤트 vs push 이벤트의 payload schema 차이를 fallback chain으로 흡수해야 함",
            "회귀 테스트 부재로 incident 5회 반복 — 페이로드 fallback regression test 신설 필요",
        ),
    ),
    "task-2483": MockProposal(
        proposed_task_id="task-2484",
        scope="close lifecycle external workspace dirty 해소",
        classification="CLOSE_LIFECYCLE_BLOCKED",
        chairman_required=False,
        conflict_summary=None,
        affected_files=(
            "scripts/finish_task.py",
            "tests/regression/test_close_lifecycle_dirty.py",
        ),
        allowed_resources=(
            "PR #46 head_sha abcdef0",
            "tools/poc/cycle_advancer/fixtures/task-2483.json",
        ),
        consensus_root_cause=(
            "finish-task lifecycle close가 외부 worktree dirty 상태를 사전 점검 없이 "
            "진행하여 중단 — pre-flight check 부재"
        ),
        candidate_root_causes=(
            "마아트(facts): finish_task.py가 worktree 상태를 finalize 직전 단계까지 검사하지 않음",
            "외부 AI(strategy): close 단계 진입 전 pre-flight gate를 두고 dirty면 즉시 escalate",
            "외부 worktree에 한정된 dirty 처리 경로가 main repo 처리 경로와 분리되지 않음",
        ),
    ),
    "task-2472+1": MockProposal(
        proposed_task_id="task-2472+2",
        scope="workflow regex task-N+M 호환",
        classification="WORKFLOW_REGEX_INCOMPATIBLE",
        chairman_required=False,
        conflict_summary=None,
        affected_files=(
            ".github/workflows/dispatch.yml",
            "utils/task_id_parser.py",
            "tests/regression/test_workflow_taskid_regex_hardening.py",
        ),
        allowed_resources=(
            "PR #45 head_sha fedcba9",
            "tools/poc/cycle_advancer/fixtures/task-2472+1.json",
        ),
        consensus_root_cause=(
            "workflow의 task-id regex가 task-N+M 형식을 인식하지 못해 dispatch 단계 차단 — "
            "TASK_ID_PATTERN 동기화 누락"
        ),
        candidate_root_causes=(
            "마아트(facts): workflow regex가 ^task-\\d+$ 단일 형식만 허용",
            "외부 AI(strategy): utils/task_id_parser의 TASK_ID_PATTERN을 workflow regex의 단일 source of truth로 통일",
            "task-N+M 형식이 chain dependency를 표현하므로 workflow 측 인식 실패 시 chain 진행 불가",
        ),
    ),
}


def lookup_proposal(task_id: str) -> MockProposal:
    """주어진 source task_id에 대한 deterministic 제안을 반환한다.

    Args:
        task_id: source task의 식별자 (예: ``task-2485``).

    Returns:
        해당 task에 대해 정해진 :class:`MockProposal`.

    Raises:
        KeyError: 매핑 테이블에 정의되지 않은 task_id.
    """
    if task_id not in _MOCK_MAPPINGS:
        raise KeyError(
            f"mock_ai_adapter: '{task_id}'에 대한 deterministic 매핑이 없습니다. "
            f"등록된 키: {sorted(_MOCK_MAPPINGS)}"
        )
    return _MOCK_MAPPINGS[task_id]


def supported_task_ids() -> tuple[str, ...]:
    """매핑 테이블에 등록된 task_id 목록을 정렬하여 반환한다."""
    return tuple(sorted(_MOCK_MAPPINGS))
