from botocore.exceptions import ClientError
from src.__types.index import UsageEventRequestPayload
from src.infrastructure.clients.s3_client import S3Client
from src.logger import logger
from src.settings import Settings

settings = Settings()


class UsageEventRequestStorage:
    """使用イベントリクエストのストレージ"""

    def __init__(self, client: S3Client) -> None:
        if not settings.usage_event_storage_name:
            raise ValueError("usage_event_storage_name is required in settings")

        self.storage_name = settings.usage_event_storage_name
        self.client = client

    def upload(self, usage_event: UsageEventRequestPayload) -> str | None:
        """使用イベントを一時保存用ストレージにアップロードする

        Returns:
            アップロードしたオブジェクトのキー。すでに同じオブジェクトが存在する場合は None を返す。

        """
        key = f"{usage_event.tenant.id}/{usage_event.client.id}/{usage_event.idempotency_key}.json"

        try:
            self.client.upload_object(
                bucket_name=self.storage_name,
                key=key,
                body=usage_event.model_dump_json().encode("utf-8"),
                if_none_match=True,  # 冪等性を保つため、既存ファイルがない場合にのみアップロードする
            )
        except ClientError as e:
            if e.response["Error"]["Code"] in ["PreconditionFailed", "ConditionalRequestConflict"]:
                # すでに同じオブジェクトが存在する場合は無視する
                logger.debug(f"Object '{key}' already exists. Skipping upload.")
                return None
            raise

        return key

    def get(self, key: str) -> UsageEventRequestPayload | None:
        """使用イベントを一時保存用ストレージから取得する"""
        try:
            content = self.client.get_object(
                bucket_name=self.storage_name,
                key=key,
            )
        except ClientError as e:
            if e.response["Error"]["Code"] == "NoSuchKey":
                logger.warning(f"Object '{key}' not found. Skipping.")
                return None
            raise

        return UsageEventRequestPayload.model_validate_json(content)

    def delete(self, key: str) -> None:
        """使用イベントを一時保存用ストレージから削除する"""
        try:
            self.client.delete_object(
                bucket_name=self.storage_name,
                key=key,
            )
        except ClientError as e:
            if e.response["Error"]["Code"] == "NoSuchKey":
                logger.warning(f"Object '{key}' not found. Skipping.")
                return
            logger.error(f"Failed to delete object '{key}': {e}")  # ログに記録するが、処理自体は成功したと見なす
