# -*- coding: utf-8 -*-
"""tests.regression.test_gemini_evidence_freshness_checker — task-2641 Track A.

회장 verbatim §10 (2026-05-23) 1:1 정합 — PR HEAD SHA vs Gemini review commit_id
정합 검증 helper 회귀.

본 회귀는 Layer A / NO-CRON: subprocess / cokacdir / merge / cron 호출 0.
github_api 는 callable inject 로 mock — live gh 호출 0.
"""
from __future__ import annotations

import pytest

from anu_v2.gemini_evidence_freshness_checker import (
    GEMINI_BOT_LOGIN,
    RESULT_FRESH,
    RESULT_NO_REVIEW,
    RESULT_STALE,
    FreshnessCheckerError,
    check_gemini_evidence_fresh,
    is_gemini_trigger_comment,
)


HEAD_A = "0" * 40
HEAD_B = "0" * 39 + "1"
HEAD_C = "abc" + "0" * 37


def _mock_api(payload):
    captured = []

    def _api(method, path):
        captured.append((method, path))
        return payload

    _api.captured = captured  # type: ignore[attr-defined]
    return _api


# ─── check_gemini_evidence_fresh ──────────────────────────────────────────────


def test_fresh_when_gemini_latest_commit_matches_head():
    payload = [
        {"user": {"login": GEMINI_BOT_LOGIN}, "commit_id": HEAD_A},
    ]
    result = check_gemini_evidence_fresh(
        pr_number=143,
        current_head_sha=HEAD_A,
        github_api=_mock_api(payload),
        owner="o",
        repo="r",
    )
    assert result.status == RESULT_FRESH
    assert result.gemini_commit_id_observed == HEAD_A
    assert result.reviews_inspected == 1


def test_stale_when_gemini_latest_commit_differs():
    payload = [
        {"user": {"login": GEMINI_BOT_LOGIN}, "commit_id": HEAD_B},
    ]
    result = check_gemini_evidence_fresh(
        pr_number=143,
        current_head_sha=HEAD_A,
        github_api=_mock_api(payload),
        owner="o",
        repo="r",
    )
    assert result.status == RESULT_STALE
    assert result.gemini_commit_id_observed == HEAD_B


def test_no_review_when_no_gemini_bot_review_present():
    payload = [
        {"user": {"login": "human-reviewer"}, "commit_id": HEAD_A},
        {"user": {"login": "Jeon-Jonghyuk"}, "commit_id": HEAD_A},
    ]
    result = check_gemini_evidence_fresh(
        pr_number=143,
        current_head_sha=HEAD_A,
        github_api=_mock_api(payload),
        owner="o",
        repo="r",
    )
    assert result.status == RESULT_NO_REVIEW
    assert result.gemini_commit_id_observed is None
    assert result.reviews_inspected == 2


def test_no_review_when_payload_empty_list():
    result = check_gemini_evidence_fresh(
        pr_number=143,
        current_head_sha=HEAD_A,
        github_api=_mock_api([]),
        owner="o",
        repo="r",
    )
    assert result.status == RESULT_NO_REVIEW
    assert result.gemini_commit_id_observed is None


def test_no_review_when_github_api_returns_none():
    result = check_gemini_evidence_fresh(
        pr_number=143,
        current_head_sha=HEAD_A,
        github_api=_mock_api(None),
        owner="o",
        repo="r",
    )
    assert result.status == RESULT_NO_REVIEW


def test_latest_gemini_review_wins_when_multiple_present():
    """list end = 최신 (GitHub API order). spec §3.1."""
    payload = [
        {"user": {"login": GEMINI_BOT_LOGIN}, "commit_id": HEAD_B},
        {"user": {"login": GEMINI_BOT_LOGIN}, "commit_id": HEAD_A},
    ]
    result = check_gemini_evidence_fresh(
        pr_number=143,
        current_head_sha=HEAD_A,
        github_api=_mock_api(payload),
        owner="o",
        repo="r",
    )
    assert result.status == RESULT_FRESH
    assert result.gemini_commit_id_observed == HEAD_A


def test_head_sha_normalised_to_lower():
    upper_head = HEAD_C.upper()
    payload = [{"user": {"login": GEMINI_BOT_LOGIN}, "commit_id": HEAD_C}]
    result = check_gemini_evidence_fresh(
        pr_number=143,
        current_head_sha=upper_head,
        github_api=_mock_api(payload),
        owner="o",
        repo="r",
    )
    assert result.current_head_sha == HEAD_C
    assert result.status == RESULT_FRESH


