package com.escolapios.fctmongo.service;

import java.io.IOException;
import java.nio.file.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;

import com.escolapios.fctmongo.entity.Convenio;
import com.escolapios.fctmongo.entity.Documento;
import com.escolapios.fctmongo.repository.DocumentoRepository;
import com.escolapios.fctmongo.util.PdfConverter;
import com.escolapios.fctmongo.dto.GenerarDocumentoContext;
import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class DocumentoService {
    private final DocumentoRepository documentoRepository;
    private final TemplateService templateService; // 👈 Tu TemplateService limpio
    private final ConvenioService convenioService;
    public List<Documento> obtenerTodos() {
        return documentoRepository.findAll().stream()
                .sorted((d1, d2) -> d2.getFechaGeneracion().compareTo(d1.getFechaGeneracion()))
                .toList();
    }
    /**
     * 🟢 NUEVO MÉTODO PUENTE: Genera los bytes utilizando tu método obtenerConvenioConDetallesPorId
     * @param convenioId ID alfanumérico del convenio en MongoDB
     */
    public byte[] obtenerBytesBorradorConvenio(String convenioId) {
        try {
            // 1. ⚡ CLAVE: Invocamos tu método existente para obtener la entidad unificada Convenio
            Convenio convenioDto = convenioService.obtenerConvenioConDetallesPorId(convenioId);

            if (convenioDto == null) {
                System.err.println("❌ No se pudieron consolidar los detalles para el convenio ID: " + convenioId);
                return null;
            }

            // 2. Definimos una ruta base temporal para el motor de plantillas
            Path baseDir = Paths.get(System.getProperty("java.io.tmpdir"));

            // 3. 🔄 El record GenerarDocumentoContext ahora acepta la entidad unificada de forma nativa
            GenerarDocumentoContext ctx = new GenerarDocumentoContext(
                convenioDto, 
                "CONVENIO_MARCO", 
                "convenio.docx", 
                baseDir
            );

            // 4. Invocamos tu motor interno pasándole el record
            Documento documento = this.generarDocumento(ctx);
            
            if (documento != null) {
                // Retornamos el array de bytes de forma segura
                return documento.getBytes(); 
            }
            
        } catch (Exception e) {
            System.err.println("❌ Error al orquestar el contexto de aprobación con el Convenio: " + e.getMessage());
        }
        return null;
    }    

    /**
     * 1️⃣ FLUJO INDIVIDUAL (Convenio / Anexo 4): Genera un Word aislado por convenio en memoria y disco
     */
    public Documento generarDocumento(GenerarDocumentoContext ctx) throws IOException {               
        String tipoNormalizado = ctx.tipoDocumento().toUpperCase();
        
        // 🔄 Recuperamos tu nueva entidad unificada Convenio del record contextual
        Convenio dto = ctx.datos();       
        if (dto == null) {
            throw new IllegalArgumentException("Los datos del Convenio no pueden ser nulos");
        }
        
        // ⚡ CLAVE LOMBOK: Forzamos el aplanamiento de variables {{ ... }} antes de mandarlo a Apache POI
        dto.sincronizarCamposPlanosTemplate();

        System.out.println("====== [PASO 1: DocumentoService] ======");
        System.out.println("-> Tipo de documento: " + tipoNormalizado);
        System.out.println("-> ID Convenio a procesar: " + dto.getId());
        System.out.println("-> Nombre de Plantilla solicitada: " + ctx.plantilla());
        System.out.println("-> Empresa cargada: " + dto.getEmpresaNombre());
        System.out.println("-> Alumno cargado: " + dto.getAlumnoNombre());

        // 1. PASO EN MEMORIA: Procesamos el documento Word sin tocar el disco todavía
        System.out.println("-> Invocando procesamiento en memoria...");
        XWPFDocument docProcesado = templateService.procesarDocumentoEnMemoria(ctx);
        System.out.println("-> Procesamiento en memoria finalizado.");

        // 2. EVITAR SOBREESCRITURA: Nombre único amigable
        var listaAlumnos = dto.getAlumnos();
        String prefijoFichero = "empresa";
        if (listaAlumnos != null && !listaAlumnos.isEmpty()) {
            var primerAlumno = listaAlumnos.get(0);
            if (primerAlumno != null && primerAlumno.getNombre() != null) {
                prefijoFichero = primerAlumno.getNombre().toLowerCase().trim().replaceAll("\\s+", "_");
            }
        }
                                
        String extensionLimpia = ctx.plantilla().replace(".docx", "").replace(".DOCX", "");
        String nuevoNombreFichero = extensionLimpia + "_" + prefijoFichero + "_" + dto.getId() + ".docx";
        Path destino = ctx.baseDir().resolve(nuevoNombreFichero);
        System.out.println("-> Destino físico del archivo: " + destino.toAbsolutePath());

        // 3. PASO FÍSICO: Escritura a disco
        System.out.println("-> Escribiendo archivo a disco...");
        templateService.escribirDocumentoADisco(docProcesado, destino);
        System.out.println("-> Archivo escrito correctamente en disco.");

        // 4. CONVERSIÓN A PDF
        System.out.println("-> Convirtiendo a PDF...");
        Path pdf = PdfConverter.convertirDocxAPdf(destino);              
        System.out.println("-> PDF generado en: " + pdf.toAbsolutePath());
        
        
        // 5. CONSTRUCCIÓN DE LA ENTIDAD DOCUMENTO (Asignamos las rutas para que getBytes() funcione)
        Documento documento = new Documento();
        documento.setConvenioId(dto.getId()); 
        documento.setAlumnos(new ArrayList<>(listaAlumnos != null ? listaAlumnos : List.of())); 
        documento.setTipo(tipoNormalizado);
        documento.setNombreFichero(nuevoNombreFichero.replace(".docx", ".pdf"));
        documento.setRuta(destino.toString());
        documento.setRutaPdf(pdf.toString()); // 👈 ¡ESTA RUTA ES LA CLAVE! Tu getBytes() la usará para leer el PDF
        documento.setFechaGeneracion(LocalDateTime.now());

        System.out.println("====== [FIN: DocumentoService] ======\n");


        return documento;

    }
    /**
     * 📊 ANEXO 5 (Opción B): Genera un ÚNICO documento Word masivo clonando las filas de la tabla
     */
    public Documento generarDocumentoColectivoAnexo5(
            List<Convenio> listaConvenios, // 🔄 Cambiado a List<Convenio> nativo de MongoDB
            String plantilla, 
            String curso,
            Path baseDir
    ) throws IOException {               
            
            System.out.println("====== [PASO 2: DocumentoService Colectivo] ======");
            
            // 1. Cargamos la plantilla base limpia del Anexo 5
            org.springframework.core.io.ClassPathResource resource = 
                new org.springframework.core.io.ClassPathResource("plantillas/" + plantilla);
            XWPFDocument docProcesado = new XWPFDocument(resource.getInputStream());

            // 2. PROCESAMIENTO DE TABLA: Pasamos el documento y la lista al motor de clonación
            System.out.println("-> Iniciando clonación e inyección de filas dinámicas...");
            
            // ⚡ NOTA: Asegúrate de que la firma interna de 'procesarTablaAnexo5' reciba también List<Convenio>
            templateService.procesarTablaAnexo5(docProcesado, listaConvenios, curso);
            System.out.println("-> Inyección dinámica completada con éxito.");

            // 3. EVITAR SOBREESCRITURAS: Fabricamos un nombre de archivo único para el curso escolar
            String cursoLimpio = curso != null ? curso.replace("/", "-").trim() : "general";
            String nuevoNombreFichero = "ANEXO5_Curso_" + cursoLimpio + "_" + System.currentTimeMillis() + ".docx";
            Path destino = baseDir.resolve(nuevoNombreFichero);
            System.out.println("-> Guardando archivo unificado en: " + destino.toAbsolutePath());

            // 4. Escritura física final a disco
            templateService.escribirDocumentoADisco(docProcesado, destino);

            // 5. Conversión a formato PDF
            Path pdf = PdfConverter.convertirDocxAPdf(destino);              
            
            // 6. Construcción de metadatos históricos colectivos en memoria (Sin persistencia duplicada)
            Documento documento = new Documento();
            documento.setConvenioId("COLECTIVO"); 
            documento.setAlumnos(new ArrayList<>()); // El array histórico de alumnos se llena en el controlador si lo deseas
            documento.setTipo("ANEXO_5");
            documento.setNombreFichero(nuevoNombreFichero.replace(".docx", ".pdf"));
            documento.setRuta(destino.toString());
            documento.setRutaPdf(pdf.toString());
            documento.setFechaGeneracion(LocalDateTime.now());
            documento.setRutaPdf(pdf.toString()); // 👈 ¡ESTA RUTA ES LA CLAVE! Tu getBytes() la usará para leer el PDF

            System.out.println("====== [FIN: DocumentoService Colectivo] ======\n");
            
            // 🟢 RETORNAMOS EL OBJETO DIRECTO EN MEMORIA SIN GUARDAR EN REPOSITORIO
            return documento;
    }

    // Métodos de descargas y búsquedas estándar...
    public Resource descargar(String documentoId) throws Exception {
            Documento documento = documentoRepository.findById(documentoId).orElseThrow();
            return new UrlResource(Paths.get(documento.getRuta()).toUri());
    }

    public Resource descargarPdf(String documentoId) throws Exception {
            Documento documento = documentoRepository.findById(documentoId).orElseThrow();
            return new UrlResource(Paths.get(documento.getRutaPdf()).toUri());
    }

    public List<Documento> buscarPorAlumno(String alumnoId) { return documentoRepository.findByAlumnos_Id(alumnoId); }
    public List<Documento> buscarPorConvenio(String convenioId) { return documentoRepository.findByConvenioId(convenioId); }
    
        // 🟢 RECUERDA: Añade la inyección en la cabecera de tu servicio (ej: mediante constructor o @Autowired)
    // private final Anexo6Processor anexo6Processor;

    /**
     * 📝 ANEXO 6 SERVICE: Orquesta el Plan de Formación individualizado de un alumno.
     * Invoca explícitamente al procesador de tablas de RAs de MongoDB.
     */
    public Documento generarDocumentoAnexo6(GenerarDocumentoContext ctx) throws IOException {               
        String tipoNormalizado = ctx.tipoDocumento().toUpperCase();
        
        // 🔄 Cambiado a tu entidad unificada Convenio
        Convenio dto = ctx.datos();
        if (dto == null) {
            throw new IllegalArgumentException("Los datos del Convenio no pueden ser nulos");
        }
        
        // ⚡ CLAVE LOMBOK: Forzamos el aplanamiento de variables {{ ... }} para el Word
        dto.sincronizarCamposPlanosTemplate();

        System.out.println("\n⚙️ ===== [PASO 3: DocumentoService Anexo 6] =====");
        System.out.println("  -> Iniciando orquestación física del Plan de Formación...");
        System.out.println("  -> Nombre de la plantilla base obligatoria: " + ctx.plantilla());
        
        // 🟢 CORRECCIÓN DE LOG: Extraemos el alumno principal para auditar sus RAs reales
        var primerAlumno = (dto.getAlumnos() != null && !dto.getAlumnos().isEmpty()) ? dto.getAlumnos().get(0) : null;
        
        if (primerAlumno == null) {
            System.out.println("  -> ⚠️ ALERTA: No hay alumnos vinculados en este convenio para el Anexo 6.");
        } else if (primerAlumno.getResultadosAprendizaje() == null || primerAlumno.getResultadosAprendizaje().isEmpty()) {
            System.out.println("  -> ⚠️ ALERTA NO-SQL: El array 'resultadosAprendizaje' del ALUMNO (" + primerAlumno.getNombre() + ") viene VACÍO.");
        } else {
            System.out.println("  -> 🎯 RAs localizados en el ALUMNO listos para inyectar: " + primerAlumno.getResultadosAprendizaje().size());
        }

        // 🟢 ⚡ ESTA ES LA CLAVE: Si tu TemplateService internamente no usa los procesadores,
        // ejecútalo aquí de forma manual pasándole el mapa que creas en caliente o deja que tu
        // arquitectura use un Factory/List de procesadores. Si tienes inyectado el bean:
        // java.util.Map<String, Object> variablesEspecificas = new java.util.HashMap<>();
        // anexo6Processor.procesarDatosEspecificos(ctx, variablesEspecificas);
        // (Nota: Si tu templateService internamente ya tiene la lista de todos los procesadores 
        // y los busca por String, entonces saltará solo dentro de 'procesarDocumentoEnMemoriaAnexo6').

        // 1. Procesamiento en memoria
        System.out.println("  -> Llamando al motor de Apache POI en memoria (procesarDocumentoEnMemoriaAnexo6)...");
        XWPFDocument docProcesado = templateService.procesarDocumentoEnMemoriaAnexo6(ctx);
        System.out.println("  -> Copia modificada en memoria generada con éxito.");

        // 2. NOMBRE ÚNICO: Usamos el nombre del alumno aislado para el fichero físico
        String nombreAlumno = "alumno_sin_nombre";
        if (primerAlumno != null && primerAlumno.getNombre() != null) {
            nombreAlumno = primerAlumno.getNombre().toLowerCase().trim().replaceAll("\\s+", "_");
        }
        
        String nuevoNombreFichero = "ANEXO6_" + nombreAlumno + "_" + dto.getId() + "_" + System.currentTimeMillis() + ".docx";
        Path destino = ctx.baseDir().resolve(nuevoNombreFichero);
        System.out.println("  -> Destino del Word en el disco del servidor: " + destino.toAbsolutePath());

        // 3. PASO FÍSICO: Escritura a disco y conversión limpia a PDF
        System.out.println("  -> Escribiendo fichero físico .docx a disco...");
        templateService.escribirDocumentoADisco(docProcesado, destino);
        
        System.out.println("  -> Invoking PdfConverter...");
        Path pdf = PdfConverter.convertirDocxAPdf(destino);              
        System.out.println("  -> PDF final consolidado en: " + pdf.toAbsolutePath());
        
        // 4. CONSTRUCCIÓN DE LA ENTIDAD DOCUMENTO (Sin persistencia duplicada)
        Documento documento = new Documento();
        documento.setConvenioId(dto.getId()); 
        documento.setAlumnos(new ArrayList<>(dto.getAlumnos() != null ? dto.getAlumnos() : List.of())); 
        documento.setTipo(tipoNormalizado);
        documento.setNombreFichero(nuevoNombreFichero.replace(".docx", ".pdf"));
        documento.setRuta(destino.toString());
        documento.setRutaPdf(pdf.toString());
        documento.setFechaGeneracion(LocalDateTime.now());

        System.out.println("⚙️ ===== [FIN: DocumentoService Anexo 6] =====\n");
        
        return documento;
    }

    /**
     * 📝 ANEXO 8 SERVICE: Orquesta la Ficha de Seguimiento Periódico individualizada de un alumno.
     * Invoca al procesador de tablas de evaluación de RAs, escribe los archivos a disco y
     * retorna la entidad construida en memoria.
     */
    public Documento generarDocumentoAnexo8(GenerarDocumentoContext ctx) throws IOException {               
        String tipoNormalizado = ctx.tipoDocumento().toUpperCase();
        
        // 🔄 Cambiado a tu entidad unificada Convenio
        Convenio dto = ctx.datos();
        if (dto == null) {
            throw new IllegalArgumentException("Los datos del Convenio no pueden ser nulos");
        }
        
        // ⚡ CLAVE LOMBOK: Forzamos el aplanamiento de variables {{ ... }} para el Word
        dto.sincronizarCamposPlanosTemplate();

        System.out.println("\n⚙️ ===== [PASO 3: DocumentoService Anexo 8] =====");
        System.out.println("  -> Iniciando orquestación de la Ficha de Seguimiento Periódico...");
        System.out.println("  -> Nombre de la plantilla base cargada: " + ctx.plantilla());
        
        // 🟢 CORRECCIÓN DE LOG: Extraemos el alumno principal para auditar sus RAs reales de seguimiento
        var primerAlumno = (dto.getAlumnos() != null && !dto.getAlumnos().isEmpty()) ? dto.getAlumnos().get(0) : null;
        
        if (primerAlumno == null) {
            System.out.println("  -> ⚠️ ALERTA: No hay alumnos vinculados en este convenio para el Anexo 8.");
        } else if (primerAlumno.getResultadosAprendizaje() == null || primerAlumno.getResultadosAprendizaje().isEmpty()) {
            // 🟢 Ahora busca y valida en: primerAlumno.getResultadosAprendizaje()
            System.out.println("  -> ⚠️ ALERTA NO-SQL: El array 'resultadosAprendizaje' viene vacío para el ALUMNO (" + primerAlumno.getNombre() + ") en el Anexo 8.");
        } else {
            System.out.println("  -> 🎯 RAs localizados en el ALUMNO listos para el seguimiento: " + primerAlumno.getResultadosAprendizaje().size());
        }

        // 1. PROCESAMIENTO EN MEMORIA
        System.out.println("  -> Llamando al motor de Apache POI en memoria (procesarDocumentoEnMemoriaAnexo8)...");
        XWPFDocument docProcesado = templateService.procesarDocumentoEnMemoriaAnexo8(ctx);
        System.out.println("  -> Copia modificada en memoria de la Ficha generada con éxito.");

        // 2. NOMBRE ÚNICO DE ARCHIVO: Formateamos el nombre del alumno para evitar caracteres raros
        String nombreAlumno = "alumno_sin_nombre";
        if (primerAlumno != null && primerAlumno.getNombre() != null) {
            nombreAlumno = primerAlumno.getNombre().toLowerCase().trim().replaceAll("\\s+", "_");
        }
        
        String nuevoNombreFichero = "ANEXO8_" + nombreAlumno + "_" + dto.getId() + "_" + System.currentTimeMillis() + ".docx";
        Path destino = ctx.baseDir().resolve(nuevoNombreFichero);
        System.out.println("  -> Destino del Word de Seguimiento en el servidor: " + destino.toAbsolutePath());

        // 3. PASO FÍSICO: Escritura binaria final a disco y conversión automática a PDF
        System.out.println("  -> Escribiendo fichero físico .docx a disco...");
        templateService.escribirDocumentoADisco(docProcesado, destino);
        
        System.out.println("  -> Invocando convertidor de formatos PdfConverter...");
        Path pdf = PdfConverter.convertirDocxAPdf(destino);              
        System.out.println("  -> PDF de seguimiento consolidado en: " + pdf.toAbsolutePath());
        
        // 4. CONSTRUCCIÓN DE LA ENTIDAD DOCUMENTO (Sin persistencia duplicada)
        Documento documento = new Documento();
        documento.setConvenioId(dto.getId()); 
        documento.setAlumnos(new ArrayList<>(dto.getAlumnos() != null ? dto.getAlumnos() : List.of())); 
        documento.setTipo(tipoNormalizado);
        documento.setNombreFichero(nuevoNombreFichero.replace(".docx", ".pdf"));
        documento.setRuta(destino.toString());
        documento.setRutaPdf(pdf.toString());
        documento.setFechaGeneracion(LocalDateTime.now());

        System.out.println("⚙️ ===== [FIN: DocumentoService Anexo 8] =====\n");

        return documento;
    }

    /**
     * 📝 ANEXO 9 SERVICE: Orquesta la Ficha de Valoración Final individualizada de un alumno.
     * Invoca al procesador de tablas de evaluación de RAs para el Anexo 9, escribe los archivos a disco y
     * retorna la entidad construida en memoria.
     */
    public Documento generarDocumentoAnexo9(GenerarDocumentoContext ctx) throws IOException {               
        String tipoNormalizado = ctx.tipoDocumento().toUpperCase();
        
        // 🔄 Cambiado a tu entidad unificada Convenio
        Convenio dto = ctx.datos();
        if (dto == null) {
            throw new IllegalArgumentException("Los datos del Convenio no pueden ser nulos");
        }
        
        // ⚡ CLAVE LOMBOK: Forzamos el aplanamiento de variables {{ ... }} para el Word antes de enviarlo a Apache POI
        dto.sincronizarCamposPlanosTemplate();

        System.out.println("\n⚙️ ===== [PASO 3: DocumentoService Anexo 9] =====");
        System.out.println("  -> Enrutando orquestación de la Ficha de Valoración Final...");
        System.out.println("  -> Nombre de la plantilla base cargada: " + ctx.plantilla());
        
        // 🟢 CORRECCIÓN DE LOG: Extraemos el alumno principal para auditar sus RAs reales de valoración final
        var primerAlumno = (dto.getAlumnos() != null && !dto.getAlumnos().isEmpty()) ? dto.getAlumnos().get(0) : null;
        
        if (primerAlumno == null) {
            System.out.println("  -> ⚠️ ALERTA: No hay alumnos vinculados en este convenio para el Anexo 9.");
        } else if (primerAlumno.getResultadosAprendizaje() == null || primerAlumno.getResultadosAprendizaje().isEmpty()) {
            // 🟢 Ahora busca y valida en: primerAlumno.getResultadosAprendizaje()
            System.out.println("  -> ⚠️ ALERTA NO-SQL: El array 'resultadosAprendizaje' viene vacío para el ALUMNO (" + primerAlumno.getNombre() + ") en el Anexo 9.");
        } else {
            System.out.println("  -> 🎯 RAs localizados en el ALUMNO listos para la valoración: " + primerAlumno.getResultadosAprendizaje().size());
        }

        // 1. PROCESAMIENTO EN MEMORIA: Abrimos la plantilla y rellenamos cabeceras y tabla del Anexo 9
        System.out.println("  -> Llamando al motor de Apache POI en memoria (procesarDocumentoEnMemoriaAnexo9)...");
        XWPFDocument docProcesado = templateService.procesarDocumentoEnMemoriaAnexo9(ctx);
        System.out.println("  -> Copia modificada en memoria de la Ficha de Valoración generada con éxito.");

        // 2. NOMBRE ÚNICO DE ARCHIVO: Formateamos el nombre del alumno para evitar caracteres raros
        String nombreAlumno = "alumno_sin_nombre";
        if (primerAlumno != null && primerAlumno.getNombre() != null) {
            nombreAlumno = primerAlumno.getNombre().toLowerCase().trim().replaceAll("\\s+", "_");
        }
        
        String nuevoNombreFichero = "ANEXO9_" + nombreAlumno + "_" + dto.getId() + "_" + System.currentTimeMillis() + ".docx";
        Path destino = ctx.baseDir().resolve(nuevoNombreFichero);
        System.out.println("  -> Destino del Word de Valoración en el servidor: " + destino.toAbsolutePath());

        // 3. PASO FÍSICO: Escritura binaria final a disco y conversión automática a PDF
        System.out.println("  -> Escribiendo fichero físico .docx a disco...");
        templateService.escribirDocumentoADisco(docProcesado, destino);
        
        System.out.println("  -> Invocando convertidor de formatos PdfConverter...");
        Path pdf = PdfConverter.convertirDocxAPdf(destino);              
        System.out.println("  -> PDF de valoración consolidado en: " + pdf.toAbsolutePath());
        
        // 4. CONSTRUCCIÓN DE LA ENTIDAD DOCUMENTO (Sin persistencia duplicada)
        Documento documento = new Documento();
        documento.setConvenioId(dto.getId()); 
        documento.setAlumnos(new ArrayList<>(dto.getAlumnos() != null ? dto.getAlumnos() : List.of())); 
        documento.setTipo(tipoNormalizado);
        documento.setNombreFichero(nuevoNombreFichero.replace(".docx", ".pdf"));
        documento.setRuta(destino.toString());
        documento.setRutaPdf(pdf.toString());
        documento.setFechaGeneracion(LocalDateTime.now());

        System.out.println("⚙️ ===== [FIN: DocumentoService Anexo 9] =====\n");
        
        return documento;
    }


}