import sys
import time
import os
import subprocess
import logging
import configparser  # Para leer el .ini
import glob          # Para buscar archivos existentes
import queue         # La cola de procesamiento
import threading     # Para el hilo de trabajo (worker)
from pathlib import Path  # Para auto-detectar Downloads
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

# --- 1. Leer la Configuración ---
config = configparser.ConfigParser()

# --- INICIO DE MODIFICACIÓN PARA PYINSTALLER ---
# Determina la ruta del script, ya sea como .py o como .exe compilado
if getattr(sys, 'frozen', False):
    # Si se ejecuta como .exe (empaquetado por PyInstaller)
    script_dir = os.path.dirname(sys.executable)
else:
    # Si se ejecuta como .py (normal)
    script_dir = os.path.dirname(os.path.realpath(__file__))
# --- FIN DE MODIFICACIÓN ---

# El resto del script continúa usando la variable 'script_dir'
config_path = os.path.join(script_dir, 'limpioo.print.ini')

if not os.path.exists(config_path):
    # (Nota: print() no funcionará en modo --windowed, pero es útil para debug)
    print(f"Error: No se encuentra 'limpioo.print.ini' en {script_dir}")
    sys.exit(1)
config.read(config_path)

settings = config['Settings']
SUMATRA_PATH = settings.get('SumatraPath')
PATRON_PREFIJO = settings.get('FilePrefix')
PATRON_SUFIJO = settings.get('FileSuffix')
PATRON_COMPLETO = settings.get('FilePattern')

# --- NUEVO: Auto-detectar carpeta Downloads ---
def get_downloads_folder():
    """Detecta automáticamente la carpeta Downloads del usuario"""
    if os.name == 'nt':  # Windows
        downloads_path = str(Path.home() / "Downloads")
        if os.path.exists(downloads_path):
            return downloads_path
    # Fallback: usar directorio del script
    return script_dir

# Obtener directorio a vigilar desde configuración o auto-detectar Downloads
DIRECTORIO_A_VIGILAR = settings.get('WatchDirectory', get_downloads_folder())

# Log de la configuración detectada
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - [%(threadName)s] - %(message)s',
    handlers=[
        logging.FileHandler(os.path.join(script_dir, 'limpioo.print.log'), encoding='utf-8')
    ]
)

logging.info("=== CONFIGURACIÓN DE LIMPIOO PRINT ===")
logging.info(f"Directorio del script: {script_dir}")
logging.info(f"Directorio a vigilar: {DIRECTORIO_A_VIGILAR}")
logging.info(f"SumatraPDF: {SUMATRA_PATH}")
logging.info(f"Patrón de archivos: {PATRON_PREFIJO}*{PATRON_SUFIJO}")
logging.info("=" * 50)

# --- 2. Configuración del Logging ---
LOG_FILE = os.path.join(script_dir, 'limpioo.print.log')
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - [%(threadName)s] - %(message)s',
    handlers=[
        logging.FileHandler(LOG_FILE, encoding='utf-8')
    ]
)

# --- 3. Cola de Procesamiento y Set de Control ---
processing_queue = queue.Queue()
archivos_en_cola = set()

# --- 4. Funciones de Lógica ---
def esperar_archivo_libre(ruta_pdf):
    nombre_archivo = os.path.basename(ruta_pdf)
    MAX_INTENTOS = 15
    DELAY_INTENTO = 1
    
    for i in range(MAX_INTENTOS):
        try:
            os.rename(ruta_pdf, ruta_pdf)
            logging.info(f"Archivo {nombre_archivo} está libre.")
            return True
        except FileNotFoundError:
            logging.error(f"Error: {nombre_archivo} desapareció mientras se esperaba.")
            return False
        except PermissionError:
            logging.warning(f"Archivo {nombre_archivo} bloqueado. Intento {i+1}/{MAX_INTENTOS}")
            time.sleep(DELAY_INTENTO)
        except Exception as e:
            logging.error(f"Error inesperado esperando {nombre_archivo}: {e}")
            return False
            
    logging.error(f"{nombre_archivo} sigue bloqueado tras {MAX_INTENTOS}s. Abortando.")
    return False

