"""Auto-discovered round-trip tests for all block types.

Tests are parametrized over BLOCK_TYPE_MAP, so every registered block type
is automatically tested. Adding a new block to BLOCK_TYPE_MAP without a
corresponding entry in BLOCK_FIXTURES will cause test_all_block_types_have_fixtures
to fail, preventing untested blocks from shipping.
"""

import pytest

from cxblueprint.flow_builder import BLOCK_TYPE_MAP
from cxblueprint.blocks.types import LexV2Bot, ViewResource, Media


# Minimal valid constructor kwargs for each block type.
# Keys must exactly match BLOCK_TYPE_MAP keys.
BLOCK_FIXTURES = {
    # Participant Actions
    "MessageParticipant": {"text": "Hello, welcome."},
    "MessageParticipantIteratively": {
        "messages": [{"Text": "Message one"}, {"Text": "Message two"}],
        "interrupt_frequency_seconds": "30",
    },
    "GetParticipantInput": {"text": "Press 1 or 2", "input_time_limit_seconds": 5},
    "DisconnectParticipant": {},
    "ConnectParticipantWithLexBot": {
        "text": "How can I help?",
        "lex_v2_bot": LexV2Bot(
            alias_arn="arn:aws:lex:us-east-1:123456789:bot-alias/TESTBOT/TESTALIAS"
        ),
    },
    "ShowView": {
        "view_resource": ViewResource(id="view-1", version="1"),
        "invocation_time_limit_seconds": 300,
    },
    # Flow Control Actions
    "EndFlowExecution": {},
    "TransferToFlow": {"contact_flow_id": "arn:aws:connect:us-east-1:123:flow/test"},
    "Compare": {"comparison_value": "$.Attributes.tier"},
    "CheckHoursOfOperation": {},
    "CheckOutboundCallStatus": {},
    "Wait": {"time_limit_seconds": 30},
    "DistributeByPercentage": {"percentages": [50, 50]},
    "CheckMetricData": {},
    "CheckVoiceId": {"check_voice_id_option": "voiceAuthentication"},
    "CreateCase": {"case_template_id": "template-123"},
    "EndFlowModuleExecution": {},
    "GetCase": {"customer_id": "customer-123"},
    "GetMetricData": {},
    "InvokeFlowModule": {"flow_module_id": "module-123"},
    "Loop": {"loop_count": 5},
    "SetVoiceId": {},
    "StartVoiceIdStream": {},
    "UpdateCase": {"case_id": "case-123"},
    "UpdateFlowAttributes": {"flow_attributes": {"temp_key": "temp_value"}},
    "UpdateFlowLoggingBehavior": {"flow_logging_behavior": "Enabled"},
    "UpdateRoutingCriteria": {"routing_criteria": {"Steps": []}},
    # Interactions
    "InvokeLambdaFunction": {
        "lambda_function_arn": "arn:aws:lambda:us-east-1:123:function:test",
        "invocation_time_limit_seconds": 8,
    },
    "CreateCallbackContact": {},
    "AssociateContactToCustomerProfile": {},
    "CreateCustomerProfile": {},
    "GetCalculatedAttributesForCustomerProfile": {
        "profile_id": "profile-123",
        "calculated_attribute_names": ["TotalOrders", "LifetimeValue"],
    },
    "GetCustomerProfile": {},
    "GetCustomerProfileObject": {"profile_id": "profile-123", "object_type": "Order"},
    "UpdateCustomerProfile": {},
    # Contact Actions
    "UpdateContactAttributes": {"attributes": {"tier": "premium"}},
    "UpdateContactRecordingBehavior": {
        "recording_behavior": {"RecordedParticipants": ["Agent"]},
    },
    "UpdateContactTargetQueue": {},
    "TransferContactToQueue": {},
    "UpdateContactCallbackNumber": {},
    "UpdateContactEventHooks": {},
    "UpdateContactMediaProcessing": {
        "chat_processor": {"ProcessingEnabled": "True", "LambdaProcessorARN": "arn:aws:lambda:us-east-1:123:function:chat"},
    },
    "UpdateContactRecordingAndAnalyticsBehavior": {
        "voice_behavior": {"VoiceRecordingBehavior": {"RecordedParticipants": ["Agent", "Customer"]}},
    },
    "UpdateContactRoutingBehavior": {},
    "CreateTask": {},
    "CreateWisdomSession": {"wisdom_assistant_arn": "arn:aws:wisdom:us-east-1:123:assistant/test"},
    "CompleteOutboundCall": {"caller_id": "+12065551234"},
    "DequeueContactAndTransferToQueue": {"queue_id": "test-queue-123"},
    "ResumeContact": {},
    "StartOutboundChatContact": {},
    "TagContact": {"tags": {"priority": "high", "customer_type": "vip"}},
    "TransferContactToAgent": {},
    "UnTagContact": {"tag_keys": ["priority", "customer_type"]},
    "UpdateContactData": {"name": "Test Contact", "target_contact": "Current"},
    "UpdateContactMediaStreamingBehavior": {
        "media_streaming_state": "Enabled",
        "media_stream_type": "Audio",
    },
    "UpdateContactTextToSpeechVoice": {
        "text_to_speech_voice": "Joanna",
        "text_to_speech_engine": "neural",
    },
    "UpdatePreviousContactParticipantState": {
        "previous_contact_participant_state": "AgentOnHold",
    },
}


