# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
"""Tests for the CI-side HITL Slack message builder."""

from __future__ import annotations

import json

import pytest

from airbyte_ops_mcp.human_in_the_loop_ci import _build_slack_blocks

SESSION_URL = "https://app.devin.ai/sessions/abc123def456"
DETAIL_URL = "https://github.com/airbytehq/airbyte/pull/123"


def _find_actions_block(blocks: list[dict]) -> dict | None:
    """Return the first 'actions' block, or None."""
    for block in blocks:
        if block.get("type") == "actions":
            return block
    return None


def _get_action_ids(actions_block: dict) -> list[str]:
    """Extract action_ids from an actions block."""
    return [e.get("action_id", "") for e in actions_block.get("elements", [])]


def _get_element_by_action_id(actions_block: dict, action_id: str) -> dict | None:
    """Get a specific element from an actions block by action_id."""
    for element in actions_block.get("elements", []):
        if element.get("action_id") == action_id:
            return element
    return None


@pytest.mark.unit
@pytest.mark.parametrize(
    "approval_requested, expected_present, expected_absent",
    [
        pytest.param(
            True,
            ["approve_request", "reject_request"],
            [],
            id="approval_requested_true",
        ),
        pytest.param(
            False,
            [],
            ["approve_request", "reject_request"],
            id="approval_requested_false",
        ),
    ],
)
def test_approval_buttons_presence(
    approval_requested: bool,
    expected_present: list[str],
    expected_absent: list[str],
) -> None:
    """Approve and Reject buttons appear only when approval_requested=True."""
    blocks = _build_slack_blocks(
        target_person="aaronsteers",
        target_slack_id="U12345",
        cc_mentions=[],
        message="Test message.",
        agent_session_url=SESSION_URL,
        pr_url=None,
        issue_url=None,
        additional_actions=None,
        approval_requested=approval_requested,
    )
    actions_block = _find_actions_block(blocks)
    assert actions_block is not None
    action_ids = _get_action_ids(actions_block)
    for aid in expected_present:
        assert aid in action_ids
    for aid in expected_absent:
        assert aid not in action_ids


@pytest.mark.unit
@pytest.mark.parametrize(
    "action_id, expected_style",
    [
        pytest.param("approve_request", "primary", id="approve_is_primary"),
        pytest.param("reject_request", "danger", id="reject_is_danger"),
    ],
)
def test_approval_button_styles(action_id: str, expected_style: str) -> None:
    """Approve uses 'primary' style; Reject uses 'danger' style."""
    blocks = _build_slack_blocks(
        target_person="aaronsteers",
        target_slack_id="U12345",
        cc_mentions=[],
        message="Approve this.",
        agent_session_url=SESSION_URL,
        pr_url=None,
        issue_url=None,
        additional_actions=None,
        approval_requested=True,
    )
    actions_block = _find_actions_block(blocks)
    assert actions_block is not None
    btn = _get_element_by_action_id(actions_block, action_id)
    assert btn is not None
    assert btn.get("style") == expected_style


@pytest.mark.unit
def test_reject_button_carries_session_url() -> None:
    """The Reject button value should contain the session URL (same as Approve)."""
    blocks = _build_slack_blocks(
        target_person="aaronsteers",
        target_slack_id="U12345",
        cc_mentions=[],
        message="Approve this.",
        agent_session_url=SESSION_URL,
        pr_url=None,
        issue_url=None,
        additional_actions=None,
        approval_requested=True,
    )
    actions_block = _find_actions_block(blocks)
    assert actions_block is not None
    reject_btn = _get_element_by_action_id(actions_block, "reject_request")
    assert reject_btn is not None
    value = json.loads(reject_btn["value"])
    assert value["session_url"] == SESSION_URL


@pytest.mark.unit
@pytest.mark.parametrize(
    "action_id, expected_title, expected_confirm_text",
    [
        pytest.param(
            "approve_request",
            "Confirm Approval",
            "Yes, Approve",
            id="approve_confirm_dialog",
        ),
        pytest.param(
            "reject_request",
            "Confirm Rejection",
            "Yes, Reject",
            id="reject_confirm_dialog",
        ),
    ],
)
def test_confirmation_dialogs(
    action_id: str, expected_title: str, expected_confirm_text: str
) -> None:
    """Both buttons get confirmation dialogs when summary is provided."""
    blocks = _build_slack_blocks(
        target_person="aaronsteers",
        target_slack_id="U12345",
        cc_mentions=[],
        message="Approve this.",
        agent_session_url=SESSION_URL,
        pr_url=None,
        issue_url=None,
        additional_actions=None,
        approval_requested=True,
        approval_request_summary="Deploy v2.0 to prod",
    )
    actions_block = _find_actions_block(blocks)
    assert actions_block is not None
    btn = _get_element_by_action_id(actions_block, action_id)
    assert btn is not None
    assert "confirm" in btn
    assert btn["confirm"]["title"]["text"] == expected_title
    assert btn["confirm"]["confirm"]["text"] == expected_confirm_text


