"""Targeted tests for blocks with custom methods or complex serialization.

These go beyond the round-trip tests to exercise block-specific behavior:
on_intent(), branch(), on_action(), and custom parameter handling.
"""

import pytest

from cxblueprint.blocks.base import FlowBlock
from cxblueprint.blocks.participant_actions import (
    ConnectParticipantWithLexBot,
    MessageParticipantIteratively,
    ShowView,
)
from cxblueprint.blocks.flow_control_actions import (
    DistributeByPercentage,
    CheckMetricData,
)
from cxblueprint.blocks.interactions import CreateCallbackContact
from cxblueprint.blocks.contact_actions import CreateTask
from cxblueprint.blocks.types import LexV2Bot, LexBot, ViewResource, Media


# --- ConnectParticipantWithLexBot ---


class TestConnectParticipantWithLexBot:
    def test_on_intent_adds_condition(self):
        target = FlowBlock(identifier="target-block")
        bot = ConnectParticipantWithLexBot(
            text="How can I help?",
            lex_v2_bot=LexV2Bot(alias_arn="arn:aws:lex:us-east-1:123:bot-alias/B/A"),
        )
        bot.on_intent("OrderStatus", target)

        conditions = bot.transitions["Conditions"]
        assert len(conditions) == 1
        assert conditions[0]["NextAction"] == "target-block"
        assert conditions[0]["Condition"]["Operator"] == "Equals"
        assert conditions[0]["Condition"]["Operands"] == ["OrderStatus"]

    def test_on_intent_chaining(self):
        t1 = FlowBlock(identifier="t1")
        t2 = FlowBlock(identifier="t2")
        bot = ConnectParticipantWithLexBot(
            text="Help",
            lex_v2_bot=LexV2Bot(alias_arn="arn:aws:lex:us-east-1:123:bot-alias/B/A"),
        )
        bot.on_intent("Order", t1).on_intent("Return", t2)

        assert len(bot.transitions["Conditions"]) == 2
        assert bot.transitions["Conditions"][0]["Condition"]["Operands"] == ["Order"]
        assert bot.transitions["Conditions"][1]["Condition"]["Operands"] == ["Return"]

    def test_lex_v2_bot_serialization(self):
        bot = ConnectParticipantWithLexBot(
            text="Help",
            lex_v2_bot=LexV2Bot(alias_arn="arn:aws:lex:us-east-1:123:bot-alias/B/A"),
        )
        params = bot.to_dict()["Parameters"]
        assert "LexV2Bot" in params
        assert params["LexV2Bot"]["AliasArn"] == "arn:aws:lex:us-east-1:123:bot-alias/B/A"

    def test_legacy_lex_bot_serialization(self):
        bot = ConnectParticipantWithLexBot(
            text="Help",
            lex_bot=LexBot(name="MyBot", region="us-east-1", alias="prod"),
        )
        params = bot.to_dict()["Parameters"]
        assert "LexBot" in params
        assert params["LexBot"]["Name"] == "MyBot"
        assert params["LexBot"]["Region"] == "us-east-1"
        assert params["LexBot"]["Alias"] == "prod"

    def test_from_dict_restores_lex_v2_bot(self):
        original = ConnectParticipantWithLexBot(
            text="Help",
            lex_v2_bot=LexV2Bot(alias_arn="arn:aws:lex:us-east-1:123:bot-alias/B/A"),
        )
        restored = ConnectParticipantWithLexBot.from_dict(original.to_dict())
        assert restored.lex_v2_bot is not None
        assert restored.lex_v2_bot.alias_arn == "arn:aws:lex:us-east-1:123:bot-alias/B/A"
        assert restored.text == "Help"

    def test_media_prompt_serialization(self):
        bot = ConnectParticipantWithLexBot(
            media=Media(uri="s3://bucket/audio.wav"),
            lex_v2_bot=LexV2Bot(alias_arn="arn:aws:lex:us-east-1:123:bot-alias/B/A"),
        )
        params = bot.to_dict()["Parameters"]
        assert "Media" in params
        assert params["Media"]["Uri"] == "s3://bucket/audio.wav"


# --- DistributeByPercentage ---


