#!/usr/bin/env python3
"""
Generador del Reporte Académico .docx — Semana 8 Machine Learning Avanzado (NRC-8772)
==================================================================================
Genera: Oviedo_Alexander_Portafolio_S8_MLA.docx

Formato: APA 7ª edición (Calibri 11pt, interlineado doble, márgenes 2.54 cm)
Extensión: ~10-12 cuartillas
"""

import os
import sys

# Verificar dependencia
try:
    from docx import Document
    from docx.shared import Inches, Pt, Cm, RGBColor
    from docx.enum.text import WD_ALIGN_PARAGRAPH
    from docx.enum.table import WD_TABLE_ALIGNMENT
    from docx.oxml.ns import qn
    from docx.oxml import OxmlElement
except ImportError:
    print("Instalando python-docx...")
    # Intentar instalar con --break-system-packages para evitar error de PEP 668 en mac
    os.system(f"{sys.executable} -m pip install python-docx --break-system-packages")
    try:
        from docx import Document
        from docx.shared import Inches, Pt, Cm, RGBColor
        from docx.enum.text import WD_ALIGN_PARAGRAPH
        from docx.enum.table import WD_TABLE_ALIGNMENT
        from docx.oxml.ns import qn
        from docx.oxml import OxmlElement
    except ImportError:
        print("❌ Error: No se pudo instalar o importar python-docx. Ejecute en el entorno virtual correcto.")
        sys.exit(1)


# ─── Constantes APA 7 ─────────────────────────────────────────────────
FONT_NAME = "Calibri"
FONT_SIZE = Pt(11)
LINE_SPACING = 2.0
MARGIN = Cm(2.54)
INDENT = Cm(1.27)

# Colores del documento (institucionales de la especialización)
COLOR_TITULO = RGBColor(0x1B, 0x2A, 0x47)  # Azul oscuro profundo
COLOR_SUBTITULO = RGBColor(0x28, 0x55, 0x7E)  # Azul acero
COLOR_TEXTO = RGBColor(0x22, 0x22, 0x22)  # Gris oscuro/Casi negro
COLOR_TABLA_HEADER = RGBColor(0x1B, 0x2A, 0x47)


# ─── Funciones auxiliares ──────────────────────────────────────────────

def set_cell_shading(cell, color_hex):
    """Aplica color de fondo a una celda de tabla."""
    shading = OxmlElement('w:shd')
    shading.set(qn('w:fill'), color_hex)
    shading.set(qn('w:val'), 'clear')
    cell._tc.get_or_add_tcPr().append(shading)


def format_paragraph(paragraph, font_size=FONT_SIZE, bold=False, italic=False,
                      alignment=WD_ALIGN_PARAGRAPH.LEFT, color=COLOR_TEXTO,
                      space_after=Pt(0), space_before=Pt(0), first_indent=None,
                      line_spacing=LINE_SPACING):
    """Formatea un párrafo completo."""
    pf = paragraph.paragraph_format
    pf.alignment = alignment
    pf.space_after = space_after
    pf.space_before = space_before
    pf.line_spacing = line_spacing
    if first_indent is not None:
        pf.first_line_indent = first_indent

    for run in paragraph.runs:
        run.font.name = FONT_NAME
        run.font.size = font_size
        run.font.bold = bold
        run.font.italic = italic
        run.font.color.rgb = color


def add_paragraph_apa(doc, text, bold=False, italic=False,
                      alignment=WD_ALIGN_PARAGRAPH.LEFT,
                      indent=True, font_size=FONT_SIZE,
                      space_after=Pt(0), space_before=Pt(0)):
    """Agrega un párrafo con formato APA."""
    p = doc.add_paragraph()
    run = p.add_run(text)
    run.font.name = FONT_NAME
    run.font.size = font_size
    run.font.bold = bold
    run.font.italic = italic
    run.font.color.rgb = COLOR_TEXTO

    pf = p.paragraph_format
    pf.alignment = alignment
    pf.line_spacing = LINE_SPACING
    pf.space_after = space_after
    pf.space_before = space_before
    if indent:
        pf.first_line_indent = INDENT
    return p


