import uuid

import psycopg
import pytest
from src.__types.errors import ResourceDuplicatedError
from src.__types.index import Distribution
from src.infrastructure.repositories.client_tenant_repository import ClientTenantRepository


class TestClientTenantRepository:
    @pytest.fixture
    def repository(self, db_connection: psycopg.Connection) -> ClientTenantRepository:
        return ClientTenantRepository(connection=db_connection)

    class TestAdd:
        def test_unique_client_tenant_name(
            self,
            repository: ClientTenantRepository,
            db_connection: psycopg.Connection,
        ) -> None:
            """全てのClientTenantとTenantNameが重複しないClientTenantが作成できるかを確認する。"""
            # Arrange
            tenant_id = "00000001-0000-0000-0000-000000000000"
            client_id = "00000001-0000-0000-0000-000000000000"
            client_tenant_name = "dummy_client_tenant_name"

            # Act
            repository.add(
                tenant_id=uuid.UUID(tenant_id),
                client_id=uuid.UUID(client_id),
                name=client_tenant_name,
                distribution=Distribution.WHI_SFDC,
                customer_id="whi#dummy",
                base_url="https://dummy.example.com",
            )

            # Assert
            with db_connection.cursor() as cur:
                cur.execute(
                    """--sql
                    SELECT tenant_id, client_id, name, distribution, customer_id, base_url
                    FROM client_tenants
                    WHERE tenant_id = %(tenant_id)s
                        AND client_id = %(client_id)s
                        AND name = %(client_tenant_name)s
                    """,
                    {
                        "tenant_id": tenant_id,
                        "client_id": client_id,
                        "client_tenant_name": client_tenant_name,
                    },
                )
                actual = cur.fetchone()
                assert actual is not None
                assert actual[0] == uuid.UUID(tenant_id)
                assert actual[1] == uuid.UUID(client_id)
                assert actual[2] == client_tenant_name
                assert actual[3] == "whi_sfdc"
                assert actual[4] == "whi#dummy"
                assert actual[5] == "https://dummy.example.com"

        def test_client_tenant_name_already_exists(
            self,
            repository: ClientTenantRepository,
            db_connection: psycopg.Connection,
        ) -> None:
            """client_id=nullのClientとTenantNameが重複するClientTenantを作成できることを確認する。"""
            # Arrange
            tenant_id = "00000001-0000-0000-0000-000000000000"
            client_id = "00000001-0000-0000-0000-000000000000"
            client_tenant_name = "client_tenant_alpha_alpha_1"

            # Act
            repository.add(
                tenant_id=uuid.UUID(tenant_id),
                client_id=uuid.UUID(client_id),
                name=client_tenant_name,
                distribution=Distribution.WHI_SFDC,
                customer_id="whi#dummy",
                base_url="https://dummy.example.com",
            )

            # Assert
            with db_connection.cursor() as cur:
                cur.execute(
                    """--sql
                    SELECT tenant_id, client_id, name
                    FROM client_tenants
                    WHERE tenant_id = %(tenant_id)s
                        AND name = %(client_tenant_name)s
                    """,
                    {
                        "tenant_id": tenant_id,
                        "client_id": client_id,
                        "client_tenant_name": client_tenant_name,
                    },
                )
                assert len(cur.fetchall()) == 2

        def test_client_tenant_already_exists(
            self,
            repository: ClientTenantRepository,
        ) -> None:
            """client_idが同一かつTenantNameが重複するリクエストを拒否できるか確認する。"""
            # Arrange
            tenant_id = "00000001-0000-0000-0000-000000000000"
            client_id = "00000002-0000-0000-0000-000000000000"
            client_tenant_name = "client_tenant_alpha_hci_1"

            # Act & Assert
            with pytest.raises(ResourceDuplicatedError):
                repository.add(
                    tenant_id=uuid.UUID(tenant_id),
                    client_id=uuid.UUID(client_id),
                    name=client_tenant_name,
                    distribution=Distribution.WHI_SFDC,
                    customer_id="whi#dummy",
                    base_url="https://dummy.example.com",
                )

        def test_client_tenant_name_already_exists_in_other_client(
            self,
            repository: ClientTenantRepository,
            db_connection: psycopg.Connection,
        ) -> None:
            """他Clientに同一のTenantNameを持つClientTenantが存在する場合に、ClientTenantを作成できることを確認する。"""
            # Arrange
            tenant_id = "00000001-0000-0000-0000-000000000000"
            client_id = "00000002-0000-0000-0000-000000000000"
            client_tenant_name = "client_tenant_alpha_gen_ai_profile_2"

            # Act
            repository.add(
                tenant_id=uuid.UUID(tenant_id),
                client_id=uuid.UUID(client_id),
                name=client_tenant_name,
                distribution=Distribution.CYDAS_SFDC,
                customer_id="cydas#dummy",
                base_url="https://dummy.example.com",
            )

            # Assert
            with db_connection.cursor() as cur:
                cur.execute(
                    """--sql
                    SELECT tenant_id, client_id, name, distribution, customer_id, base_url
                    FROM client_tenants
                    WHERE name = %(client_tenant_name)s
                    ORDER BY client_id NULLS FIRST
                    """,
                    {
                        "client_tenant_name": client_tenant_name,
                    },
                )
                actual = cur.fetchall()
                assert len(actual) == 2
                assert actual[0] == (
                    uuid.UUID("00000001-0000-0000-0000-000000000000"),
                    uuid.UUID("00000001-0000-0000-0000-000000000000"),
                    client_tenant_name,
                    "whi_sfdc",
                    "whi#alpha_1",
                    "https://gen-ai-profile-2.example.com",
                )
                assert actual[1] == (
                    uuid.UUID(tenant_id),
                    uuid.UUID(client_id),
                    client_tenant_name,
                    "cydas_sfdc",
                    "cydas#dummy",
                    "https://dummy.example.com",
                )

    class TestGet:
        def test_client_id_is_null(
            self,
            repository: ClientTenantRepository,
        ) -> None:
            """client_id=nullのClientTenantのみ存在する場合に取得できるかを確認する。"""
            # Arrange
            client_id = "00000000-0000-0000-0000-000000000000"
            client_tenant_name = "client_tenant_alpha_gen_ai_profile_1"

            # Act
            client_tenant = repository.get(
                client_id=uuid.UUID(client_id),
                client_tenant_name=client_tenant_name,
            )

            # Assert
            assert client_tenant.tenant_id == uuid.UUID("00000001-0000-0000-0000-000000000000")
            assert client_tenant.client_id is None
            assert client_tenant.name == client_tenant_name
            assert client_tenant.distribution == Distribution.WHI_SFDC
            assert client_tenant.customer_id == "whi#alpha_1"
            assert client_tenant.base_url is None

        def test_client_id_is_not_null(
            self,
            repository: ClientTenantRepository,
        ) -> None:
            """client_idを持つClientTenantのみ存在する場合に取得できるかを確認する。"""
            # Arrange
            client_id = "00000001-0000-0000-0000-000000000000"
            client_tenant_name = "client_tenant_alpha_gen_ai_profile_2"

            # Act
            client_tenant = repository.get(
                client_id=uuid.UUID(client_id),
                client_tenant_name=client_tenant_name,
            )

            # Assert
            assert client_tenant.tenant_id == uuid.UUID("00000001-0000-0000-0000-000000000000")
            assert client_tenant.client_id == uuid.UUID(client_id)
            assert client_tenant.name == client_tenant_name
            assert client_tenant.distribution == Distribution.WHI_SFDC
            assert client_tenant.customer_id == "whi#alpha_1"
            assert client_tenant.base_url == "https://gen-ai-profile-2.example.com"

        def test_prefer_client_id_is_not_null(
            self,
            repository: ClientTenantRepository,
        ) -> None:
            """TenantNameが同一で、client_idを持つものと持たないものが存在する時、client_idを持つClientTenantが優先されることを確認する。"""
            # Arrange
            client_id = "00000001-0000-0000-0000-000000000000"
            client_tenant_name = "client_tenant_alpha_gen_ai_profile_3"

            # Act
            client_tenant = repository.get(
                client_id=uuid.UUID(client_id),
                client_tenant_name=client_tenant_name,
            )

            # Assert
            assert client_tenant.tenant_id == uuid.UUID("00000001-0000-0000-0000-000000000000")
            assert client_tenant.client_id == uuid.UUID(client_id)
            assert client_tenant.name == client_tenant_name
            assert client_tenant.distribution == Distribution.WHI_SFDC
            assert client_tenant.customer_id == "whi#alpha_1"
            assert client_tenant.base_url == "https://gen-ai-profile-3.client.example.com"

    class TestFind:
        def test_client_tenant_list(
            self,
            repository: ClientTenantRepository,
        ) -> None:
            """client_idが一致するClientTenantの一覧を取得できることを確認する。"""
            # Arrange
            client_id = "00000001-0000-0000-0000-000000000000"

            # Act
            client_tenants = repository.find(client_id=uuid.UUID(client_id))

            # Assert
            assert len(client_tenants) == 2
            assert all(client_tenant.client_id == uuid.UUID(client_id) for client_tenant in client_tenants)

        def test_client_id_no_match(
            self,
            repository: ClientTenantRepository,
        ) -> None:
            """client_idが一致するClientTenantが存在しないときに空のリストを取得できることを確認する。"""
            # Arrange
            client_id = "00000003-0000-0000-0000-000000000000"

            # Act
            client_tenants = repository.find(client_id=uuid.UUID(client_id))

            # Assert
            assert len(client_tenants) == 0
