"""Client MinIO pour opérations JSON."""

from __future__ import annotations

import io
import json
from typing import TYPE_CHECKING

from minio import Minio
from minio.error import S3Error
from pydantic import BaseModel

from .exceptions import MinIOError

if TYPE_CHECKING:
    from minio.datatypes import Object


class MinIOClient:
    """Client MinIO pour opérations JSON."""

    def __init__(
        self,
        endpoint: str,
        access_key: str,
        secret_key: str,
        bucket: str,
        secure: bool = False,
    ) -> None:
        """Initialise le client MinIO.

        Args:
            endpoint: Endpoint MinIO (ex: "localhost:9000").
            access_key: Clé d'accès.
            secret_key: Clé secrète.
            bucket: Nom du bucket.
            secure: Utiliser HTTPS si True.
        """
        self._bucket = bucket
        try:
            self._client = Minio(
                endpoint=endpoint,
                access_key=access_key,
                secret_key=secret_key,
                secure=secure,
            )
        except Exception as e:
            raise MinIOError(f"Échec de connexion à MinIO: {e}") from e

    def put_json(self, path: str, data: dict | BaseModel) -> None:
        """Écrit un JSON sur MinIO.

        Args:
            path: Chemin de l'objet (ex: "sim123/ocr/doc1.json").
            data: Données à sérialiser (dict ou Pydantic model).

        Raises:
            MinIOError: Si l'écriture échoue.
        """
        try:
            if isinstance(data, BaseModel):
                json_bytes = data.model_dump_json().encode("utf-8")
            else:
                json_bytes = json.dumps(data, ensure_ascii=False).encode("utf-8")

            data_stream = io.BytesIO(json_bytes)
            self._client.put_object(
                bucket_name=self._bucket,
                object_name=path,
                data=data_stream,
                length=len(json_bytes),
                content_type="application/json",
            )
        except S3Error as e:
            raise MinIOError(f"Échec d'écriture sur MinIO ({path}): {e}") from e
        except Exception as e:
            raise MinIOError(f"Erreur lors de l'écriture JSON ({path}): {e}") from e

    def get_json(self, path: str) -> dict:
        """Lit un JSON depuis MinIO.

        Args:
            path: Chemin de l'objet.

        Returns:
            Données désérialisées.

        Raises:
            MinIOError: Si la lecture échoue ou fichier inexistant.
        """
        try:
            response = self._client.get_object(
                bucket_name=self._bucket,
                object_name=path,
            )
            try:
                data = json.loads(response.read().decode("utf-8"))
                return data
            finally:
                response.close()
                response.release_conn()
        except S3Error as e:
            if e.code == "NoSuchKey":
                raise MinIOError(f"Objet inexistant sur MinIO: {path}") from e
            raise MinIOError(f"Échec de lecture sur MinIO ({path}): {e}") from e
        except json.JSONDecodeError as e:
            raise MinIOError(f"JSON invalide sur MinIO ({path}): {e}") from e
        except Exception as e:
            raise MinIOError(f"Erreur lors de la lecture JSON ({path}): {e}") from e

    def exists(self, path: str) -> bool:
        """Vérifie l'existence d'un objet.

        Args:
            path: Chemin de l'objet.

        Returns:
            True si l'objet existe, False sinon.
        """
        try:
            self._client.stat_object(
                bucket_name=self._bucket,
                object_name=path,
            )
            return True
        except S3Error as e:
            if e.code == "NoSuchKey":
                return False
            raise MinIOError(f"Échec de vérification sur MinIO ({path}): {e}") from e
        except Exception as e:
            raise MinIOError(f"Erreur lors de la vérification ({path}): {e}") from e

    def list_prefix(self, prefix: str) -> list[str]:
        """Liste les objets avec un préfixe donné.

        Args:
            prefix: Préfixe pour filtrer les objets.

        Returns:
            Liste des chemins d'objets correspondants.
        """
        try:
            objects: list[Object] = list(
                self._client.list_objects(
                    bucket_name=self._bucket,
                    prefix=prefix,
                    recursive=True,
                )
            )
            return [obj.object_name for obj in objects if obj.object_name]
        except S3Error as e:
            raise MinIOError(f"Échec de listage sur MinIO ({prefix}): {e}") from e
        except Exception as e:
            raise MinIOError(f"Erreur lors du listage ({prefix}): {e}") from e
