#
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
#

from pathlib import Path

import pytest
from consts import BUILD_PLATFORMS
from tests.utils import mock_container

from pipelines.airbyte_ci.connectors.build_image.steps import (
    build_customization,
    manifest_only_connectors,
)
from pipelines.models.steps import StepStatus

pytestmark = [
    pytest.mark.anyio,
]


class TestBuildConnectorImage:
    @pytest.fixture
    def all_platforms(self):
        return BUILD_PLATFORMS

    @pytest.fixture
    def test_context(self, mocker):
        return mocker.Mock(secrets_to_mask=[], targeted_platforms=BUILD_PLATFORMS)

    @pytest.fixture
    def test_context_with_connector_with_base_image(self, test_context):
        test_context.connector.metadata = {
            "connectorBuildOptions": {"baseImage": "xyz"},
            "dockerImageTag": "0.0.0",
            "dockerRepository": "test",
        }
        return test_context

    @pytest.fixture
    def mock_connector_directory(self, mocker):
        mock_components_file = mocker.Mock()
        mock_connector_dir = mocker.Mock()
        mock_connector_dir.file.return_value = mock_components_file
        return mock_connector_dir, mock_components_file

    def _assert_file_not_handled(self, container_mock, file_path):
        """Assert that a specified file_path was not handled by the container_mock"""
        assert not any(
            file_path in call.args[0]
            for call in container_mock.with_file.call_args_list
        )

    async def test__run_using_base_image_with_mocks(
        self, mocker, test_context_with_connector_with_base_image, all_platforms
    ):
        container_built_from_base = mock_container()
        container_built_from_base.with_label.return_value = container_built_from_base

        mocker.patch.object(
            Path, "exists", return_value=True
        )  # Mock Path.exists() to always return True
        mocker.patch.object(
            manifest_only_connectors.BuildConnectorImages,
            "_build_from_base_image",
            mocker.AsyncMock(return_value=container_built_from_base),
        )
        mocker.patch.object(
            manifest_only_connectors.BuildConnectorImages,
            "get_step_result",
            mocker.AsyncMock(),
        )
        step = manifest_only_connectors.BuildConnectorImages(
            test_context_with_connector_with_base_image
        )
        step_result = await step._run()
        assert step._build_from_base_image.call_count == len(all_platforms)
        container_built_from_base.with_exec.assert_called_with(
            ["spec"], use_entrypoint=True
        )
        container_built_from_base.with_label.assert_any_call(
            "io.airbyte.version",
            test_context_with_connector_with_base_image.connector.metadata[
                "dockerImageTag"
            ],
        )
        container_built_from_base.with_label.assert_any_call(
            "io.airbyte.name",
            test_context_with_connector_with_base_image.connector.metadata[
                "dockerRepository"
            ],
        )

        assert step_result.status is StepStatus.SUCCESS
        for platform in all_platforms:
            assert step_result.output[platform] == container_built_from_base

    @pytest.mark.parametrize("components_file_exists", [True, False])
    async def test__run_using_base_image_with_components_file(
        self,
        mocker,
        all_platforms,
        test_context_with_connector_with_base_image,
        mock_connector_directory,
        components_file_exists,
    ):
        mock_connector_dir, mock_components_file = mock_connector_directory
        container_built_from_base = mock_container()

        container_built_from_base.with_label.return_value = container_built_from_base
        container_built_from_base.with_file.return_value = container_built_from_base

        test_context_with_connector_with_base_image.get_connector_dir = (
            mocker.AsyncMock(return_value=mock_connector_dir)
        )
        test_context_with_connector_with_base_image.connector.manifest_only_components_path.exists = mocker.Mock(
            return_value=components_file_exists
        )

        mocker.patch.object(
            manifest_only_connectors.BuildConnectorImages,
            "_get_base_container",
            return_value=container_built_from_base,
        )

        mocker.patch.object(
            manifest_only_connectors.BuildConnectorImages,
            "get_image_user",
            return_value="airbyte",
        )

        mocker.patch.object(
            build_customization,
            "apply_airbyte_entrypoint",
            return_value=container_built_from_base,
        )

        mocker.patch.object(
            manifest_only_connectors,
            "apply_python_development_overrides",
            side_effect=mocker.AsyncMock(return_value=container_built_from_base),
        )

        step = manifest_only_connectors.BuildConnectorImages(
            test_context_with_connector_with_base_image
        )

        await step._build_connector(all_platforms[0], container_built_from_base)
        if components_file_exists:
            container_built_from_base.with_file.assert_any_call(
                "source_declarative_manifest/components.py",
                mock_components_file,
                owner="airbyte",
            )
            mock_connector_dir.file.assert_any_call("components.py")
        else:
            self._assert_file_not_handled(
                container_built_from_base, "source_declarative_manifest/components.py"
            )