class TestDistributeByPercentage:
    def test_branch_two_way_split(self):
        t1 = FlowBlock(identifier="path-a")
        t2 = FlowBlock(identifier="path-b")
        split = DistributeByPercentage(percentages=[50, 50])
        split.branch(0, t1).branch(1, t2)

        conditions = split.transitions["Conditions"]
        assert len(conditions) == 2
        # 50% cumulative + 1 = 51
        assert conditions[0]["Condition"]["Operands"] == ["51"]
        assert conditions[0]["NextAction"] == "path-a"
        # 100% cumulative + 1 = 101
        assert conditions[1]["Condition"]["Operands"] == ["101"]
        assert conditions[1]["NextAction"] == "path-b"

    def test_branch_three_way_split(self):
        targets = [FlowBlock(identifier=f"path-{i}") for i in range(3)]
        split = DistributeByPercentage(percentages=[30, 40, 30])
        for i, t in enumerate(targets):
            split.branch(i, t)

        conditions = split.transitions["Conditions"]
        assert len(conditions) == 3
        assert conditions[0]["Condition"]["Operands"] == ["31"]
        assert conditions[1]["Condition"]["Operands"] == ["71"]
        assert conditions[2]["Condition"]["Operands"] == ["101"]

    def test_parameters_always_empty(self):
        split = DistributeByPercentage(percentages=[50, 50])
        serialized = split.to_dict()
        assert serialized["Parameters"] == {}

    def test_auto_adds_no_matching_condition_error(self):
        split = DistributeByPercentage(percentages=[50, 50])
        serialized = split.to_dict()
        error_types = [
            e["ErrorType"] for e in serialized["Transitions"].get("Errors", [])
        ]
        assert "NoMatchingCondition" in error_types

    def test_branch_index_out_of_range(self):
        split = DistributeByPercentage(percentages=[50, 50])
        target = FlowBlock(identifier="t")
        with pytest.raises(ValueError, match="out of range"):
            split.branch(2, target)

    def test_branch_without_percentages(self):
        split = DistributeByPercentage()
        target = FlowBlock(identifier="t")
        with pytest.raises(ValueError, match="percentages must be set"):
            split.branch(0, target)

    def test_build_condition_metadata(self):
        split = DistributeByPercentage(percentages=[60, 40])
        conditions, metadata = split.build_condition_metadata()
        assert len(conditions) == 2
        assert len(metadata) == 2
        assert metadata[0]["name"] == "A"
        assert metadata[0]["percent"]["value"] == 60
        assert metadata[1]["name"] == "B"
        assert metadata[1]["percent"]["value"] == 40

    def test_build_condition_metadata_empty(self):
        split = DistributeByPercentage()
        conditions, metadata = split.build_condition_metadata()
        assert conditions == []
        assert metadata == []


# --- ShowView ---


class TestShowView:
    def test_on_action_adds_condition(self):
        target = FlowBlock(identifier="submit-handler")
        view = ShowView(
            view_resource=ViewResource(id="view-1", version="1"),
            invocation_time_limit_seconds=300,
        )
        view.on_action("Submit", target)

        conditions = view.transitions["Conditions"]
        assert len(conditions) == 1
        assert conditions[0]["NextAction"] == "submit-handler"
        assert conditions[0]["Condition"]["Operator"] == "Equals"
        assert conditions[0]["Condition"]["Operands"] == ["Submit"]

    def test_on_action_chaining(self):
        t1 = FlowBlock(identifier="t1")
        t2 = FlowBlock(identifier="t2")
        view = ShowView(view_resource=ViewResource(id="v", version="1"))
        view.on_action("Submit", t1).on_action("Cancel", t2)

        assert len(view.transitions["Conditions"]) == 2

    def test_view_resource_serialization(self):
        view = ShowView(
            view_resource=ViewResource(id="my-view", version="2"),
            invocation_time_limit_seconds=120,
        )
        params = view.to_dict()["Parameters"]
        assert params["ViewResource"]["Id"] == "my-view"
        assert params["ViewResource"]["Version"] == "2"
        assert params["InvocationTimeLimitSeconds"] == "120"

    def test_view_data_serialization(self):
        view = ShowView(
            view_resource=ViewResource(id="v", version="1"),
            view_data={"customer_name": "$.Attributes.Name"},
        )
        params = view.to_dict()["Parameters"]
        assert params["ViewData"] == {"customer_name": "$.Attributes.Name"}

    def test_sensitive_data_configuration(self):
        view = ShowView(
            view_resource=ViewResource(id="v", version="1"),
            sensitive_data_configuration={"HideResponseOn": ["TRANSCRIPT"]},
        )
        params = view.to_dict()["Parameters"]
        assert params["SensitiveDataConfiguration"]["HideResponseOn"] == ["TRANSCRIPT"]


# --- MessageParticipantIteratively ---


class TestMessageParticipantIteratively:
    def test_messages_list_serialization(self):
        block = MessageParticipantIteratively(
            messages=[{"Text": "Msg 1"}, {"Text": "Msg 2"}]
        )
        params = block.to_dict()["Parameters"]
        assert params["Messages"] == [{"Text": "Msg 1"}, {"Text": "Msg 2"}]

    def test_interrupt_frequency_serialization(self):
        block = MessageParticipantIteratively(
            messages=[{"Text": "Hold music"}],
            interrupt_frequency_seconds="30",
        )
        params = block.to_dict()["Parameters"]
        assert params["InterruptFrequencySeconds"] == "30"

    def test_from_dict_restores_messages(self):
        original = MessageParticipantIteratively(
            messages=[{"Text": "A"}, {"SSML": "<speak>B</speak>"}],
            interrupt_frequency_seconds="60",
        )
        restored = MessageParticipantIteratively.from_dict(original.to_dict())
        assert restored.messages == [{"Text": "A"}, {"SSML": "<speak>B</speak>"}]
        assert restored.interrupt_frequency_seconds == "60"


# --- Shell blocks (minimal typed fields) ---


class TestShellBlocks:
    def test_create_callback_contact_type(self):
        block = CreateCallbackContact()
        assert block.type == "CreateCallbackContact"

    def test_check_metric_data_type(self):
        block = CheckMetricData()
        assert block.type == "CheckMetricData"

    def test_create_task_type(self):
        block = CreateTask()
        assert block.type == "CreateTask"
