"""Merge-ready classifier state constants (single source).

회장 확정 schema 의 enum/상수 단일소스:
- verdict enum 4값: PASS / HOLD / CHAIR_REQUIRED / UNKNOWN
- verdict precedence (상태기계): UNKNOWN > CHAIR_REQUIRED > HOLD > PASS
- credential 3계층(+NONE): NONE / EXISTING_SYSTEM_IDENTIFIER / NET_NEW / BLOCKING_SECRET
- Critical7 1:1 (회장 7종)
- 8 회장 보고 트리거(+ lifecycle incident, + auto-remediation loop boundary)
- Gemini auto-remediation doctrine 의 auto_remediable enum
- 자동 머지 10조건 키

단일소스 스펙: memory/specs/system_merge_ready_executor_spec_260522.md
★ evidence-only decoupled: anu_v3/런타임 import 0 · merge/write/live-git/subprocess I/O 0.
   이 모듈은 상수/순수 헬퍼만 보유한다.
"""

# ─────────────────────────────────────────────────────────────────────────────
# verdict enum (4값, 회장 확정 — UNKNOWN 포함)
# ─────────────────────────────────────────────────────────────────────────────
PASS = "PASS"
HOLD = "HOLD"
CHAIR_REQUIRED = "CHAIR_REQUIRED"
UNKNOWN = "UNKNOWN"

VERDICTS = frozenset({PASS, HOLD, CHAIR_REQUIRED, UNKNOWN})

# verdict precedence (상태기계): UNKNOWN > CHAIR_REQUIRED > HOLD > PASS
# index 가 낮을수록 우선순위 높음.
VERDICT_PRECEDENCE = [UNKNOWN, CHAIR_REQUIRED, HOLD, PASS]
VERDICT_RANK = {v: i for i, v in enumerate(VERDICT_PRECEDENCE)}


def verdict_precedes(a, b):
    """a 가 b 보다 (또는 동급으로) 우선하면 True. precedence 단언용 헬퍼."""
    return VERDICT_RANK[a] <= VERDICT_RANK[b]


# ─────────────────────────────────────────────────────────────────────────────
# credential 3계층 (+NONE) — feedback-credential-scan-3tier-doctrine-260522
# ─────────────────────────────────────────────────────────────────────────────
CRED_NONE = "NONE"
CRED_EXISTING_SYSTEM_IDENTIFIER = "EXISTING_SYSTEM_IDENTIFIER"
CRED_NET_NEW = "NET_NEW"
CRED_BLOCKING_SECRET = "BLOCKING_SECRET"

CREDENTIAL_TIERS = frozenset({
    CRED_NONE, CRED_EXISTING_SYSTEM_IDENTIFIER, CRED_NET_NEW, CRED_BLOCKING_SECRET,
})
# tier precedence (위험도 높은 순). 동시 신호 시 가장 위험한 tier 채택.
CREDENTIAL_TIER_PRECEDENCE = [
    CRED_BLOCKING_SECRET, CRED_NET_NEW, CRED_EXISTING_SYSTEM_IDENTIFIER, CRED_NONE,
]
# CHAIR 로 escalate 되는 tier (EXISTING/NONE 은 fail 아님 · PASS 영향 0)
CREDENTIAL_CHAIR_TIERS = frozenset({CRED_BLOCKING_SECRET, CRED_NET_NEW})

# ─────────────────────────────────────────────────────────────────────────────
# evidence_completeness
# ─────────────────────────────────────────────────────────────────────────────
COMPLETE = "COMPLETE"
PARTIAL = "PARTIAL"
MISSING = "MISSING"

