import pytest
import pytest_mock
from temporalio.testing import ActivityEnvironment

from csirtmu.beta_temporal.commons.conditions import CommonConditions
from csirtmu.beta_temporal.config import Config as WorkerConfig
from csirtmu.beta_temporal.external_services.rt import structs
from csirtmu.beta_temporal.external_services.rt.client import RTClient


class TestCommonConditions:
    @pytest.mark.parametrize("sender", ["ca@cesnet.cz", "tcs-ra@cesnet.cz"])
    async def test_is_certificate_notification_true(
        self,
        sender: str,
        worker_config: WorkerConfig,
        mock_ticket_data: structs.TicketData,
        mock_transaction_obj: structs.TransactionStruct,
        test_activity_env: ActivityEnvironment,
        mocker: pytest_mock.MockerFixture,
    ) -> None:
        mock_transaction_obj.interface = "Email"
        mock_ticket_data.headers.add_header("From", sender)
        get_ticket_data_mock = mocker.patch(
            "csirtmu.beta_temporal.commons.conditions.common_conditions.RTClient.get_ticket_data",
        )
        get_ticket_data_mock.return_value = mock_ticket_data
        activities = CommonConditions(RTClient(worker_config.rt))
        assert await test_activity_env.run(activities.is_certificate_notification, mock_transaction_obj)
        get_ticket_data_mock.assert_called_once_with(mock_transaction_obj)

    async def test_is_certificate_notification_wrong_interface(
        self,
        worker_config: WorkerConfig,
        mock_transaction_obj: structs.TransactionStruct,
        test_activity_env: ActivityEnvironment,
        mocker: pytest_mock.MockerFixture,
    ) -> None:
        mock_transaction_obj.interface = "Web"
        get_ticket_data_mock = mocker.patch(
            "csirtmu.beta_temporal.commons.conditions.common_conditions.RTClient.get_ticket_data",
        )
        activities = CommonConditions(RTClient(worker_config.rt))
        assert not await test_activity_env.run(activities.is_certificate_notification, mock_transaction_obj)
        get_ticket_data_mock.assert_not_called()

    async def test_is_certificate_notification_wrong_sender(
        self,
        worker_config: WorkerConfig,
        mock_ticket_data: structs.TicketData,
        mock_transaction_obj: structs.TransactionStruct,
        test_activity_env: ActivityEnvironment,
        mocker: pytest_mock.MockerFixture,
    ) -> None:
        mock_transaction_obj.interface = "Email"
        mock_ticket_data.headers.add_header("From", "anyone")
        get_ticket_data_mock = mocker.patch(
            "csirtmu.beta_temporal.commons.conditions.common_conditions.RTClient.get_ticket_data",
        )
        get_ticket_data_mock.return_value = mock_ticket_data
        activities = CommonConditions(RTClient(worker_config.rt))
        assert not await test_activity_env.run(activities.is_certificate_notification, mock_transaction_obj)
        get_ticket_data_mock.assert_called_once_with(mock_transaction_obj)

    async def test_custom_field_value_set_ticket_obj_is_none(
        self,
        worker_config: WorkerConfig,
        mock_transaction_obj: structs.TransactionStruct,
        test_activity_env: ActivityEnvironment,
    ) -> None:
        mock_transaction_obj.ticket_obj = None
        activities = CommonConditions(RTClient(worker_config.rt))
        assert not await test_activity_env.run(
            activities.custom_field_value_set, "Category", "Analytics", mock_transaction_obj
        )

    async def test_custom_field_value_set_false(
        self,
        worker_config: WorkerConfig,
        mock_transaction_obj: structs.TransactionStruct,
        test_activity_env: ActivityEnvironment,
    ) -> None:
        assert mock_transaction_obj.ticket_obj is not None
        mock_transaction_obj.ticket_obj.custom_fields = [
            structs.CustomFieldStruct(id="", url="", type="", name="Category", _values=["Test"]),
        ]
        activities = CommonConditions(RTClient(worker_config.rt))
        assert not await test_activity_env.run(
            activities.custom_field_value_set, "Category", "Analytics", mock_transaction_obj
        )

    async def test_custom_field_value_set_true(
        self,
        worker_config: WorkerConfig,
        mock_transaction_obj: structs.TransactionStruct,
        test_activity_env: ActivityEnvironment,
    ) -> None:
        assert mock_transaction_obj.ticket_obj is not None
        mock_transaction_obj.ticket_obj.custom_fields = [
            structs.CustomFieldStruct(id="", url="", type="", name="Category", _values=["Analytics"]),
        ]
        activities = CommonConditions(RTClient(worker_config.rt))
        assert await test_activity_env.run(
            activities.custom_field_value_set, "Category", "Analytics", mock_transaction_obj
        )

    async def test_is_from_interface_interface_is_none(
        self,
        worker_config: WorkerConfig,
        mock_transaction_obj: structs.TransactionStruct,
        test_activity_env: ActivityEnvironment,
    ) -> None:
        mock_transaction_obj.interface = None
        activities = CommonConditions(RTClient(worker_config.rt))
        assert not await test_activity_env.run(activities.is_from_interface, "Web", mock_transaction_obj)

    async def test_is_from_interface_false(
        self,
        worker_config: WorkerConfig,
        mock_transaction_obj: structs.TransactionStruct,
        test_activity_env: ActivityEnvironment,
    ) -> None:
        mock_transaction_obj.interface = "Email"
        activities = CommonConditions(RTClient(worker_config.rt))
        assert not await test_activity_env.run(activities.is_from_interface, "Web", mock_transaction_obj)

    async def test_is_from_interface_true(
        self,
        worker_config: WorkerConfig,
        mock_transaction_obj: structs.TransactionStruct,
        test_activity_env: ActivityEnvironment,
    ) -> None:
        mock_transaction_obj.interface = "Email"
        activities = CommonConditions(RTClient(worker_config.rt))
        assert await test_activity_env.run(activities.is_from_interface, "Email", mock_transaction_obj)

    async def test_is_tool_report_ticket_data_is_none(
        self,
        worker_config: WorkerConfig,
        mock_transaction_obj: structs.TransactionStruct,
        test_activity_env: ActivityEnvironment,
        mocker: pytest_mock.MockerFixture,
    ) -> None:
        get_ticket_data_mock = mocker.patch(
            "csirtmu.beta_temporal.commons.conditions.common_conditions.RTClient.get_ticket_data",
        )
        get_ticket_data_mock.return_value = None
        activities = CommonConditions(RTClient(worker_config.rt))
        assert not await test_activity_env.run(activities.is_tool_report, mock_transaction_obj)
        get_ticket_data_mock.assert_called_once_with(mock_transaction_obj)

    async def test_is_tool_report_no_attachments(
        self,
        worker_config: WorkerConfig,
        mock_ticket_data: structs.TicketData,
        mock_transaction_obj: structs.TransactionStruct,
        test_activity_env: ActivityEnvironment,
        mocker: pytest_mock.MockerFixture,
    ) -> None:
        get_ticket_data_mock = mocker.patch(
            "csirtmu.beta_temporal.commons.conditions.common_conditions.RTClient.get_ticket_data",
        )
        mock_ticket_data.attachments = []
        get_ticket_data_mock.return_value = mock_ticket_data
        activities = CommonConditions(RTClient(worker_config.rt))
        assert not await test_activity_env.run(activities.is_tool_report, mock_transaction_obj)
        get_ticket_data_mock.assert_called_once_with(mock_transaction_obj)

    async def test_is_tool_report_missing_header(
        self,
        worker_config: WorkerConfig,
        mock_ticket_data: structs.TicketData,
        mock_transaction_obj: structs.TransactionStruct,
        test_activity_env: ActivityEnvironment,
        mocker: pytest_mock.MockerFixture,
    ) -> None:
        get_ticket_data_mock = mocker.patch(
            "csirtmu.beta_temporal.commons.conditions.common_conditions.RTClient.get_ticket_data",
        )
        get_ticket_data_mock.return_value = mock_ticket_data
        activities = CommonConditions(RTClient(worker_config.rt))
        assert not await test_activity_env.run(activities.is_tool_report, mock_transaction_obj)
        get_ticket_data_mock.assert_called_once_with(mock_transaction_obj)

    async def test_is_tool_report_true(
        self,
        worker_config: WorkerConfig,
        mock_ticket_data: structs.TicketData,
        mock_transaction_obj: structs.TransactionStruct,
        test_activity_env: ActivityEnvironment,
        mocker: pytest_mock.MockerFixture,
    ) -> None:
        get_ticket_data_mock = mocker.patch(
            "csirtmu.beta_temporal.commons.conditions.common_conditions.RTClient.get_ticket_data",
        )
        mock_ticket_data.headers.add_header("X-RT-Tool-Name", "any")
        get_ticket_data_mock.return_value = mock_ticket_data
        activities = CommonConditions(RTClient(worker_config.rt))
        assert await test_activity_env.run(activities.is_tool_report, mock_transaction_obj)
        get_ticket_data_mock.assert_called_once_with(mock_transaction_obj)

    async def test_custom_field_changed_or_set_ticket_obj_is_none(
        self,
        worker_config: WorkerConfig,
        mock_transaction_obj: structs.TransactionStruct,
        test_activity_env: ActivityEnvironment,
    ) -> None:
        mock_transaction_obj.ticket_obj = None
        activities = CommonConditions(RTClient(worker_config.rt))
        assert not await test_activity_env.run(activities.custom_field_changed_or_set, "IP", mock_transaction_obj)

    async def test_custom_field_changed_or_set_transaction_type_not_cf_no_cf(
        self,
        worker_config: WorkerConfig,
        mock_transaction_obj: structs.TransactionStruct,
        test_activity_env: ActivityEnvironment,
    ) -> None:
        mock_transaction_obj.type = structs.TransactionTypeEnum.CREATE
        activities = CommonConditions(RTClient(worker_config.rt))
        assert not await test_activity_env.run(activities.custom_field_changed_or_set, "IP", mock_transaction_obj)

    async def test_custom_field_changed_or_set_transaction_type_not_cf_different_cf_set(
        self,
        worker_config: WorkerConfig,
        mock_transaction_obj: structs.TransactionStruct,
        test_activity_env: ActivityEnvironment,
    ) -> None:
        assert mock_transaction_obj.ticket_obj is not None
        mock_transaction_obj.type = structs.TransactionTypeEnum.CREATE
        mock_transaction_obj.ticket_obj.custom_fields = [
            structs.CustomFieldStruct("", "", "", "not IP", ["test"]),
        ]
        activities = CommonConditions(RTClient(worker_config.rt))
        assert not await test_activity_env.run(activities.custom_field_changed_or_set, "IP", mock_transaction_obj)

    async def test_custom_field_changed_or_set_transaction_type_not_cf_empty_cf(
        self,
        worker_config: WorkerConfig,
        mock_transaction_obj: structs.TransactionStruct,
        test_activity_env: ActivityEnvironment,
    ) -> None:
        assert mock_transaction_obj.ticket_obj is not None
        mock_transaction_obj.type = structs.TransactionTypeEnum.CREATE
        mock_transaction_obj.ticket_obj.custom_fields = [
            structs.CustomFieldStruct("", "", "", "IP", []),
        ]
        activities = CommonConditions(RTClient(worker_config.rt))
        assert not await test_activity_env.run(activities.custom_field_changed_or_set, "IP", mock_transaction_obj)

    async def test_custom_field_changed_or_set_transaction_type_not_cf_true(
        self,
        worker_config: WorkerConfig,
        mock_transaction_obj: structs.TransactionStruct,
        test_activity_env: ActivityEnvironment,
    ) -> None:
        assert mock_transaction_obj.ticket_obj is not None
        mock_transaction_obj.type = structs.TransactionTypeEnum.CREATE
        mock_transaction_obj.ticket_obj.custom_fields = [
            structs.CustomFieldStruct("", "", "", "IP", ["something"]),
        ]
        activities = CommonConditions(RTClient(worker_config.rt))
        assert await test_activity_env.run(activities.custom_field_changed_or_set, "IP", mock_transaction_obj)

    async def test_custom_field_changed_or_set_different_cf_changed(
        self,
        worker_config: WorkerConfig,
        mock_transaction_obj: structs.TransactionStruct,
        test_activity_env: ActivityEnvironment,
    ) -> None:
        assert mock_transaction_obj.ticket_obj is not None
        mock_transaction_obj.type = structs.TransactionTypeEnum.CUSTOM_FIELD
        mock_transaction_obj.new_value = "test"
        mock_transaction_obj.ticket_obj.custom_fields = [
            structs.CustomFieldStruct("", "", "", "not IP", ["test"]),
        ]
        activities = CommonConditions(RTClient(worker_config.rt))
        assert not await test_activity_env.run(activities.custom_field_changed_or_set, "IP", mock_transaction_obj)

    async def test_custom_field_changed_or_set_correct_cf_changed_true(
        self,
        worker_config: WorkerConfig,
        mock_transaction_obj: structs.TransactionStruct,
        test_activity_env: ActivityEnvironment,
    ) -> None:
        assert mock_transaction_obj.ticket_obj is not None
        mock_transaction_obj.type = structs.TransactionTypeEnum.CUSTOM_FIELD
        mock_transaction_obj.new_value = "test"
        mock_transaction_obj.ticket_obj.custom_fields = [
            structs.CustomFieldStruct("", "", "", "IP", ["test"]),
        ]
        activities = CommonConditions(RTClient(worker_config.rt))
        assert await test_activity_env.run(activities.custom_field_changed_or_set, "IP", mock_transaction_obj)