@pytest.mark.unit
@pytest.mark.parametrize(
    "detail_url, expect_button",
    [
        pytest.param(DETAIL_URL, True, id="detail_url_provided"),
        pytest.param(None, False, id="detail_url_absent"),
    ],
)
def test_view_details_button_presence(
    detail_url: str | None, expect_button: bool
) -> None:
    """View Details button appears only when approval_request_detail_url is set."""
    blocks = _build_slack_blocks(
        target_person="aaronsteers",
        target_slack_id="U12345",
        cc_mentions=[],
        message="Approve this.",
        agent_session_url=SESSION_URL,
        pr_url=None,
        issue_url=None,
        additional_actions=None,
        approval_requested=True,
        approval_request_detail_url=detail_url,
    )
    actions_block = _find_actions_block(blocks)
    assert actions_block is not None
    action_ids = _get_action_ids(actions_block)
    if expect_button:
        assert "view_approval_details" in action_ids
        btn = _get_element_by_action_id(actions_block, "view_approval_details")
        assert btn is not None
        assert btn["url"] == detail_url
        assert btn["text"]["text"] == "View Details"
    else:
        assert "view_approval_details" not in action_ids


@pytest.mark.unit
def test_view_details_without_approval() -> None:
    """View Details button works even without approval_requested=True."""
    blocks = _build_slack_blocks(
        target_person="aaronsteers",
        target_slack_id="U12345",
        cc_mentions=[],
        message="FYI.",
        agent_session_url=SESSION_URL,
        pr_url=None,
        issue_url=None,
        additional_actions=None,
        approval_requested=False,
        approval_request_detail_url=DETAIL_URL,
    )
    actions_block = _find_actions_block(blocks)
    assert actions_block is not None
    assert "view_approval_details" in _get_action_ids(actions_block)


@pytest.mark.unit
def test_approval_buttons_appear_first() -> None:
    """Approve and Reject buttons are the first two buttons when approval_requested=True."""
    blocks = _build_slack_blocks(
        target_person="aaronsteers",
        target_slack_id="U12345",
        cc_mentions=[],
        message="Approve this.",
        agent_session_url=SESSION_URL,
        pr_url="https://github.com/airbytehq/repo/pull/99",
        issue_url="https://github.com/airbytehq/repo/issues/42",
        additional_actions={"Run CI": "https://github.com/actions/run/1"},
        approval_requested=True,
        approval_request_detail_url=DETAIL_URL,
    )
    actions_block = _find_actions_block(blocks)
    assert actions_block is not None
    action_ids = _get_action_ids(actions_block)
    # Approve and Reject must be the first two buttons, View Details third
    assert action_ids[0] == "approve_request"
    assert action_ids[1] == "reject_request"
    assert action_ids[2] == "view_approval_details"
    # Other buttons follow after
    assert "view_pr" in action_ids
    assert "view_issue" in action_ids
    assert "view_session" in action_ids
    # View Details immediately follows Reject, before any link buttons
    details_idx = action_ids.index("view_approval_details")
    for link_id in ["view_pr", "view_issue", "view_session"]:
        assert action_ids.index(link_id) > details_idx, (
            f"{link_id} (index {action_ids.index(link_id)}) should come after "
            f"view_approval_details (index {details_idx})"
        )


@pytest.mark.unit
@pytest.mark.parametrize(
    "approval_requested, first_button_should_be_primary",
    [
        pytest.param(False, True, id="no_approval_first_btn_primary"),
        pytest.param(True, False, id="with_approval_pr_btn_not_primary"),
    ],
)
def test_primary_style_on_first_button(
    approval_requested: bool, first_button_should_be_primary: bool
) -> None:
    """Primary style is set on first button only when no approval buttons exist."""
    blocks = _build_slack_blocks(
        target_person="aaronsteers",
        target_slack_id="U12345",
        cc_mentions=[],
        message="Test.",
        agent_session_url=SESSION_URL,
        pr_url="https://github.com/airbytehq/repo/pull/1",
        issue_url=None,
        additional_actions=None,
        approval_requested=approval_requested,
    )
    actions_block = _find_actions_block(blocks)
    assert actions_block is not None
    pr_btn = _get_element_by_action_id(actions_block, "view_pr")
    assert pr_btn is not None
    if first_button_should_be_primary:
        assert pr_btn.get("style") == "primary"
    else:
        assert pr_btn.get("style") != "primary"