def test_invalid_head_sha_raises():
    with pytest.raises(FreshnessCheckerError):
        check_gemini_evidence_fresh(
            pr_number=143,
            current_head_sha="not-a-sha",
            github_api=_mock_api([]),
            owner="o",
            repo="r",
        )


def test_invalid_pr_number_raises():
    with pytest.raises(FreshnessCheckerError):
        check_gemini_evidence_fresh(
            pr_number=0,
            current_head_sha=HEAD_A,
            github_api=_mock_api([]),
            owner="o",
            repo="r",
        )


def test_bool_pr_number_rejected():
    with pytest.raises(FreshnessCheckerError):
        check_gemini_evidence_fresh(
            pr_number=True,  # type: ignore[arg-type]
            current_head_sha=HEAD_A,
            github_api=_mock_api([]),
            owner="o",
            repo="r",
        )


def test_invalid_commit_id_format_treated_as_no_review():
    """commit_id 가 hex 40-char 가 아니면 skip → NO_REVIEW."""
    payload = [
        {"user": {"login": GEMINI_BOT_LOGIN}, "commit_id": "short"},
        {"user": {"login": GEMINI_BOT_LOGIN}, "commit_id": "g" * 40},
    ]
    result = check_gemini_evidence_fresh(
        pr_number=143,
        current_head_sha=HEAD_A,
        github_api=_mock_api(payload),
        owner="o",
        repo="r",
    )
    assert result.status == RESULT_NO_REVIEW


def test_github_api_exception_wrapped_in_checker_error():
    def _broken_api(method, path):
        raise RuntimeError("network down")

    with pytest.raises(FreshnessCheckerError):
        check_gemini_evidence_fresh(
            pr_number=143,
            current_head_sha=HEAD_A,
            github_api=_broken_api,
            owner="o",
            repo="r",
        )


def test_path_template_uses_owner_repo_and_pr():
    api = _mock_api([])
    check_gemini_evidence_fresh(
        pr_number=999,
        current_head_sha=HEAD_A,
        github_api=api,
        owner="ownerX",
        repo="repoY",
    )
    assert api.captured == [("GET", "/repos/ownerX/repoY/pulls/999/reviews")]  # type: ignore[attr-defined]


# ─── is_gemini_trigger_comment (회장 verbatim §1/§2/§3) ────────────────────────


def test_is_gemini_trigger_comment_true_for_issue_comment_exact_body():
    comment = {
        "kind": "issue_comment",
        "body": "/gemini review",
        "user": {"login": "Jeon-Jonghyuk"},
    }
    assert is_gemini_trigger_comment(comment) is True


def test_is_gemini_trigger_comment_true_with_whitespace_padding():
    comment = {
        "kind": "issue_comment",
        "body": "  /gemini review  \n",
        "user": {"login": "Jeon-Jonghyuk"},
    }
    assert is_gemini_trigger_comment(comment) is True


def test_is_gemini_trigger_comment_false_for_pr_review_empty_body():
    """task-2640 사고 박제: 회장 review id=4350214991 state=COMMENTED, body=""."""
    comment = {
        "id": 4350214991,
        "kind": "review",
        "state": "COMMENTED",
        "body": "",
        "user": {"login": "Jeon-Jonghyuk"},
    }
    assert is_gemini_trigger_comment(comment) is False


def test_is_gemini_trigger_comment_false_for_review_comment_kind():
    comment = {
        "kind": "review_comment",
        "body": "/gemini review",
        "user": {"login": "Jeon-Jonghyuk"},
    }
    assert is_gemini_trigger_comment(comment) is False


def test_is_gemini_trigger_comment_false_for_other_body_text():
    comment = {
        "kind": "issue_comment",
        "body": "/gemini review please",
        "user": {"login": "Jeon-Jonghyuk"},
    }
    assert is_gemini_trigger_comment(comment) is False


def test_is_gemini_trigger_comment_false_for_non_dict_input():
    assert is_gemini_trigger_comment(None) is False
    assert is_gemini_trigger_comment("not-a-dict") is False
    assert is_gemini_trigger_comment(42) is False
