"""
Client MinIO/S3 asincrono per il worker.

Percorso EML raw:  pecflow/tenants/{tenant_id}/mailboxes/{mailbox_id}/raw/{uid}.eml
Percorso allegati: pecflow/tenants/{tenant_id}/mailboxes/{mailbox_id}/attachments/{msg_id}/{filename}
"""

import io
import logging
from functools import lru_cache

from miniopy_async import Minio

from app.config import get_settings

logger = logging.getLogger(__name__)
settings = get_settings()


@lru_cache(maxsize=1)
def get_minio_client() -> Minio:
    """Restituisce l'istanza singleton del client MinIO."""
    return Minio(
        endpoint=settings.minio_endpoint,
        access_key=settings.minio_access_key,
        secret_key=settings.minio_secret_key,
        secure=settings.minio_use_ssl,
    )


async def upload_eml(
    tenant_id: str,
    mailbox_id: str,
    uid: int,
    eml_bytes: bytes,
) -> str:
    """
    Carica un raw EML su MinIO e restituisce il percorso oggetto.

    Percorso: tenants/{tenant_id}/mailboxes/{mailbox_id}/raw/{uid}.eml
    """
    client = get_minio_client()
    bucket = settings.minio_bucket
    object_path = f"tenants/{tenant_id}/mailboxes/{mailbox_id}/raw/{uid}.eml"

    try:
        data_stream = io.BytesIO(eml_bytes)
        await client.put_object(
            bucket_name=bucket,
            object_name=object_path,
            data=data_stream,
            length=len(eml_bytes),
            content_type="message/rfc822",
        )
        logger.debug(f"EML caricato: s3://{bucket}/{object_path} ({len(eml_bytes)} bytes)")
        return object_path
    except Exception as e:
        logger.error(f"Errore upload EML {object_path}: {e}")
        raise


async def upload_attachment(
    tenant_id: str,
    mailbox_id: str,
    message_id: str,
    filename: str,
    content: bytes,
    content_type: str = "application/octet-stream",
) -> str:
    """
    Carica un allegato su MinIO e restituisce il percorso oggetto.

    Percorso: tenants/{tenant_id}/mailboxes/{mailbox_id}/attachments/{message_id}/{filename}

    Args:
        tenant_id:    UUID del tenant
        mailbox_id:   UUID della casella
        message_id:   UUID del messaggio a cui appartiene l'allegato
        filename:     nome file dell'allegato (usato nel path)
        content:      byte del file
        content_type: MIME type del file

    Returns:
        Percorso oggetto su MinIO (senza il nome bucket)
    """
    client = get_minio_client()
    bucket = settings.minio_bucket

    # Sanitizza il filename per evitare path traversal
    safe_filename = _sanitize_filename(filename)
    object_path = (
        f"tenants/{tenant_id}/mailboxes/{mailbox_id}"
        f"/attachments/{message_id}/{safe_filename}"
    )

    try:
        data_stream = io.BytesIO(content)
        await client.put_object(
            bucket_name=bucket,
            object_name=object_path,
            data=data_stream,
            length=len(content),
            content_type=content_type,
        )
        logger.debug(
            f"Allegato caricato: s3://{bucket}/{object_path} "
            f"({len(content)} bytes, {content_type})"
        )
        return object_path
    except Exception as e:
        logger.error(f"Errore upload allegato {object_path}: {e}")
        raise


def _sanitize_filename(filename: str) -> str:
    """
    Sanitizza il nome file per uso sicuro come path MinIO.

    Rimuove caratteri pericolosi mantenendo l'estensione originale.
    """
    import re
    # Rimuovi path separators e null bytes
    safe = filename.replace("/", "_").replace("\\", "_").replace("\x00", "")
    # Rimuovi caratteri non ASCII e di controllo
    safe = re.sub(r"[^\w.\-() ]", "_", safe, flags=re.UNICODE)
    # Limita la lunghezza (MinIO ha limite 1024 chars per object name)
    if len(safe) > 200:
        # Mantieni estensione
        parts = safe.rsplit(".", 1)
        if len(parts) == 2:
            safe = parts[0][:196] + "." + parts[1]
        else:
            safe = safe[:200]
    return safe or "attachment"


async def upload_outbound_eml(
    tenant_id: str,
    mailbox_id: str,
    message_id: str,
    eml_bytes: bytes,
) -> str:
    """
    Carica il raw EML di un messaggio outbound su MinIO.

    Percorso: tenants/{tenant_id}/mailboxes/{mailbox_id}/outbound/{message_id}.eml

    Args:
        tenant_id:  UUID del tenant
        mailbox_id: UUID della casella mittente
        message_id: UUID del messaggio
        eml_bytes:  byte del raw EML

    Returns:
        Percorso oggetto su MinIO (senza bucket name)
    """
    client = get_minio_client()
    bucket = settings.minio_bucket
    object_path = (
        f"tenants/{tenant_id}/mailboxes/{mailbox_id}/outbound/{message_id}.eml"
    )

    try:
        import io as _io
        data_stream = _io.BytesIO(eml_bytes)
        await client.put_object(
            bucket_name=bucket,
            object_name=object_path,
            data=data_stream,
            length=len(eml_bytes),
            content_type="message/rfc822",
        )
        logger.debug(
            f"EML outbound caricato: s3://{bucket}/{object_path} "
            f"({len(eml_bytes)} bytes)"
        )
        return object_path
    except Exception as e:
        logger.error(f"Errore upload EML outbound {object_path}: {e}")
        raise


async def ensure_bucket_exists() -> None:
    """Verifica che il bucket MinIO esista, altrimenti lo crea."""
    client = get_minio_client()
    bucket = settings.minio_bucket
    try:
        found = await client.bucket_exists(bucket)
        if not found:
            await client.make_bucket(bucket)
            logger.info(f"Bucket MinIO creato: {bucket}")
        else:
            logger.debug(f"Bucket MinIO esistente: {bucket}")
    except Exception as e:
        logger.warning(f"Impossibile verificare/creare bucket MinIO: {e}")