def imprimir_y_borrar(ruta_pdf):
    nombre_archivo = os.path.basename(ruta_pdf)
    
    if not os.path.exists(SUMATRA_PATH):
        logging.error(f"No se encontró SumatraPDF en: {SUMATRA_PATH}")
        return False

    if not esperar_archivo_libre(ruta_pdf):
        return False

    comando = [SUMATRA_PATH, "-print-to-default", "-silent", ruta_pdf]
    logging.info(f"Enviando a imprimir: {nombre_archivo}")
    try:
        time.sleep(1)
        subprocess.run(comando, check=True, capture_output=True, text=True)
        logging.info(f"¡Impresión de {nombre_archivo} enviada con éxito!")

        try:
            os.remove(ruta_pdf)
            logging.info(f"Archivo {nombre_archivo} eliminado con éxito.")
        except OSError as e:
            logging.warning(f"¡ADVERTENCIA! No se pudo eliminar {nombre_archivo}. Error: {e}")
        
        return True

    except subprocess.CalledProcessError as e:
        logging.error(f"ERROR al imprimir {nombre_archivo}. El archivo NO se eliminará.")
        logging.error(f"Detalle SumatraPDF: {e.stderr}")
    except Exception as e:
        logging.error(f"Error inesperado {nombre_archivo}. NO se eliminará. Error: {e}")
    
    return False

# --- 5. El Hilo de Trabajo (Worker) ---
def worker():
    """Toma tareas de la cola y las procesa una por una."""
    while True:
        try:
            ruta_pdf = processing_queue.get()
            if ruta_pdf is None: 
                break
            
            logging.info(f"Procesando desde cola: {os.path.basename(ruta_pdf)}")
            imprimir_y_borrar(ruta_pdf)
            
        except Exception as e:
            logging.critical(f"Error fatal en el worker: {e}")
        finally:
            processing_queue.task_done()
            archivos_en_cola.discard(ruta_pdf)

# --- 6. El Manejador de Watchdog (Handler) ---
class PDFHandler(FileSystemEventHandler):
    """Añade archivos a la cola si coinciden con el patrón."""
    
    def _coincide(self, ruta_archivo):
        nombre = os.path.basename(ruta_archivo)
        return nombre.startswith(PATRON_PREFIJO) and nombre.endswith(PATRON_SUFIJO)

    def _encolar(self, ruta_archivo):
        if ruta_archivo not in archivos_en_cola:
            archivos_en_cola.add(ruta_archivo)
            processing_queue.put(ruta_archivo)
            logging.info(f"Archivo añadido a la cola: {os.path.basename(ruta_archivo)}")

    def on_created(self, event):
        if not event.is_directory and self._coincide(event.src_path):
            logging.info(f"Detectado [on_created]: {event.src_path}")
            self._encolar(event.src_path)

    def on_moved(self, event):
        if not event.is_directory and self._coincide(event.dest_path):
            logging.info(f"Detectado [on_moved]: {event.dest_path}")
            self._encolar(event.dest_path)

# --- 7. Función Principal (Main) ---
if __name__ == "__main__":
    if not os.path.exists(SUMATRA_PATH):
        # Este log es crucial para saber por qué falla el exe
        logging.critical(f"Error Crítico: SumatraPDF no existe en {SUMATRA_PATH}. Saliendo.")
        sys.exit(1)
    
    threading.Thread(target=worker, daemon=True, name="PrintWorker").start()
    logging.info("Hilo 'PrintWorker' iniciado.")

    logging.info("Buscando archivos pendientes al inicio...")
    ruta_busqueda = os.path.join(DIRECTORIO_A_VIGILAR, PATRON_COMPLETO)
    archivos_pendientes = glob.glob(ruta_busqueda)
    
    if archivos_pendientes:
        logging.info(f"Encontrados {len(archivos_pendientes)} archivos pendientes. Encolando...")
        for ruta_pdf in archivos_pendientes:
            if ruta_pdf not in archivos_en_cola:
                archivos_en_cola.add(ruta_pdf)
                processing_queue.put(ruta_pdf)
    else:
        logging.info("No hay archivos pendientes. Listo.")

    event_handler = PDFHandler()
    observer = Observer()
    observer.schedule(event_handler, DIRECTORIO_A_VIGILAR, recursive=False)
    observer.start()
    logging.info(f"Supervisor iniciado en: {DIRECTORIO_A_VIGILAR}")

    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        logging.info("Deteniendo supervisor (Ctrl+C)...")
        observer.stop()
    
    observer.join()
    processing_queue.put(None) 
    logging.info("Supervisor detenido.")