from uuid import UUID

import psycopg
from psycopg.rows import class_row
from src.__types.errors import (
    ResourceDuplicatedError,
    ResourceNotFoundError,
)
from src.__types.index import ContractProvider


class ContractProviderRepository:
    """ContractProviderを管理するリポジトリ"""

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

    def get(self, contract_provider_id: UUID) -> ContractProvider:
        """指定されたIDのContractProviderを取得する"""
        with self.connection.cursor(row_factory=class_row(ContractProvider)) as cur:
            cur.execute(
                """--sql
                SELECT id, application_client_id, name
                FROM contract_providers
                WHERE id = %(contract_provider_id)s
                """,
                {
                    "contract_provider_id": contract_provider_id,
                },
            )
            if contract_provider := cur.fetchone():
                return contract_provider

            raise ResourceNotFoundError(f"ContractProvider with ID {contract_provider_id} not found.")

    def get_by_application_client_id(self, application_client_id: str) -> ContractProvider:
        """指定されたアプリケーションクライアントIDのContractProviderを取得する ※ 認可時にのみ使用"""
        with self.connection.cursor(row_factory=class_row(ContractProvider)) as cur:
            cur.execute(
                """--sql
                SELECT id, application_client_id, name
                FROM contract_providers
                WHERE application_client_id = %(application_client_id)s
                """,
                {
                    "application_client_id": application_client_id,
                },
            )
            if contract_provider := cur.fetchone():
                return contract_provider
            raise ResourceNotFoundError(
                f"ContractProvider with application client ID '{application_client_id}' not found.",
            )

    def add(
        self,
        contract_provider_id: UUID,  # NOTE: application_clientの命名規則の依存を断ち切った後にDB側で生成するようにする。
        name: str,
        application_client_id: str,
    ) -> None:
        """ContractProviderを追加する。"""
        try:
            with self.connection.cursor() as cur:
                cur.execute(
                    """--sql
                    INSERT INTO contract_providers (id, application_client_id, name)
                    VALUES (%(contract_provider_id)s, %(application_client_id)s, %(name)s)
                    """,
                    {
                        "contract_provider_id": str(contract_provider_id),
                        "application_client_id": application_client_id,
                        "name": name,
                    },
                )
        except psycopg.errors.UniqueViolation as e:
            raise ResourceDuplicatedError("ContractProvider already exists.") from e