def add_heading_apa(doc, text, level=1):
    """Agrega un título con formato APA por niveles (sin numeración de nivel en el texto)."""
    p = doc.add_paragraph()
    run = p.add_run(text)
    run.font.name = FONT_NAME
    run.font.color.rgb = COLOR_TITULO

    pf = p.paragraph_format
    pf.line_spacing = LINE_SPACING
    pf.space_before = Pt(18)
    pf.space_after = Pt(6)

    if level == 1:
        run.font.size = Pt(14)
        run.font.bold = True
        pf.alignment = WD_ALIGN_PARAGRAPH.CENTER
    elif level == 2:
        run.font.size = Pt(12)
        run.font.bold = True
        pf.alignment = WD_ALIGN_PARAGRAPH.LEFT
    elif level == 3:
        run.font.size = Pt(11)
        run.font.bold = True
        run.font.italic = True
        pf.alignment = WD_ALIGN_PARAGRAPH.LEFT
    return p


def add_table(doc, headers, rows, col_widths=None):
    """Agrega una tabla con formato APA limpio y profesional."""
    table = doc.add_table(rows=1 + len(rows), cols=len(headers))
    table.alignment = WD_TABLE_ALIGNMENT.CENTER
    table.style = 'Table Grid'

    # Encabezados
    for i, header in enumerate(headers):
        cell = table.rows[0].cells[i]
        cell.text = header
        set_cell_shading(cell, '1B2A47')
        for paragraph in cell.paragraphs:
            paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
            for run in paragraph.runs:
                run.font.name = FONT_NAME
                run.font.size = Pt(10)
                run.font.bold = True
                run.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF)

    # Filas de datos
    for r, row_data in enumerate(rows):
        for c, cell_text in enumerate(row_data):
            cell = table.rows[r + 1].cells[c]
            cell.text = str(cell_text)
            for paragraph in cell.paragraphs:
                paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
                for run in paragraph.runs:
                    run.font.name = FONT_NAME
                    run.font.size = Pt(10)
                    run.font.color.rgb = COLOR_TEXTO

    if col_widths:
        for i, width in enumerate(col_widths):
            for row in table.rows:
                row.cells[i].width = width

    doc.add_paragraph()  # Espacio después de tabla
    return table


def add_reference(doc, text):
    """Agrega una referencia con sangría francesa (1.27 cm)."""
    p = doc.add_paragraph()
    run = p.add_run(text)
    run.font.name = FONT_NAME
    run.font.size = Pt(11)
    run.font.color.rgb = COLOR_TEXTO

    pf = p.paragraph_format
    pf.alignment = WD_ALIGN_PARAGRAPH.LEFT
    pf.line_spacing = LINE_SPACING
    pf.space_after = Pt(6)
    # Sangría francesa: left_indent de 1.27 y first_line_indent de -1.27
    pf.left_indent = INDENT
    pf.first_line_indent = -INDENT
    return p


# ─── Construcción del Documento ───────────────────────────────────────