class TestIsStatusChange:
    async def test_is_status_change_ticket_obj_is_none(
        self,
        worker_config: WorkerConfig,
        test_activity_env: ActivityEnvironment,
        mock_transaction_obj: structs.TransactionStruct,
    ) -> None:
        activities = CommonConditions(RTClient(worker_config.rt))
        mock_transaction_obj.ticket_obj = None
        assert not await test_activity_env.run(activities.is_status_change, "test", mock_transaction_obj)

    async def test_is_status_change_transaction_type_not_status(
        self,
        worker_config: WorkerConfig,
        test_activity_env: ActivityEnvironment,
        mock_transaction_obj: structs.TransactionStruct,
    ) -> None:
        activities = CommonConditions(RTClient(worker_config.rt))
        assert not await test_activity_env.run(activities.is_status_change, "test", mock_transaction_obj)

    async def test_is_status_change_wrong_status(
        self,
        worker_config: WorkerConfig,
        test_activity_env: ActivityEnvironment,
        mock_transaction_obj: structs.TransactionStruct,
    ) -> None:
        assert mock_transaction_obj.ticket_obj is not None
        activities = CommonConditions(RTClient(worker_config.rt))
        mock_transaction_obj.ticket_obj.status = "idk"
        assert not await test_activity_env.run(activities.is_status_change, "test", mock_transaction_obj)

    async def test_is_status_change_wrong_new_value_correct_status_true(
        self,
        worker_config: WorkerConfig,
        test_activity_env: ActivityEnvironment,
        mock_transaction_obj: structs.TransactionStruct,
    ) -> None:
        assert mock_transaction_obj.ticket_obj is not None
        activities = CommonConditions(RTClient(worker_config.rt))
        mock_transaction_obj.new_value = "idk"
        mock_transaction_obj.ticket_obj.status = "test"
        assert await test_activity_env.run(activities.is_status_change, "test", mock_transaction_obj)

    async def test_is_status_change_new_value_status_change_true(
        self,
        worker_config: WorkerConfig,
        test_activity_env: ActivityEnvironment,
        mock_transaction_obj: structs.TransactionStruct,
    ) -> None:
        assert mock_transaction_obj.ticket_obj is not None
        activities = CommonConditions(RTClient(worker_config.rt))
        mock_transaction_obj.type = structs.TransactionTypeEnum.STATUS
        mock_transaction_obj.new_value = "test"
        assert await test_activity_env.run(activities.is_status_change, "test", mock_transaction_obj)
