import streamlit as st
import cloudinary
import cloudinary.uploader
import cloudinary.api
import requests
import io
import zipfile
import logging

# Configuración inicial de la página y logging
st.set_page_config(
    page_title="✂️ Cloudinary Smart Crop",
    page_icon="✂️",
    layout="wide"
)
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')


def init_cloudinary():
    """
    Inicializa Cloudinary usando las credenciales definidas en st.secrets.
    Realiza una limpieza inicial de recursos y registra el estado de inicialización.
    """
    if 'cloudinary_initialized' not in st.session_state:
        try:
            cloudinary.config(url=st.secrets['CLOUDINARY_URL'])
            st.session_state.cloudinary_initialized = True
            cleanup_cloudinary()  # Limpieza inicial de recursos
            logging.info("Cloudinary inicializado correctamente.")
        except Exception as e:
            st.error("Error: No se encontraron las credenciales de Cloudinary en secrets.toml")
            st.session_state.cloudinary_initialized = False
            logging.error(f"Error al inicializar Cloudinary: {e}")


def check_file_size(file, max_size_mb=10):
    """
    Verifica que el tamaño del archivo no exceda el límite especificado (en MB).
    """
    file.seek(0, 2)  # Mover al final del archivo
    file_size = file.tell() / (1024 * 1024)
    file.seek(0)  # Regresar al inicio
    return file_size <= max_size_mb


def cleanup_cloudinary():
    """
    Limpia todos los recursos almacenados en Cloudinary.
    """
    if not st.session_state.get('cloudinary_initialized', False):
        return
    try:
        result = cloudinary.api.resources()
        if 'resources' in result and result['resources']:
            public_ids = [resource['public_id'] for resource in result['resources']]
            if public_ids:
                cloudinary.api.delete_resources(public_ids)
                logging.info("Recursos de Cloudinary limpiados.")
    except Exception as e:
        st.error(f"Error al limpiar recursos: {e}")
        logging.error(f"Error en cleanup_cloudinary: {e}")


def process_image(image, width, height, gravity_option, dpr):
    """
    Procesa la imagen usando Cloudinary y retorna la imagen procesada.
    Reinicia el puntero del stream y utiliza el atributo 'name' para determinar
    si se debe preservar la transparencia en PNG.
    """
    if not st.session_state.get('cloudinary_initialized', False):
        st.error("Cloudinary no está inicializado correctamente")
        return None

    try:
        image.seek(0)
        image_name = getattr(image, 'name', '')
        if not check_file_size(image, 10):
            st.error(f"{image_name} excede el límite de 10MB")
            return None

        image_content = image.read()

        # Configurar la transformación para Cloudinary
        transformation = {
            "width": width,
            "height": height,
            "crop": "fill",
            "gravity": gravity_option,
            "quality": 100,
            "dpr": dpr,
        }
        if image_name.lower().endswith('.png'):
            transformation["flags"] = "preserve_transparency"
        else:
            transformation["flags"] = None

        response = cloudinary.uploader.upload(
            image_content,
            transformation=[transformation]
        )

        processed_url = response.get('secure_url')
        processed_image = requests.get(processed_url).content

        # Limpia el recurso procesado en Cloudinary
        cloudinary.api.delete_resources([response.get('public_id')])
        logging.info(f"Imagen {image_name} procesada correctamente.")
        return processed_image

    except Exception as e:
        st.error(f"Error procesando imagen: {e}")
        logging.error(f"Error en process_image para {image_name}: {e}")
        return None


def main():
    """
    Función principal que ejecuta la aplicación Streamlit.
    """
    init_cloudinary()

    st.title("✂️ Cloudinary Smart Crop")

    with st.expander("📌 Instrucciones de uso", expanded=True):
        st.markdown("""
        **Recorta y redimensiona imágenes inteligentemente con Cloudinary**

        **Formatos soportados:**
        ✅ PNG, JPG, JPEG, WEBP

        **Características principales:**
        - 🔍 Detección automática de rostros (opción 'face'/'faces')
        - 🖼️ Mantenimiento de transparencia en PNG
        - 📐 Redimensionado preciso con diferentes modos de gravedad
        - 🚀 Procesamiento por lotes y descarga en ZIP

        **Pasos para usar:**
        1. ⚙️ Configura dimensiones deseadas y parámetros de recorte
        2. 🎯 Selecciona el tipo de gravedad y el DPR (entre 1 y 3)
        3. 📤 Sube tus imágenes (máx. 10MB c/u)
        4. 🚀 Procesa y descarga los resultados
        """)

    # Parámetros de configuración
    col1, col2, col3, col4 = st.columns(4)
    with col1:
        width = st.number_input("Ancho (px)", value=1000, min_value=100, max_value=3000)
    with col2:
        height = st.number_input("Alto (px)", value=460, min_value=100, max_value=3000)
    with col3:
        gravity_option = st.selectbox(
            "Gravedad",
            ["auto", "center", "face", "faces", "north", "south", "east", "west"],
            help="Configura cómo se enfocará el recorte en la imagen"
        )
    with col4:
        dpr = st.number_input("DPR", value=3, min_value=1, max_value=3, step=1)

    # Carga de imágenes
    uploaded_files = st.file_uploader(
        "Sube tus imágenes (máx. 10MB por archivo)",
        type=['png', 'jpg', 'jpeg', 'webp'],
        accept_multiple_files=True
    )

    if uploaded_files:
        st.header("Vista Previa Original")
        cols = st.columns(3)
        original_images = []
        for idx, file in enumerate(uploaded_files):
            file_bytes = file.getvalue()
            original_images.append((file.name, file_bytes))
            with cols[idx % 3]:
                st.image(file_bytes, caption=file.name, use_column_width=True)

        if st.button("✨ Procesar Imágenes"):
            processed_images = []
            progress_bar = st.progress(0)
            total_images = len(original_images)

            for idx, (name, img_bytes) in enumerate(original_images):
                st.write(f"Procesando: {name}")
                img_io = io.BytesIO(img_bytes)
                with st.spinner(f"Procesando {name}..."):
                    processed = process_image(img_io, width, height, gravity_option, dpr)
                    if processed:
                        processed_images.append((name, processed))
                progress_bar.progress((idx + 1) / total_images)

            if processed_images:
                st.header("Resultados Finales")
                cols = st.columns(3)
                for idx, (name, img_bytes) in enumerate(processed_images):
                    with cols[idx % 3]:
                        st.image(img_bytes, caption=name, use_column_width=True)

                # Crear archivo ZIP con las imágenes procesadas
                zip_buffer = io.BytesIO()
                with zipfile.ZipFile(zip_buffer, 'w') as zip_file:
                    for name, img_bytes in processed_images:
                        zip_file.writestr(f"procesada_{name}", img_bytes)

                st.download_button(
                    label="📥 Descargar Todas",
                    data=zip_buffer.getvalue(),
                    file_name="imagenes_procesadas.zip",
                    mime="application/zip"
                )


if __name__ == "__main__":
    main()