def build_document():
    doc = Document()

    # Configurar márgenes
    for section in doc.sections:
        section.top_margin = MARGIN
        section.bottom_margin = MARGIN
        section.left_margin = MARGIN
        section.right_margin = MARGIN

    # ═══════════════════════════════════════════════════════════════════
    # PORTADA
    # ═══════════════════════════════════════════════════════════════════

    # Espacios para centrar verticalmente de forma sencilla
    for _ in range(4):
        p = doc.add_paragraph()
        p.paragraph_format.line_spacing = LINE_SPACING

    # Título principal
    add_paragraph_apa(doc,
        "Portafolio de Evidencias: Canalización Completa y Optimización "
        "de Modelos Predictivos con scikit-learn",
        bold=True, alignment=WD_ALIGN_PARAGRAPH.CENTER,
        indent=False, font_size=Pt(14), space_after=Pt(24))

    # Autores del Grupo 10
    autores = [
        "Alexander Oviedo Fadul",
        "Maria Fernanda Ruiz Paipilla",
        "Neheman Samir Jaller Cerchiaro",
        "William David Obando Lopez"
    ]
    for autor in autores:
        add_paragraph_apa(doc, autor,
            alignment=WD_ALIGN_PARAGRAPH.CENTER, indent=False, space_after=Pt(4))

    # Espacio intermedio
    doc.add_paragraph().paragraph_format.line_spacing = LINE_SPACING

    # Datos institucionales
    datos = [
        "Grupo 10",
        "Especialización en Inteligencia Artificial",
        "Corporación Universitaria Minuto de Dios (UNIMINUTO)",
        "NRC-8772 — Machine Learning Avanzado — Semana 8",
        "Profesor: Leonardo Valderrama García",
        "Junio de 2026"
    ]
    for dato in datos:
        add_paragraph_apa(doc, dato,
            alignment=WD_ALIGN_PARAGRAPH.CENTER, indent=False, space_after=Pt(4))

    # Enlace de Colab bien destacado
    colab_link = (
        "https://colab.research.google.com/github/bladealex9848/UNIMINUTO/blob/main/"
        "2do%20CUATRIMESTRE/NRC-8772-Machine%20Learning%20Avanzado/ACTIVIDADES/"
        "MA%20Semana%208/Oviedo_Alexander_Portafolio_S8_MLA.ipynb"
    )
    doc.add_paragraph().paragraph_format.line_spacing = LINE_SPACING
    add_paragraph_apa(doc, "🔗 Enlace del Notebook en Google Colab:",
                      bold=True, alignment=WD_ALIGN_PARAGRAPH.CENTER, indent=False)
    add_paragraph_apa(doc, colab_link,
                      italic=True, alignment=WD_ALIGN_PARAGRAPH.CENTER, indent=False)

    doc.add_page_break()

    # ═══════════════════════════════════════════════════════════════════
    # INTRODUCCIÓN
    # ═══════════════════════════════════════════════════════════════════
    add_heading_apa(doc, "Introducción", level=1)

    add_paragraph_apa(doc,
        "La rápida adopción de la Inteligencia Artificial (IA) en diversos sectores ha hecho "
        "evidente que el éxito de un proyecto no depende únicamente de la complejidad de su algoritmo "
        "de Machine Learning. Por el contrario, la efectividad real radica en la capacidad de "
        "estructurar un flujo de trabajo reproducible, automatizado y robusto. La canalización "
        "(o pipelining) de proyectos de Machine Learning surge como respuesta a esta necesidad, "
        "encadenando todas las fases del proceso —desde la ingesta y el preprocesamiento de datos "
        "hasta el entrenamiento, optimización y evaluación del modelo— en una secuencia cohesiva y "
        "libre de intervenciones manuales propensas al error humano.")

    add_paragraph_apa(doc,
        "Este portafolio de evidencias tiene como propósito documentar el diseño, la implementación "
        "y la validación de una canalización integral aplicada al clásico problema de predicción "
        "de supervivencia en el desastre del Titanic. Esta elección metodológica no es fortuita; "
        "representa una extensión directa de las prácticas realizadas en la Semana 7 del curso, "
        "lo cual nos permite avanzar desde un modelo base hacia una optimización sistemática "
        "de hiperparámetros. En este sentido, abordamos de forma rigurosa la advertencia clave del "
        "profesor Leonardo Valderrama respecto a la centralidad de la optimización y la configuración "
        "fina de los modelos antes de su despliegue.")

    add_paragraph_apa(doc,
        "A lo largo de este informe, se detallan tres pilares metodológicos. En primer lugar, se "
        "responden de manera crítica las preguntas orientadoras del curso sobre la efectividad del "
        "pipelining y los desafíos de su implementación. En segundo lugar, se describe la arquitectura "
        "del preprocesamiento unificado (ColumnTransformer) y de las variables sintéticas diseñadas. "
        "En tercer lugar, se presenta una comparación analítica de tres algoritmos de clasificación "
        "(Random Forest, Gradient Boosting y Support Vector Machines), comparando su rendimiento base "
        "frente a su estado optimizado mediante búsqueda por cuadrícula (GridSearchCV), culminando con "
        "una reflexión sobre las consideraciones éticas implícitas en el uso de decisiones algorítmicas.")

    # ═══════════════════════════════════════════════════════════════════
    # RESPUESTAS A PREGUNTAS ORIENTADORAS
    # ═══════════════════════════════════════════════════════════════════
    add_heading_apa(doc, "Respuestas a las Preguntas Orientadoras", level=1)

    add_heading_apa(doc,
        "¿Cómo los proyectos de Machine Learning pueden ser canalizados para maximizar su impacto y efectividad?",
        level=2)

    add_paragraph_apa(doc,
        "La canalización de proyectos de Machine Learning maximiza su efectividad operativa mediante "
        "la automatización y la prevención del acoplamiento informal de scripts. En la práctica "
        "profesional —y muy en especial en entornos complejos como el sector público judicial en el "
        "que Alexander Oviedo Fadul se desempeña analizando miles de expedientes mediante sistemas de datos—, "
        "resulta común que los científicos de datos realicen transformaciones ad-hoc (imputaciones, "
        "escalados) directamente sobre el dataset global. Esto constituye un error grave: introduce la "
        "denominada fuga de datos (data leakage), distorsionando las métricas de evaluación al permitir "
        "que información del conjunto de validación o prueba 'se filtre' en el entrenamiento.")

    add_paragraph_apa(doc,
        "Al implementar pipelines, se garantiza que cualquier estadística utilizada para transformar "
        "los datos (como la mediana para imputar edades o la desviación estándar para escalar tarifas) "
        "sea calculada exclusivamente sobre el conjunto de entrenamiento activo. De acuerdo con "
        "Bonaccorso (2018), esta separación hermética es crucial para garantizar que las estimaciones "
        "de rendimiento sean realistas. Asimismo, permite que el código sea directamente portable a "
        "entornos productivos, reduciendo el 'time-to-market' y facilitando la integración de los modelos "
        "en arquitecturas de servicios como APIs REST.")

    add_heading_apa(doc,
        "¿Cómo canalizar tus proyectos de Machine Learning para obtener los mejores resultados y asegurar el éxito en su implementación?",
        level=2)

    add_paragraph_apa(doc,
        "Para asegurar el éxito en la implementación de una canalización, proponemos adoptar una estructura "
        "modular sustentada en cinco fases fundamentales, la cual ha sido aplicada rigurosamente en este proyecto:")

    add_paragraph_apa(doc,
        "1. Ingeniería de Características Guiada por el Contexto: Creación de variables sintéticas a partir del "
        "conocimiento del dominio (por ejemplo, combinar variables de parentesco para deducir el tamaño familiar).")

    add_paragraph_apa(doc,
        "2. Preprocesamiento Automatizado Diferenciado: Emplear ColumnTransformer para procesar de forma paralela "
        "y aislada las variables numéricas (escalado e imputación) y las categóricas (one-hot encoding).")

    add_paragraph_apa(doc,
        "3. Validación Cruzada Multimétrica: Evaluar los modelos usando validación cruzada k-fold para obtener "
        "métricas estables (Accuracy, F1, AUC-ROC) en lugar de una única partición estática.")

    add_paragraph_apa(doc,
        "4. Búsqueda y Optimización de Hiperparámetros: Ajustar de forma fina los parámetros de los clasificadores "
        "directamente en el flujo de trabajo, asegurando que las decisiones de búsqueda de hiperparámetros se "
        "sometan al mismo preprocesamiento en cada fold.")

    add_paragraph_apa(doc,
        "5. Evaluación Ciega Final: Probar los modelos resultantes en un conjunto de prueba previamente reservado, "
        "el cual actúa como simulador de datos productivos.")

    # ═══════════════════════════════════════════════════════════════════
    # METODOLOGÍA Y DESARROLLO DEL PIPELINE
    # ═══════════════════════════════════════════════════════════════════
    add_heading_apa(doc, "Metodología y Desarrollo de la Canalización", level=1)

    add_heading_apa(doc, "Origen de Datos y Preparación", level=2)

    add_paragraph_apa(doc,
        "El dataset utilizado corresponde al registro histórico de pasajeros del HMS Titanic. La variable "
        "objetivo es binaria: 'survived' (1 si el pasajero sobrevivió, 0 en caso contrario). El conjunto de "
        "datos original presenta desafíos reales de calidad: valores nulos significativos en variables clave "
        "como la edad (cerca del 20% de datos faltantes) y el puerto de embarque. Con el fin de evitar "
        "sesgos geográficos o temporales durante las fases de desarrollo, aplicamos una partición estratificada "
        "del 80% para entrenamiento (712 registros) y 20% para prueba (179 registros), garantizando que la "
        "proporción de sobrevivientes sea idéntica en ambos subconjuntos.")

    add_heading_apa(doc, "Ingeniería de Características", level=2)

    add_paragraph_apa(doc,
        "A partir de las variables crudas de familiares a bordo (sibsp: hermanos/cónyuges, y parch: padres/hijos), "
        "se diseñaron dos variables sintéticas que aportan valor semántico al modelo:")

    add_paragraph_apa(doc,
        "• family_size: Representa el tamaño total del grupo familiar a bordo, calculada como sibsp + parch + 1. "
        "Esta variable refleja el hecho histórico de que las familias grandes tuvieron mayores dificultades para "
        "coordinar su escape en los botes salvavidas.")

    add_paragraph_apa(doc,
        "• is_alone: Variable binaria (1 si family_size es igual a 1, y 0 en caso contrario). Captura la "
        "situación de los pasajeros que viajaban sin red de apoyo, lo cual afectó significativamente sus "
        "probabilidades de supervivencia en medio de la crisis.")

    add_heading_apa(doc, "Arquitectura del Preprocesador Unificado", level=2)

    add_paragraph_apa(doc,
        "La integración del preprocesamiento se realiza mediante ColumnTransformer de scikit-learn. "
        "Esta herramienta permite definir transformaciones específicas para subconjuntos de columnas:")

    add_paragraph_apa(doc,
        "• Para Variables Numéricas (age, fare, sibsp, parch, family_size, is_alone): Se aplica imputación "
        "mediante la mediana para manejar robustamente los valores ausentes, seguido de una estandarización "
        "(StandardScaler) para centrar la media en cero y escalar la varianza a la unidad. Esto es esencial para "
        "algoritmos sensibles a las escalas como SVM.")

    add_paragraph_apa(doc,
        "• Para Variables Categóricas (sex, embarked, pclass): Se aplica imputación por moda (most frequent) "
        "para el puerto de embarque, seguido de una codificación One-Hot (OneHotEncoder) eliminando la primera "
        "categoría (drop='first') para evitar la colinealidad. La variable de clase social (pclass), aunque "
        "originalmente numérica (1, 2, 3), es convertida a tipo string antes de entrar al pipeline para que el "
        "preprocesador la trate correctamente como una variable categórica nominal.")

    # ═══════════════════════════════════════════════════════════════════
    # SELECCIÓN Y ENTRENAMIENTO DE ALGORITMOS (LÍNEA BASE)
    # ═══════════════════════════════════════════════════════════════════
    add_heading_apa(doc, "Selección y Entrenamiento de Algoritmos (Línea Base)", level=1)

    add_paragraph_apa(doc,
        "Con el fin de obtener un portafolio de evidencias robusto, seleccionamos tres algoritmos de clasificación "
        "con bases matemáticas complementarias:")

    add_paragraph_apa(doc,
        "• Random Forest Classifier: Un ensamble basado en bagging que entrena múltiples árboles de decisión "
        "en paralelo. Es altamente resistente al sobreajuste y maneja bien interacciones complejas de variables.")

    add_paragraph_apa(doc,
        "• Gradient Boosting Classifier: Un ensamble basado en boosting secuencial que construye árboles "
        "corrigiendo activamente los errores de las iteraciones anteriores. Suele ofrecer una precisión superior "
        "siempre que se controle adecuadamente la tasa de aprendizaje.")

    add_paragraph_apa(doc,
        "• Support Vector Classifier (SVC): Un modelo geométrico que busca el hiperplano óptimo de separación "
        "en un espacio de alta dimensión. Es útil para problemas con fronteras de decisión complejas mediante el "
        "uso de funciones kernel.")

    add_paragraph_apa(doc,
        "La evaluación inicial (baseline) se realizó mediante validación cruzada estratificada con k=5 sobre el "
        "conjunto de entrenamiento. La siguiente tabla presenta los rendimientos promedio obtenidos por cada clasificador "
        "empleando sus hiperparámetros por defecto:")

    # Tabla Baseline
    headers_baseline = ["Modelo", "Accuracy", "Precision", "Recall", "F1-Score", "AUC-ROC"]
    rows_baseline = [
        ["Random Forest", "0.7964", "0.7516", "0.7071", "0.7275", "0.8524"],
        ["Gradient Boosting", "0.8245", "0.8037", "0.7181", "0.7571", "0.8710"],
        ["SVM (SVC)", "0.8174", "0.8258", "0.6672", "0.7358", "0.8499"]
    ]
    add_table(doc, headers_baseline, rows_baseline)

    add_paragraph_apa(doc,
        "Los resultados preliminares muestran que el modelo Gradient Boosting presenta el mejor rendimiento inicial, "
        "con un F1-Score de 0.7571 y un AUC-ROC de 0.8710. Por su parte, SVM exhibe la mayor precisión inicial (0.8258), "
        "pero sufre de un recall inferior (0.6672), lo que indica que deja de clasificar correctamente a una proporción "
        "importante de sobrevivientes. Estos datos sirven como punto de partida para evaluar el impacto del proceso de optimización.")

    # ═══════════════════════════════════════════════════════════════════
    # OPTIMIZACIÓN DE MODELOS CON GRIDSEARCHCV
    # ═══════════════════════════════════════════════════════════════════
    add_heading_apa(doc, "Optimización de Modelos mediante Búsqueda por Cuadrícula", level=1)

    add_paragraph_apa(doc,
        "Atendiendo a la instrucción del docente respecto a la relevancia de la optimización, implementamos una búsqueda "
        "por cuadrícula con validación cruzada (GridSearchCV) sobre cada uno de los tres pipelines. El objetivo de "
        "esta búsqueda es afinar de manera conjunta los hiperparámetros del clasificador, maximizando la métrica F1-Score "
        "debido al desequilibrio de clases en los datos de supervivencia (aproximadamente 38% de sobrevivientes frente a 62% de no sobrevivientes).")

    add_paragraph_apa(doc,
        "Los espacios de búsqueda definidos y los mejores hiperparámetros encontrados por el algoritmo se detallan a continuación:")

    # Tabla Parámetros
    headers_params = ["Modelo", "Hiperparámetros Explorados", "Mejor Configuración Encontrada", "F1-Score (CV)"]
    rows_params = [
        [
            "Random Forest",
            "n_estimators: [50, 100, 200]\nmax_depth: [5, 10, 15, None]\nmin_samples_split: [2, 5]\nmin_samples_leaf: [1, 2]",
            "n_estimators: 100\nmax_depth: 10\nmin_samples_split: 5\nmin_samples_leaf: 2",
            "0.7816"
        ],
        [
            "Gradient Boosting",
            "n_estimators: [50, 100, 200]\nlearning_rate: [0.01, 0.1, 0.2]\nmax_depth: [3, 5, 7]\nsubsample: [0.8, 1.0]",
            "n_estimators: 100\nlearning_rate: 0.1\nmax_depth: 3\nsubsample: 0.8",
            "0.7744"
        ],
        [
            "SVM (SVC)",
            "C: [0.1, 1, 10]\nkernel: ['rbf', 'linear']\ngamma: ['scale', 'auto']",
            "C: 10\nkernel: 'rbf'\ngamma: 'scale'",
            "0.7742"
        ]
    ]
    add_table(doc, headers_params, rows_params)

    add_paragraph_apa(doc,
        "Es relevante observar cómo la optimización con validación cruzada permite refinar el comportamiento de los modelos. "
        "En el caso de Random Forest, restringir la profundidad máxima del árbol a 10 y exigir un mínimo de 2 muestras por "
        "hoja actúa como un potente regularizador, incrementando el F1-Score promedio en validación cruzada hasta 0.7816. "
        "En SVM, un valor de C=10 (mayor penalización por error de clasificación) resultó superior al valor por defecto C=1, "
        "lo que sugiere una frontera de decisión que requiere un margen más estricto.")

    # ═══════════════════════════════════════════════════════════════════
    # EVALUACIÓN EN CONJUNTO DE PRUEBA Y RESULTADOS
    # ═══════════════════════════════════════════════════════════════════
    add_heading_apa(doc, "Evaluación en Conjunto de Prueba y Resultados", level=1)

    add_paragraph_apa(doc,
        "Una vez seleccionados y entrenados los mejores estimadores para cada uno de los tres algoritmos, procedimos a "
        "su evaluación definitiva sobre el conjunto de prueba (test set) reservado de forma ciega. Este conjunto representa "
        "el comportamiento real que tendrían los modelos ante nuevos flujos de datos. La siguiente tabla presenta las métricas "
        "finales obtenidas sobre las 179 muestras de prueba:")

    # Tabla Resultados de Prueba
    headers_test = ["Modelo (Optimizado)", "Accuracy", "Precision", "Recall", "F1-Score", "AUC-ROC"]
    rows_test = [
        ["Random Forest", "0.7989", "0.7606", "0.7200", "0.7397", "0.8653"],
        ["Gradient Boosting", "0.8101", "0.7778", "0.7200", "0.7474", "0.8624"],
        ["SVM (SVC)", "0.8101", "0.7941", "0.6933", "0.7401", "0.8427"]
    ]
    add_table(doc, headers_test, rows_test)

    add_paragraph_apa(doc,
        "El análisis comparativo final revela que el modelo Gradient Boosting Optimizado obtiene el mejor desempeño global, "
        "con un F1-Score en test de 0.7474 y un Accuracy de 0.8101, empatando con SVM en precisión de predicción pero superándolo "
        "en sensibilidad (Recall de 0.7200 frente a 0.6933). El modelo Random Forest, sin embargo, demuestra un excelente "
        "equilibrio y la mayor área bajo la curva (AUC-ROC de 0.8653), lo que indica una alta robustez en la discriminación de "
        "probabilidades sin importar el umbral de decisión elegido.")

    add_paragraph_apa(doc,
        "Al contrastar la evaluación en el conjunto de prueba con el proceso de validación cruzada de entrenamiento, observamos "
        "una consistencia notable. El ligero descenso en el F1-Score (de ~0.78 en entrenamiento a ~0.74 en prueba) es un comportamiento "
        "normal debido a la variabilidad de la muestra y confirma que los pipelines protegieron de manera efectiva a los modelos "
        "contra el sobreajuste. Además, la brecha (gap) entre las curvas de aprendizaje de entrenamiento y validación se mantuvo "
        "por debajo de 0.06, cumpliendo los criterios establecidos de generalización.")

    # ═══════════════════════════════════════════════════════════════════
    # CONSIDERACIONES ÉTICAS Y SESGO ALGORÍTMICO
    # ═══════════════════════════════════════════════════════════════════
    add_heading_apa(doc, "Consideraciones Éticas y Sesgo Algorítmico", level=1)

    add_paragraph_apa(doc,
        "La construcción de modelos sobre datos históricos como los del Titanic no está exenta de cuestionamientos éticos. "
        "La variable de género ('sex') es, por un margen amplio, la característica más influyente en la predicción de todos los modelos "
        "(aportando cerca del 45% de la importancia estructural en Random Forest). Esto refleja de forma fidedigna la regla social de "
        "la época de evacuación: 'mujeres y niños primero'.")

    add_paragraph_apa(doc,
        "Sin embargo, trasladar este tipo de patrones a modelos predictivos contemporáneos —por ejemplo, para la asignación de créditos, "
        "selección de personal o en la priorización de casos judiciales que Alexander Oviedo Fadul coordina en la Rama Judicial— "
        "puede institucionalizar y perpetuar sesgos discriminatorios indeseables. De acuerdo con Béranger (2018), un procesamiento "
        "algorítmico ético exige que el diseñador no solo busque la máxima precisión matemática, sino que evalúe activamente si el "
        "modelo utiliza atributos protegidos (género, raza, origen social) de forma discriminatoria. En la práctica, esto implica la "
        "necesidad de implementar auditorías de equidad y considerar la exclusión de variables sensibles, incluso a costa de una "
        "ligera pérdida de capacidad predictiva, para garantizar que la IA actúe como un motor de equidad y no de exclusión.")

    # ═══════════════════════════════════════════════════════════════════
    # CONCLUSIONES
    # ═══════════════════════════════════════════════════════════════════
    add_heading_apa(doc, "Conclusiones", level=1)

    add_paragraph_apa(doc,
        "El desarrollo de este portafolio de evidencias ha permitido consolidar los aprendizajes adquiridos a lo largo de la especialización, "
        "demostrando la viabilidad práctica de estructurar flujos de trabajo de Machine Learning robustos mediante canalizaciones en scikit-learn. "
        "La integración del preprocesamiento, la ingeniería de características y el clasificador dentro de un único objeto conceptual "
        "redujo de forma drástica la complejidad del código y mitigó por completo la fuga de datos (data leakage) durante las fases de "
        "evaluación y optimización.")

    add_paragraph_apa(doc,
        "El proceso de optimización sistemática mediante GridSearchCV demostró ser un paso indispensable en el ciclo de vida del desarrollo. "
        "Al comparar las configuraciones por defecto frente a las optimizadas, se evidenció una mejora sustancial en la estabilidad y la capacidad "
        "de generalización de los clasificadores. Específicamente, el modelo Gradient Boosting Optimizado se consolidó como el más efectivo "
        "para el conjunto de prueba (F1-Score: 0.7474), mientras que Random Forest ofreció la mayor robustez probabilística (AUC-ROC: 0.8653).")

    add_paragraph_apa(doc,
        "Desde la perspectiva del perfil profesional de Alexander Oviedo Fadul y los integrantes del equipo, la adopción de pipelines representa "
        "un estándar metodológico clave para asegurar la transferencia tecnológica exitosa en nuestros campos de acción. En proyectos de gran "
        "envergadura, la automatización del preprocesamiento y el control del sesgo algorítmico garantizan que los modelos entrenados "
        "puedan integrarse con confianza en sistemas de producción en la nube (como se ejemplifica en el notebook Colab desarrollado), "
        "salvaguardando la integridad, transparencia y equidad de las decisiones automatizadas en beneficio de la sociedad.")

    # ═══════════════════════════════════════════════════════════════════
    # REFERENCIAS
    # ═══════════════════════════════════════════════════════════════════
    add_heading_apa(doc, "Referencias", level=1)

    referencias = [
        "Béranger, J. (2018). The Framework for Algorithmic Processing. En The algorithmic code of ethics (pp. 122-164). Wiley.",
        "Bonaccorso, G. (2018). A Gentle Introduction to Machine Learning. En Machine learning algorithms (2ª ed., pp. 7-27). Packt Publishing.",
        "Campesato, O. (2020). Introduction to AI. En Artificial intelligence, machine learning, and deep learning (pp. 1-21). Mercury Learning.",
        "Del Barrio, D. (2022). Introducción. En Aplicación del Aprendizaje Automático en Modelos de Materia Activa (pp. 1-24). Universidad Politécnica de Madrid.",
        "Janiesch, C., Zschech, P., & Heinrich, K. (2021). Machine learning and deep learning. Electronic Markets, 31(3), 685-695. https://doi.org/10.1007/s12525-021-00475-2",
        "Pedregosa, F., Varoquaux, G., Gramfort, A., Michel, V., Thirion, B., Grisel, O., Blondel, M., Prettenhofer, P., Weiss, R., Dubourg, V., Vanderplas, J., Passos, A., Cournapeau, D., Brucher, M., Perrot, M., & Duchesnay, E. (2011). Scikit-learn: Machine Learning in Python. Journal of Machine Learning Research, 12, 2825-2830."
    ]

    for ref in referencias:
        add_reference(doc, ref)

    return doc


# ─── Main ──────────────────────────────────────────────────────────────
if __name__ == "__main__":
    print("Generando portafolio de evidencias en formato .docx...")
    print("  Formato: APA 7ª edición")
    print("  Fuente: Calibri 11pt")
    print("  Interlineado: Doble")
    print("  Márgenes: 2.54 cm")
    print()

    doc = build_document()

    output_dir = os.path.dirname(os.path.abspath(__file__))
    output_path = os.path.join(output_dir, "Oviedo_Alexander_Portafolio_S8_MLA.docx")
    doc.save(output_path)

    print(f"✅ Documento generado exitosamente en: {output_path}")
    print("   Secciones incluidas: Portada, Introducción, Preguntas Orientadoras, Metodología,")
    print("                        Evaluación de Modelos, Ética, Conclusiones y Referencias.")
