import uuid

import psycopg
import pytest
from src.__types.errors import ResourceDuplicatedError
from src.__types.index import Distribution
from src.infrastructure.repositories.pending_client_tenant_repository import PendingClientTenantRepository


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

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

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

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

        def test_client_tenant_already_exists(
            self,
            repository: PendingClientTenantRepository,
        ) -> None:
            """client_idが同一かつTenantNameが重複するリクエストを拒否できるか確認する。"""
            # Arrange
            client_id = "00000001-0000-0000-0000-000000000000"
            name = "duplicated_pending_client_tenant"
            repository.add(
                client_id=uuid.UUID(client_id),
                name=name,
                distribution=Distribution.WHI_SFDC,
                customer_id="dummy_customer_id",
                base_url=None,
            )

            # Act / Assert
            with pytest.raises(ResourceDuplicatedError):
                repository.add(
                    client_id=uuid.UUID(client_id),
                    name=name,
                    distribution=Distribution.CYDAS_SFDC,
                    customer_id="cydas#dummy-1",
                    base_url="https://dummy.example.com",
                )

        def test_client_tenant_name_already_exists_in_other_client(
            self,
            repository: PendingClientTenantRepository,
            db_connection: psycopg.Connection,
        ) -> None:
            """他Clientに同一のTenantNameを持つPendingClientTenantが存在する場合に、PendingClientTenantを作成できることを確認する。"""
            # Arrange
            client_id = "00000001-0000-0000-0000-000000000000"
            another_client_id = "00000002-0000-0000-0000-000000000000"
            name = "duplicated_pending_client_tenant_between_clients"
            repository.add(
                client_id=uuid.UUID(client_id),
                name=name,
                distribution=Distribution.WHI_SFDC,
                customer_id="dummy_customer_id",
                base_url=None,
            )

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

            # Assert
            with db_connection.cursor() as cur:
                cur.execute(
                    """--sql
                    SELECT client_id, name, distribution, customer_id, base_url
                    FROM pending_client_tenants
                    WHERE name = %(name)s
                    ORDER BY client_id
                    """,
                    {
                        "name": name,
                    },
                )
                actual = cur.fetchall()
                assert len(actual) == 2
                assert actual[0] == (
                    uuid.UUID(client_id),
                    name,
                    Distribution.WHI_SFDC.value,
                    "dummy_customer_id",
                    None,
                )
                assert actual[1] == (
                    uuid.UUID(another_client_id),
                    name,
                    Distribution.CYDAS_SFDC.value,
                    "dummy_customer_id",
                    "https://dummy.example.com",
                )

    class TestFindByCustomerId:
        def test_exists(
            self,
            repository: PendingClientTenantRepository,
        ) -> None:
            """customer_idとclient_idが合致するClientTenantの一覧を取得できることを確認する。"""
            # Arrange
            customer_id = "dummy_customer_id"
            repository.add(
                client_id=uuid.UUID("00000001-0000-0000-0000-000000000000"),
                name="dummy_pending_client_tenant_1",
                distribution=Distribution.WHI_SFDC,
                customer_id=customer_id,
                base_url=None,
            )
            repository.add(
                client_id=uuid.UUID("00000002-0000-0000-0000-000000000000"),
                name="dummy_pending_client_tenant_2",
                distribution=Distribution.WHI_SFDC,
                customer_id=customer_id,
                base_url="https://dummy.example.com",
            )

            # Act
            pending_client_tenants = repository.find_by_customer_id(
                distribution=Distribution.WHI_SFDC,
                customer_id=customer_id,
            )

            # Assert
            assert len(pending_client_tenants) == 2
            assert pending_client_tenants[0].client_id == uuid.UUID("00000001-0000-0000-0000-000000000000")
            assert pending_client_tenants[0].name == "dummy_pending_client_tenant_1"
            assert pending_client_tenants[1].client_id == uuid.UUID("00000002-0000-0000-0000-000000000000")
            assert pending_client_tenants[1].name == "dummy_pending_client_tenant_2"

        def test_no_match(
            self,
            repository: PendingClientTenantRepository,
        ) -> None:
            """customer_idとclient_idが合致するClientTenantが存在しない場合に空のリストを取得できることを確認する。"""
            # Act
            pending_client_tenants = repository.find_by_customer_id(
                distribution=Distribution.WHI_SFDC,
                customer_id="not_found",
            )

            # Assert
            assert pending_client_tenants == []

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

            # Act
            pending_client_tenants = repository.find(client_id=client_id)

            # Assert
            assert len(pending_client_tenants) == 1

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

            # Act
            pending_client_tenants = repository.find(client_id=client_id)

            # Assert
            assert pending_client_tenants == []

    class TestDelete:
        def test_exists(
            self,
            repository: PendingClientTenantRepository,
            db_connection: psycopg.Connection,
        ) -> None:
            """customer_idとclient_idが合致するClientTenantを削除できることを確認する。"""
            # Arrange
            client_id = "00000001-0000-0000-0000-000000000000"
            name = "dummy_pending_client_tenant_delete"
            repository.add(
                client_id=uuid.UUID(client_id),
                name=name,
                distribution=Distribution.WHI_SFDC,
                customer_id="dummy_customer_id",
                base_url=None,
            )

            # Act
            repository.delete(
                client_id=uuid.UUID(client_id),
                name=name,
            )

            # Assert
            with db_connection.cursor() as cur:
                cur.execute(
                    """--sql
                    SELECT COUNT(*)
                    FROM pending_client_tenants
                    WHERE client_id = %(client_id)s
                        AND name = %(name)s
                    """,
                    {
                        "client_id": client_id,
                        "name": name,
                    },
                )
                assert cur.fetchone() == (0,)
