"""Client S3 en lecture seule pour le bucket source de production."""

from __future__ import annotations

import json
from pathlib import Path
from typing import TYPE_CHECKING

from minio import Minio
from minio.error import S3Error

from .exceptions import MinIOError

if TYPE_CHECKING:
    from minio.datatypes import Object


class SourceClient:
    """Client S3 en lecture seule pour le bucket source de production."""

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

        Args:
            endpoint: Endpoint S3 (ex: "s3.eu-west-par.io.cloud.ovh.net").
            access_key: Clé d'accès.
            secret_key: Clé secrète.
            bucket: Nom du bucket source.
            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 au bucket source: {e}") from e

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

        Args:
            path: Chemin de l'objet.

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

        Raises:
            MinIOError: Si la lecture échoue.
        """
        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:
            raise MinIOError(f"Échec de lecture sur le bucket source ({path}): {e}") from e
        except json.JSONDecodeError as e:
            raise MinIOError(f"JSON invalide sur le bucket source ({path}): {e}") from e
        except Exception as e:
            raise MinIOError(f"Erreur lors de la lecture JSON ({path}): {e}") from e

    def get_bytes(self, path: str) -> bytes:
        """Lit des données binaires depuis le bucket source.

        Args:
            path: Chemin de l'objet.

        Returns:
            Contenu binaire de l'objet.

        Raises:
            MinIOError: Si la lecture échoue.
        """
        try:
            response = self._client.get_object(
                bucket_name=self._bucket,
                object_name=path,
            )
            try:
                return response.read()
            finally:
                response.close()
                response.release_conn()
        except S3Error as e:
            raise MinIOError(f"Échec de lecture sur le bucket source ({path}): {e}") from e
        except Exception as e:
            raise MinIOError(f"Erreur lors de la lecture binaire ({path}): {e}") from e

    def download_file(self, path: str, local_path: Path) -> None:
        """Télécharge un objet vers un fichier local.

        Args:
            path: Chemin de l'objet dans le bucket.
            local_path: Chemin local de destination.

        Raises:
            MinIOError: Si le téléchargement échoue.
        """
        try:
            self._client.fget_object(
                bucket_name=self._bucket,
                object_name=path,
                file_path=str(local_path),
            )
        except S3Error as e:
            raise MinIOError(f"Échec de téléchargement depuis le bucket source ({path}): {e}") from e
        except Exception as e:
            raise MinIOError(f"Erreur lors du téléchargement ({path}): {e}") from e

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

        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 le bucket source ({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 le bucket source ({prefix}): {e}") from e
        except Exception as e:
            raise MinIOError(f"Erreur lors du listage ({prefix}): {e}") from e