def test_all_block_types_have_fixtures():
    """Fail if a block type exists in BLOCK_TYPE_MAP without test fixture data.

    This is the guard test: adding a new block to BLOCK_TYPE_MAP without
    adding an entry here will cause CI to fail.
    """
    registered = set(BLOCK_TYPE_MAP.keys())
    fixtured = set(BLOCK_FIXTURES.keys())
    missing = registered - fixtured
    extra = fixtured - registered
    assert missing == set(), (
        f"Block types in BLOCK_TYPE_MAP without test fixtures: {missing}. "
        f"Add entries to BLOCK_FIXTURES in test_block_roundtrip.py."
    )
    assert extra == set(), (
        f"Fixture entries not in BLOCK_TYPE_MAP: {extra}. "
        f"Remove stale entries from BLOCK_FIXTURES."
    )


@pytest.mark.parametrize("block_type", sorted(BLOCK_TYPE_MAP.keys()))
def test_block_type_attribute(block_type):
    """Each block's __post_init__ sets self.type to the correct AWS type string."""
    block_class = BLOCK_TYPE_MAP[block_type]
    kwargs = BLOCK_FIXTURES[block_type]
    block = block_class(identifier="test-type-check", **kwargs)
    assert block.type == block_type


@pytest.mark.parametrize("block_type", sorted(BLOCK_TYPE_MAP.keys()))
def test_block_has_required_keys(block_type):
    """to_dict() output contains all required AWS action keys."""
    block_class = BLOCK_TYPE_MAP[block_type]
    kwargs = BLOCK_FIXTURES[block_type]
    block = block_class(identifier="test-keys-check", **kwargs)
    serialized = block.to_dict()
    assert "Identifier" in serialized
    assert "Type" in serialized
    assert "Parameters" in serialized
    assert "Transitions" in serialized
    assert serialized["Identifier"] == "test-keys-check"
    assert serialized["Type"] == block_type


@pytest.mark.parametrize("block_type", sorted(BLOCK_TYPE_MAP.keys()))
def test_block_roundtrip(block_type):
    """Every block survives to_dict -> from_dict -> to_dict round-trip."""
    block_class = BLOCK_TYPE_MAP[block_type]
    kwargs = BLOCK_FIXTURES[block_type]

    # Construct and serialize
    block = block_class(identifier="test-roundtrip-001", **kwargs)
    serialized = block.to_dict()

    # Deserialize
    restored = block_class.from_dict(serialized)
    assert restored.identifier == "test-roundtrip-001"
    assert restored.type == block_type

    # Re-serialize and compare
    re_serialized = restored.to_dict()
    assert re_serialized["Parameters"] == serialized["Parameters"]
    assert re_serialized["Type"] == serialized["Type"]
    assert re_serialized["Identifier"] == serialized["Identifier"]


@pytest.mark.parametrize("block_type", sorted(BLOCK_TYPE_MAP.keys()))
def test_block_repr_doesnt_crash(block_type):
    """repr() should not raise for any block type."""
    block_class = BLOCK_TYPE_MAP[block_type]
    kwargs = BLOCK_FIXTURES[block_type]
    block = block_class(identifier="test-repr", **kwargs)
    result = repr(block)
    assert isinstance(result, str)
    assert len(result) > 0