# ─────────────────────────────────────────────────────────────────────────────
# Critical7 (회장 7종 1:1 — spec §4)
# ─────────────────────────────────────────────────────────────────────────────
C7_FORBIDDEN_PATH = "C7_FORBIDDEN_PATH"                          # #1 forbidden path 침범
C7_OUT_OF_SCOPE_REPLACEMENT_FAIL = "C7_OUT_OF_SCOPE_REPLACEMENT_FAIL"  # #2 effective diff 오염 + replacement PR 실패
C7_SCOPE_EXPANSION = "C7_SCOPE_EXPANSION"                        # #3 Gemini real bug 가 expected_files 밖/scope 확장
C7_OVERRIDE = "C7_OVERRIDE"                                      # #4 admin override / branch protection 우회 필요
C7_DEPENDENCY_CYCLE_OR_SERIAL = "C7_DEPENDENCY_CYCLE_OR_SERIAL"  # #5 dependency cycle / serial_only 충돌
C7_REPLACEMENT_FAIL = "C7_REPLACEMENT_FAIL"                      # #6 replacement PR 자체 실패
C7_POST_MERGE_SMOKE_FAIL = "C7_POST_MERGE_SMOKE_FAIL"           # #7 post-merge smoke 실패

CRITICAL7 = frozenset({
    C7_FORBIDDEN_PATH, C7_OUT_OF_SCOPE_REPLACEMENT_FAIL, C7_SCOPE_EXPANSION,
    C7_OVERRIDE, C7_DEPENDENCY_CYCLE_OR_SERIAL, C7_REPLACEMENT_FAIL,
    C7_POST_MERGE_SMOKE_FAIL,
})
# 결정적 출력 순서 (회장 7종 번호 순)
CRITICAL7_ORDER = [
    C7_FORBIDDEN_PATH,
    C7_OUT_OF_SCOPE_REPLACEMENT_FAIL,
    C7_SCOPE_EXPANSION,
    C7_OVERRIDE,
    C7_DEPENDENCY_CYCLE_OR_SERIAL,
    C7_REPLACEMENT_FAIL,
    C7_POST_MERGE_SMOKE_FAIL,
]

# ─────────────────────────────────────────────────────────────────────────────
# 회장 보고 트리거 (CHAIR_REQUIRED) — spec §3 8 트리거 + lifecycle incident + loop boundary
# ─────────────────────────────────────────────────────────────────────────────
CHAIR_CRITICAL7 = "CRITICAL7"                                    # 1. Critical7 적중
CHAIR_CREDENTIAL_PERMISSION_EXPANSION = "CREDENTIAL_PERMISSION_EXPANSION"  # 2. BLOCKING_SECRET·NET_NEW
CHAIR_OUT_OF_EXPECTED_FILES = "OUT_OF_EXPECTED_FILES_MODIFICATION"        # 3. expected_files 밖 수정 필요
CHAIR_ADMIN_OVERRIDE = "ADMIN_OVERRIDE_REQUIRED"                # 4. admin override 필요
CHAIR_REPLACEMENT_PR_FAILURE = "REPLACEMENT_PR_FAILURE"         # 5. replacement PR failure
CHAIR_POST_MERGE_SMOKE_FAILURE = "POST_MERGE_SMOKE_FAILURE"     # 6. post-merge smoke failure
CHAIR_DEPENDENCY_OR_SERIAL = "DEPENDENCY_CYCLE_OR_SERIAL_COLLISION"  # 7. dependency cycle·serial collision
CHAIR_MERGE_POLICY_CHANGE = "MERGE_POLICY_CHANGE_REQUIRED"      # 8. merge policy 변경 필요
# (+) lifecycle classification==INCIDENT 승격
CHAIR_LIFECYCLE_INCIDENT = "LIFECYCLE_INCIDENT"
# (+) 같은 함수/file-boundary HIGH 반복 — auto-remediation loop 경계 회장 요약보고
CHAIR_AUTO_REMEDIATION_LOOP_BOUNDARY = "AUTO_REMEDIATION_LOOP_BOUNDARY_REVIEW"

