from uuid import UUID

import psycopg
from psycopg.rows import class_row
from src.__types.errors import ResourceDuplicatedError, ResourceNotFoundError
from src.__types.index import ClientTenant, Distribution
from src.settings import Settings

settings = Settings()


class ClientTenantRepository:
    """ClientTenantを管理するリポジトリ"""

    def __init__(self, connection: psycopg.Connection) -> None:
        self.connection = connection

    def get(self, client_id: UUID, client_tenant_name: str) -> ClientTenant:
        """一意のClientTenantを取得する。"""
        with self.connection.cursor(row_factory=class_row(ClientTenant)) as cur:
            cur.execute(
                # NOTE: 解決順位として、client_idが一致するClientTenantを優先する(移行期のみの特別対応)。
                """--sql
                SELECT id, tenant_id, client_id, name, distribution, customer_id, base_url
                FROM client_tenants
                WHERE name = %(client_tenant_name)s
                    AND (client_id = %(client_id)s OR client_id IS NULL)
                ORDER BY client_id NULLS LAST
                """,
                {
                    "client_tenant_name": client_tenant_name,
                    "client_id": str(client_id),
                },
            )
            client_tenant = cur.fetchone()
            if client_tenant is None:
                raise ResourceNotFoundError(
                    f"Client tenant with name '{client_tenant_name}' for client ID {client_id} does not exist.",
                )
            return client_tenant

    def find(self, client_id: UUID) -> list[ClientTenant]:
        """Clientが解決できるClientTenantのリストを取得する"""
        # NOTE: client_id=NULLのClientTenantは開発者に混乱を招く、かつ廃止予定のためリストには含めない。
        with self.connection.cursor(row_factory=class_row(ClientTenant)) as cur:
            cur.execute(
                """--sql
                SELECT
                    id,
                    tenant_id,
                    client_id,
                    name,
                    distribution,
                    customer_id,
                    base_url
                FROM client_tenants
                WHERE client_id = %(client_id)s
                ORDER BY name
                """,
                {
                    "client_id": str(client_id),
                },
            )
            return list(cur.fetchall())

    def add(
        self,
        tenant_id: UUID,
        client_id: UUID,
        name: str,
        distribution: Distribution,
        customer_id: str,
        base_url: str | None,
    ) -> None:
        """ClientTenantを追加する"""
        # NOTE: 廃止予定のため、client_id=NULLのClientTenantをAPIから作成することは禁止する。
        try:
            with self.connection.cursor() as cur:
                cur.execute(
                    """--sql
                    INSERT INTO client_tenants (
                        tenant_id,
                        client_id,
                        name,
                        distribution,
                        customer_id,
                        base_url
                    )
                    VALUES (
                        %(tenant_id)s,
                        %(client_id)s,
                        %(name)s,
                        %(distribution)s,
                        %(customer_id)s,
                        %(base_url)s
                    )
                    """,
                    {
                        "tenant_id": str(tenant_id),
                        "client_id": str(client_id),
                        "name": name,
                        "distribution": distribution.value,
                        "customer_id": customer_id,
                        "base_url": base_url,
                    },
                )
        except psycopg.errors.UniqueViolation as e:
            raise ResourceDuplicatedError(
                f"Client tenant with name '{name}' for client ID {client_id} already exists.",
            ) from e
