from uuid import UUID

import psycopg
from psycopg import Connection
from psycopg.rows import class_row
from src.__types.errors import ResourceDuplicatedError, ResourceNotFoundError
from src.__types.index import Tenant
from src.domain.entities.quota import Quota
from src.domain.entities.quota_allocation import QuotaAllocation, QuotaAllocationStatus
from src.tracer import tracer


class QuotaRepository:
    """クォータを管理するリポジトリ"""

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

    @tracer.capture_method
    def get(self) -> Quota:
        """クォータを取得する"""
        with self.connection.cursor(row_factory=class_row(QuotaAllocation)) as cur:
            cur.execute(
                """--sql
                SELECT
                    id, type, status, amount, used, valid_from_date, valid_to_date
                FROM quota_allocations
                WHERE tenant_id = %(tenant_id)s
                AND status != %(stats)s
                """,
                {
                    "tenant_id": self.tenant.id,
                    "stats": QuotaAllocationStatus.EXPIRED.value,
                },
            )
            if allocations := cur.fetchall():
                return Quota(allocations=allocations)
            return Quota(allocations=[])

    def lock_quota_allocation(self, allocation_id: UUID) -> QuotaAllocation:
        """指定されたIDのクォータ割り当てをロックして取得する"""
        with self.connection.cursor(row_factory=class_row(QuotaAllocation)) as cur:
            cur.execute(
                """--sql
                SELECT
                    id, type, status, amount, used, valid_from_date, valid_to_date
                FROM quota_allocations
                WHERE id = %(allocation_id)s
                AND tenant_id = %(tenant_id)s
                FOR UPDATE
                """,
                {
                    "allocation_id": allocation_id,
                    "tenant_id": self.tenant.id,
                },
            )
            if allocation := cur.fetchone():
                return allocation
            raise ResourceNotFoundError(
                f"Quota allocation with ID {allocation_id} not found for tenant {self.tenant.id}.",
            )

    def add_quota_allocation(self, allocation: QuotaAllocation, contract_id: str) -> None:
        """クォータ割り当てを追加する"""
        try:
            with self.connection.cursor() as cur:
                cur.execute(
                    """--sql
                    INSERT INTO quota_allocations (id, tenant_id, contract_id, type, status, amount, used, valid_from_date, valid_to_date)
                    VALUES (%(id)s, %(tenant_id)s, %(contract_id)s, %(type)s, %(status)s, %(amount)s, %(used)s, %(valid_from_date)s, %(valid_to_date)s)
                    """,
                    {
                        "id": allocation.id,
                        "tenant_id": self.tenant.id,
                        "contract_id": contract_id,
                        "type": allocation.type,
                        "status": allocation.status,
                        "amount": allocation.amount,
                        "used": allocation.used,
                        "valid_from_date": allocation.valid_from_date,
                        "valid_to_date": allocation.valid_to_date,
                    },
                )
        except psycopg.errors.UniqueViolation as e:
            raise ResourceDuplicatedError(
                f"QuotaAllocation for contract_id={contract_id}, "
                f"valid_from_date={allocation.valid_from_date}, "
                f"valid_to_date={allocation.valid_to_date} already exists.",
            ) from e