CHAIR_TRIGGERS = frozenset({
    CHAIR_CRITICAL7, CHAIR_CREDENTIAL_PERMISSION_EXPANSION, CHAIR_OUT_OF_EXPECTED_FILES,
    CHAIR_ADMIN_OVERRIDE, CHAIR_REPLACEMENT_PR_FAILURE, CHAIR_POST_MERGE_SMOKE_FAILURE,
    CHAIR_DEPENDENCY_OR_SERIAL, CHAIR_MERGE_POLICY_CHANGE, CHAIR_LIFECYCLE_INCIDENT,
    CHAIR_AUTO_REMEDIATION_LOOP_BOUNDARY,
})
# 결정적 출력 순서
CHAIR_TRIGGER_ORDER = [
    CHAIR_CRITICAL7,
    CHAIR_CREDENTIAL_PERMISSION_EXPANSION,
    CHAIR_OUT_OF_EXPECTED_FILES,
    CHAIR_ADMIN_OVERRIDE,
    CHAIR_REPLACEMENT_PR_FAILURE,
    CHAIR_POST_MERGE_SMOKE_FAILURE,
    CHAIR_DEPENDENCY_OR_SERIAL,
    CHAIR_MERGE_POLICY_CHANGE,
    CHAIR_LIFECYCLE_INCIDENT,
    CHAIR_AUTO_REMEDIATION_LOOP_BOUNDARY,
]

# ─────────────────────────────────────────────────────────────────────────────
# auto_remediable (HOLD 중 ANU 자동수렴 가능 항목) — spec §5 Gemini auto-remediation
# ─────────────────────────────────────────────────────────────────────────────
AR_CI_PENDING = "CI_PENDING"
AR_GEMINI_EVIDENCE_STALE = "GEMINI_EVIDENCE_STALE"
AR_GEMINI_MEDIUM_WITHIN_EXPECTED = "GEMINI_MEDIUM_WITHIN_EXPECTED"
AR_GEMINI_NONCRITICAL_HIGH_WITHIN_EXPECTED = "GEMINI_NONCRITICAL_HIGH_WITHIN_EXPECTED"
AR_UNRESOLVED_MEDIUM_THREAD = "UNRESOLVED_MEDIUM_THREAD"
AR_MERGE_STATE_TRANSIENT_BLOCKED = "MERGE_STATE_TRANSIENT_BLOCKED"

AUTO_REMEDIABLE = frozenset({
    AR_CI_PENDING, AR_GEMINI_EVIDENCE_STALE, AR_GEMINI_MEDIUM_WITHIN_EXPECTED,
    AR_GEMINI_NONCRITICAL_HIGH_WITHIN_EXPECTED, AR_UNRESOLVED_MEDIUM_THREAD,
    AR_MERGE_STATE_TRANSIENT_BLOCKED,
})
AUTO_REMEDIABLE_ORDER = [
    AR_CI_PENDING,
    AR_GEMINI_EVIDENCE_STALE,
    AR_GEMINI_MEDIUM_WITHIN_EXPECTED,
    AR_GEMINI_NONCRITICAL_HIGH_WITHIN_EXPECTED,
    AR_UNRESOLVED_MEDIUM_THREAD,
    AR_MERGE_STATE_TRANSIENT_BLOCKED,
]

# Gemini severity 중 expected_files 내부면 자동수렴 후보가 되는 등급
GEMINI_AUTO_REMEDIABLE_SEVERITIES = frozenset({"medium", "low", "style", "quality", "minor"})

# ─────────────────────────────────────────────────────────────────────────────
# 자동 머지 10조건 (결정적 키 순서) — spec §3/§8
# ─────────────────────────────────────────────────────────────────────────────
AUTO_MERGE_10_CONDITION_KEYS = [
    "exact_scope_match",      # 1. exact_match · forbidden 0 · out-of-expected 0
    "ci_all_green",           # 2. CI all pass · pending/fail 0
    "gemini_review_pass",     # 3. gemini gate pass · fresh · high/critical 미해소 0
    "phase3_merge_gate_pass", # 4. phase3 merge gate pass
    "merge_state_clean",      # 5. mergeStateStatus CLEAN
    "mergeable",              # 6. mergeable == MERGEABLE
    "threads_resolved",       # 7. unresolved high/medium thread 0
    "credential_clean",       # 8. credential_tier in {NONE, EXISTING}
    "no_critical7",           # 9. critical7 hit 0
    "lifecycle_normal",       # 10. callback lifecycle classification NORMAL
]

