import uuid

import psycopg
import pytest
from src.__types.errors import ResourceDuplicatedError, ResourceNotFoundError
from src.__types.index import Distribution
from src.infrastructure.repositories.tenant_repository import TenantRepository


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

    class TestAdd:
        def test_unique_tenant(
            self,
            repository: TenantRepository,
            db_connection: psycopg.Connection,
        ) -> None:
            """重複する値がないTenantを作成できることを確認する。"""
            # Arrange
            tenant_id = uuid.uuid4()
            m2m_application_client_id = "application_client#dummy"

            # Act
            repository.add(
                tenant_id=tenant_id,
                legal_name="ダミー株式会社",
                name="dummy_unique",
                m2m_application_client_id=m2m_application_client_id,
                whi_customer_ids=["whi#dummy_1", "whi#dummy_2"],
                cydas_customer_ids=["cydas#dummy_1"],
            )

            # Assert
            with db_connection.cursor() as cur:
                cur.execute(
                    """--sql
                    SELECT
                        id,
                        m2m_application_client_id,
                        legal_name,
                        whi_customer_id,
                        cydas_customer_id,
                        allowed_m2m_metadata
                    FROM tenants
                    WHERE id = %(tenant_id)s
                    """,
                    {
                        "tenant_id": tenant_id,
                    },
                )
                actual = cur.fetchone()
                assert actual is not None
                assert actual[0] == tenant_id
                assert actual[1] == m2m_application_client_id
                assert actual[2] == "ダミー株式会社"
                assert actual[3] == ["whi#dummy_1", "whi#dummy_2"]
                assert actual[4] == ["cydas#dummy_1"]
                assert actual[5] == []

        def test_duplicated_tenant_id(
            self,
            repository: TenantRepository,
        ) -> None:
            """tenant_idが重複するTenantの作成リクエストを拒否することを確認する。"""
            # Arrange
            tenant_id = uuid.UUID("00000001-0000-0000-0000-000000000000")

            # Act / Assert
            with pytest.raises(ResourceDuplicatedError):
                repository.add(
                    tenant_id=tenant_id,
                    legal_name="重複テナント",
                    name="dummy_duplicated",
                    m2m_application_client_id="application_client#dummy_duplicated",
                    whi_customer_ids=["whi#dummy_duplicated"],
                    cydas_customer_ids=[],
                )

        def test_duplicated_whi_customer_id(
            self,
            repository: TenantRepository,
        ) -> None:
            """既存Tenantと重複するWHI customer_idを持つTenantの作成リクエストを拒否することを確認する。"""
            with pytest.raises(ResourceDuplicatedError):
                repository.add(
                    tenant_id=uuid.uuid4(),
                    legal_name="重複WHIテナント",
                    name="dummy_whi_duplicated",
                    m2m_application_client_id="application_client#dummy_whi_duplicated",
                    whi_customer_ids=["whi#alpha_1"],
                    cydas_customer_ids=[],
                )

        def test_duplicated_cydas_customer_id(
            self,
            repository: TenantRepository,
        ) -> None:
            """既存Tenantと重複するCYDAS customer_idを持つTenantの作成リクエストを拒否することを確認する。"""
            with pytest.raises(ResourceDuplicatedError):
                repository.add(
                    tenant_id=uuid.uuid4(),
                    legal_name="重複CYDASテナント",
                    name="dummy_cydas_duplicated",
                    m2m_application_client_id="application_client#dummy_cydas_duplicated",
                    whi_customer_ids=[],
                    cydas_customer_ids=["cydas#alpha"],
                )

        def test_duplicated_customer_id_in_request(
            self,
            repository: TenantRepository,
        ) -> None:
            """単一リクエスト内で重複するcustomer_idを持つTenantの作成リクエストを拒否することを確認する。"""
            with pytest.raises(ResourceDuplicatedError):
                repository.add(
                    tenant_id=uuid.uuid4(),
                    legal_name="重複リクエストテナント",
                    name="dummy_request_duplicated",
                    m2m_application_client_id="application_client#dummy_request_duplicated",
                    whi_customer_ids=["whi#dummy_same", "whi#dummy_same"],
                    cydas_customer_ids=[],
                )

    class TestGet:
        def test_exists(
            self,
            repository: TenantRepository,
        ) -> None:
            """存在するTenantを取得できることを確認する。"""
            # Arrange
            tenant_id = uuid.UUID("00000001-0000-0000-0000-000000000000")

            # Act
            tenant = repository.get(tenant_id=tenant_id)

            # Assert
            assert tenant.id == tenant_id
            assert tenant.m2m_application_client_id == "application_client#alpha"
            assert tenant.legal_name == "アルファ株式会社"
            assert tenant.whi_customer_id == ["whi#alpha_1", "whi#alpha_2"]
            assert tenant.cydas_customer_id == ["cydas#alpha"]
            assert tenant.allowed_m2m_metadata == [
                {"callerService": "ctm", "tenantName": "client_tenant_alpha_alpha_1"},
            ]

        def test_not_found(
            self,
            repository: TenantRepository,
        ) -> None:
            """存在しないTenantを取得するリクエストを拒否できることを確認する。"""
            # Arrange
            tenant_id = "99999999-0000-0000-0000-000000000000"

            # Act / Assert
            with pytest.raises(ResourceNotFoundError):
                repository.get(tenant_id=uuid.UUID(tenant_id))

    class TestGetByM2MApplicationClientId:
        def test_exists(
            self,
            repository: TenantRepository,
        ) -> None:
            """m2m_application_client_idが一致するTenantを取得できることを確認する。"""
            # Arrange
            m2m_application_client_id = "application_client#beta"

            # Act
            tenant = repository.get_by_m2m_application_client_id(m2m_application_client_id=m2m_application_client_id)

            # Assert
            assert tenant.id == uuid.UUID("00000002-0000-0000-0000-000000000000")
            assert tenant.m2m_application_client_id == m2m_application_client_id
            assert tenant.legal_name == "ベータ株式会社"
            assert tenant.whi_customer_id == ["whi#beta"]
            assert tenant.cydas_customer_id is None

        def test_not_found(
            self,
            repository: TenantRepository,
        ) -> None:
            """m2m_application_client_idが一致しないTenantの取得リクエストを拒否することを確認する。"""
            # Arrange
            m2m_application_client_id = "application_client#not_found"

            # Act / Assert
            with pytest.raises(ResourceNotFoundError):
                repository.get_by_m2m_application_client_id(m2m_application_client_id=m2m_application_client_id)

    class TestGetByCustomerId:
        def test_whi_exists(
            self,
            repository: TenantRepository,
        ) -> None:
            """WHI商流の取引先IDと合致するTenantを取得できることを確認する。"""
            # Arrange
            customer_id = "whi#alpha_2"

            # Act
            tenant = repository.get_by_customer_id(
                distribution=Distribution.WHI_SFDC,
                customer_id=customer_id,
            )

            # Assert
            assert tenant.id == uuid.UUID("00000001-0000-0000-0000-000000000000")
            assert tenant.legal_name == "アルファ株式会社"

        def test_cydas_exists(
            self,
            repository: TenantRepository,
        ) -> None:
            """CYDAS商流の取引先IDと合致するTenantを取得できることを確認する。"""
            # Arrange
            customer_id = "cydas#alpha"

            # Act
            tenant = repository.get_by_customer_id(
                distribution=Distribution.CYDAS_SFDC,
                customer_id=customer_id,
            )

            # Assert
            assert tenant.id == uuid.UUID("00000001-0000-0000-0000-000000000000")
            assert tenant.legal_name == "アルファ株式会社"

        def test_not_found(
            self,
            repository: TenantRepository,
        ) -> None:
            """取引先IDと合致しないTenantの取得リクエストを拒否することを確認する。"""
            # Arrange
            customer_id = "whi#not_found"

            # Act / Assert
            with pytest.raises(ResourceNotFoundError):
                repository.get_by_customer_id(
                    distribution=Distribution.WHI_SFDC,
                    customer_id=customer_id,
                )

    class TestGetAuthInfoByName:
        def test_exists(
            self,
            repository: TenantRepository,
        ) -> None:
            """存在するTenantに対して、認証に必要な情報を取得できることを確認する。"""
            # Act
            tenant_auth_info = repository.get_auth_info_by_name(name="alpha")

            # Assert
            assert tenant_auth_info is not None
            assert tenant_auth_info.tenant_id == uuid.UUID("00000001-0000-0000-0000-000000000000")
            assert tenant_auth_info.user_application_client_id == "user_application_client#alpha"

        def test_not_found(
            self,
            repository: TenantRepository,
        ) -> None:
            """Tenant が存在しない場合に、エラーが発生することを確認する。"""
            # Act / Assert
            with pytest.raises(ResourceNotFoundError):
                repository.get_auth_info_by_name(name="not_found")