# ─────────────────────────────────────────────────────────────────────────────
# callback lifecycle classification (fields 10~14 입력) — CALLBACK_RUNTIME_ENFORCEMENT
# ─────────────────────────────────────────────────────────────────────────────
LIFECYCLE_NORMAL = "normal"
LIFECYCLE_INCIDENT = "incident"
# classification==INCIDENT 이거나 아래 miss_cause 면 production 신뢰성 훼손 → CHAIR_REQUIRED
LIFECYCLE_INCIDENT_MISS_CAUSES = frozenset({
    "SELF_KEY_FIRED_NON_AUTHORITATIVE",
    "CALLBACK_CONTRACT_VIOLATION",
    "CALLBACK_DELIVERY_GAP",
})

# ─────────────────────────────────────────────────────────────────────────────
# ci / merge-state 상태 정규화 집합
# ─────────────────────────────────────────────────────────────────────────────
CI_PENDING_STATES = frozenset({"pending", "in_progress", "queued", "expected", "running", "waiting"})
CI_PASS_STATES = frozenset({"pass", "success", "completed", "neutral", "skipped", "ok"})
CI_FAIL_STATES = frozenset({"fail", "failure", "error", "cancelled", "timed_out", "action_required"})

MERGE_STATE_CLEAN = "CLEAN"
MERGE_STATE_BLOCKED = "BLOCKED"
MERGE_STATE_UNKNOWN = "UNKNOWN"
MERGEABLE_OK = "MERGEABLE"
MERGEABLE_CONFLICTING = "CONFLICTING"

# ─────────────────────────────────────────────────────────────────────────────
# ANU owner key 식별 (라우팅 식별자 · 비밀 아님 · spec §6 / callback schema 와 정합)
# c119085a = ANU(authoritative). 그 외(예: 1e41a232) = executor self-key(non-authoritative).
# ─────────────────────────────────────────────────────────────────────────────
DEFAULT_ANU_KEYS = frozenset({"c119085addb0f8b7"})


def is_anu_owner_key(key, anu_keys=DEFAULT_ANU_KEYS):
    """owner key 가 ANU authoritative key 인지 판정. None/미상 → False."""
    return bool(key) and key in anu_keys


# ─────────────────────────────────────────────────────────────────────────────
# evidence 소스 단일소스 (spec §2 A~G)
# ─────────────────────────────────────────────────────────────────────────────
CANONICAL_EVIDENCE_SOURCES = [
    "scope", "credential", "gates", "gemini", "critical7", "merge_mechanics", "lifecycle",
]
# 핵심 gate evidence: scope/gates 결핍 시 merge 판정 자체 불가 → MISSING → UNKNOWN
CORE_EVIDENCE_SOURCES = frozenset({"scope", "gates"})

# ─────────────────────────────────────────────────────────────────────────────
# classifier id / next_action 메시지 / 기타 제약
# ─────────────────────────────────────────────────────────────────────────────
CLASSIFIED_BY = "merge-ready-classifier"

NEXT_ACTION = {
    UNKNOWN: "re-collect core gate evidence (scope/gates); merge/escalate 판정 보류 (추정 0)",
    CHAIR_REQUIRED: "escalate 회장 보고 (chair_triggers); auto-merge 차단 · executor 진입 불가",
    HOLD: "ANU 자동수렴/재평가 루프 (회장 보고 X · merge X) → 재분류",
    PASS: "auto-merge executor 후보 (회장 executor 활성화 승인 전까지 advisory/read-only)",
}

# callback envelope UTF-8 byte 상한 (finalize envelope-only 매직넘버 단일소스화).
# 근거(Gemini medium): cokacdir 양방향 cron prompt 한계 4096 bytes 미만으로, JSON-escape·
# 한글 multibyte·메타데이터 오버헤드 여유분을 둔 안전 상한(≈4096-196). 한계 초과 시 봇 전달
# 채널에서 silent drop(feedback_cron_prompt_4096_limit). 환경 변화 시 이 값 재조정.
MAX_ENVELOPE_BYTES = 3900
