from pathlib import Path

from docx import Document
from docx.enum.section import WD_SECTION
from docx.enum.table import WD_ALIGN_VERTICAL, WD_CELL_VERTICAL_ALIGNMENT, WD_TABLE_ALIGNMENT
from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_BREAK
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
from docx.shared import Cm, Inches, Pt, RGBColor


ROOT = Path(__file__).resolve().parent.parent
DOCS = ROOT / "documentacion"
ASSETS = ROOT / "public" / "assets"
SHOTS = DOCS / "recursos"
OUTPUT = DOCS / "Manual_institucional_y_de_uso_NexoCaja.docx"

MAROON = "5A091C"
RED = "AD0F2F"
PALE_RED = "F8EFF2"
PALE_GOLD = "FFF5DD"
GOLD = "E4A62E"
GREEN = "287E61"
TEXT = "2A1B1F"
MUTED = "6C6265"
LIGHT = "D9D9D9"
WHITE = "FFFFFF"


def set_cell_shading(cell, fill):
    tc_pr = cell._tc.get_or_add_tcPr()
    shd = tc_pr.find(qn("w:shd"))
    if shd is None:
        shd = OxmlElement("w:shd")
        tc_pr.append(shd)
    shd.set(qn("w:fill"), fill)


def set_cell_margins(cell, top=100, start=120, bottom=100, end=120):
    tc = cell._tc
    tc_pr = tc.get_or_add_tcPr()
    tc_mar = tc_pr.first_child_found_in("w:tcMar")
    if tc_mar is None:
        tc_mar = OxmlElement("w:tcMar")
        tc_pr.append(tc_mar)
    for margin, value in (("top", top), ("start", start), ("bottom", bottom), ("end", end)):
        node = tc_mar.find(qn(f"w:{margin}"))
        if node is None:
            node = OxmlElement(f"w:{margin}")
            tc_mar.append(node)
        node.set(qn("w:w"), str(value))
        node.set(qn("w:type"), "dxa")


def set_table_borders(table, color=LIGHT, size="6"):
    tbl_pr = table._tbl.tblPr
    borders = tbl_pr.find(qn("w:tblBorders"))
    if borders is None:
        borders = OxmlElement("w:tblBorders")
        tbl_pr.append(borders)
    for edge in ("top", "left", "bottom", "right", "insideH", "insideV"):
        tag = qn(f"w:{edge}")
        node = borders.find(tag)
        if node is None:
            node = OxmlElement(f"w:{edge}")
            borders.append(node)
        node.set(qn("w:val"), "single")
        node.set(qn("w:sz"), size)
        node.set(qn("w:color"), color)


def set_repeat_table_header(row):
    tr_pr = row._tr.get_or_add_trPr()
    tbl_header = OxmlElement("w:tblHeader")
    tbl_header.set(qn("w:val"), "true")
    tr_pr.append(tbl_header)


def set_column_width(cell, inches):
    tc_pr = cell._tc.get_or_add_tcPr()
    tc_w = tc_pr.find(qn("w:tcW"))
    if tc_w is None:
        tc_w = OxmlElement("w:tcW")
        tc_pr.append(tc_w)
    tc_w.set(qn("w:w"), str(int(inches * 1440)))
    tc_w.set(qn("w:type"), "dxa")


def set_run_font(run, name="Aptos", size=None, bold=None, color=None, italic=None):
    run.font.name = name
    run._element.get_or_add_rPr().rFonts.set(qn("w:ascii"), name)
    run._element.get_or_add_rPr().rFonts.set(qn("w:hAnsi"), name)
    if size is not None:
        run.font.size = Pt(size)
    if bold is not None:
        run.bold = bold
    if italic is not None:
        run.italic = italic
    if color:
        run.font.color.rgb = RGBColor.from_string(color)


def add_page_number(paragraph):
    paragraph.alignment = WD_ALIGN_PARAGRAPH.RIGHT
    run = paragraph.add_run("Página ")
    set_run_font(run, size=8.5, color=MUTED)
    fld_char1 = OxmlElement("w:fldChar")
    fld_char1.set(qn("w:fldCharType"), "begin")
    instr_text = OxmlElement("w:instrText")
    instr_text.set(qn("xml:space"), "preserve")
    instr_text.text = " PAGE "
    fld_char2 = OxmlElement("w:fldChar")
    fld_char2.set(qn("w:fldCharType"), "end")
    run._r.append(fld_char1)
    run._r.append(instr_text)
    run._r.append(fld_char2)


def add_footer(section):
    footer = section.footer
    footer.is_linked_to_previous = False
    p = footer.paragraphs[0]
    p.clear()
    p.alignment = WD_ALIGN_PARAGRAPH.RIGHT
    r = p.add_run("NexoCaja  |  Proyecto MYPES Digitales Cusco     ")
    set_run_font(r, size=8.5, color=MUTED)
    add_page_number(p)


def add_alt_text(shape, text):
    inline = shape._inline
    doc_pr = inline.docPr
    doc_pr.set("descr", text)
    doc_pr.set("title", text)


def add_picture(doc, path, width, alt, align=WD_ALIGN_PARAGRAPH.CENTER, space_after=6):
    p = doc.add_paragraph()
    p.alignment = align
    p.paragraph_format.space_after = Pt(space_after)
    shape = p.add_run().add_picture(str(path), width=width)
    add_alt_text(shape, alt)
    return p


def add_title(doc, text, subtitle=None):
    p = doc.add_paragraph(style="Title")
    p.alignment = WD_ALIGN_PARAGRAPH.LEFT
    p.paragraph_format.space_after = Pt(9)
    r = p.add_run(text)
    set_run_font(r, name="Aptos Display", size=28, bold=True, color="000000")
    if subtitle:
        sp = doc.add_paragraph()
        sp.paragraph_format.space_after = Pt(14)
        sr = sp.add_run(subtitle)
        set_run_font(sr, size=12, color=MUTED)
    return p


def add_heading(doc, text, level=1):
    p = doc.add_paragraph(style=f"Heading {level}")
    p.paragraph_format.keep_with_next = True
    p.paragraph_format.space_before = Pt(10 if level == 1 else 8)
    p.paragraph_format.space_after = Pt(5)
    for r in p.runs:
        set_run_font(r, name="Aptos Display", color="000000")
    return p


def add_body(doc, text, bold_lead=None, space_after=6):
    p = doc.add_paragraph()
    p.paragraph_format.space_after = Pt(space_after)
    p.paragraph_format.line_spacing = 1.08
    p.paragraph_format.widow_control = True
    if bold_lead:
        r = p.add_run(bold_lead)
        set_run_font(r, size=10.5, bold=True, color=TEXT)
    r = p.add_run(text)
    set_run_font(r, size=10.5, color=TEXT)
    return p


def add_bullets(doc, items, level=0):
    for item in items:
        p = doc.add_paragraph(style="List Bullet" if level == 0 else "List Bullet 2")
        p.paragraph_format.space_after = Pt(3)
        p.paragraph_format.line_spacing = 1.05
        r = p.add_run(item)
        set_run_font(r, size=10.2, color=TEXT)


def add_numbered(doc, items):
    for index, item in enumerate(items, start=1):
        p = doc.add_paragraph()
        p.paragraph_format.space_after = Pt(4)
        p.paragraph_format.line_spacing = 1.05
        p.paragraph_format.left_indent = Inches(0.28)
        p.paragraph_format.first_line_indent = Inches(-0.2)
        r = p.add_run(f"{index}. {item}")
        set_run_font(r, size=10.2, color=TEXT)


def add_table(doc, headers, rows, widths=None, header_fill=MAROON, font_size=9.3):
    table = doc.add_table(rows=1, cols=len(headers))
    table.alignment = WD_TABLE_ALIGNMENT.CENTER
    table.autofit = False
    table.rows[0]._tr.get_or_add_trPr()
    set_repeat_table_header(table.rows[0])
    for idx, header in enumerate(headers):
        cell = table.rows[0].cells[idx]
        set_cell_shading(cell, header_fill)
        set_cell_margins(cell, top=100, bottom=100)
        cell.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER
        p = cell.paragraphs[0]
        p.alignment = WD_ALIGN_PARAGRAPH.LEFT
        p.paragraph_format.space_after = Pt(0)
        r = p.add_run(str(header))
        set_run_font(r, size=9.2, bold=True, color=WHITE)
        if widths:
            set_column_width(cell, widths[idx])
    for row_index, values in enumerate(rows):
        cells = table.add_row().cells
        for idx, value in enumerate(values):
            cell = cells[idx]
            cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
            set_cell_margins(cell, top=100, bottom=100)
            if row_index % 2 == 1:
                set_cell_shading(cell, "FBF7F8")
            p = cell.paragraphs[0]
            p.paragraph_format.space_after = Pt(0)
            p.paragraph_format.line_spacing = 1.0
            r = p.add_run(str(value))
            set_run_font(r, size=font_size, color=TEXT)
            if widths:
                set_column_width(cell, widths[idx])
    set_table_borders(table)
    doc.add_paragraph().paragraph_format.space_after = Pt(2)
    return table


def add_section_label(doc, text):
    p = doc.add_paragraph()
    p.paragraph_format.space_after = Pt(5)
    r = p.add_run(text.upper())
    set_run_font(r, size=8.5, bold=True, color=RED)
    return p


def add_caption(doc, text):
    p = doc.add_paragraph()
    p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    p.paragraph_format.space_after = Pt(8)
    r = p.add_run(text)
    set_run_font(r, size=8.5, italic=True, color=MUTED)
    return p


def add_page_break(doc):
    doc.add_page_break()


doc = Document()
section = doc.sections[0]
section.page_width = Inches(8.5)
section.page_height = Inches(11)
section.top_margin = Inches(0.65)
section.bottom_margin = Inches(0.62)
section.left_margin = Inches(0.78)
section.right_margin = Inches(0.78)
section.header_distance = Inches(0.25)
section.footer_distance = Inches(0.28)
add_footer(section)

styles = doc.styles
normal = styles["Normal"]
normal.font.name = "Aptos"
normal._element.rPr.rFonts.set(qn("w:ascii"), "Aptos")
normal._element.rPr.rFonts.set(qn("w:hAnsi"), "Aptos")
normal.font.size = Pt(10.5)
normal.font.color.rgb = RGBColor.from_string(TEXT)
normal.paragraph_format.space_after = Pt(5)

for style_name, size in (("Title", 28), ("Heading 1", 20), ("Heading 2", 14), ("Heading 3", 11.5)):
    style = styles[style_name]
    style.font.name = "Aptos Display"
    style._element.rPr.rFonts.set(qn("w:ascii"), "Aptos Display")
    style._element.rPr.rFonts.set(qn("w:hAnsi"), "Aptos Display")
    style.font.size = Pt(size)
    style.font.bold = True
    style.font.color.rgb = RGBColor.from_string("000000")
    style_ppr = style._element.get_or_add_pPr()
    style_borders = style_ppr.find(qn("w:pBdr"))
    if style_borders is not None:
        style_ppr.remove(style_borders)

doc.core_properties.title = "Manual institucional y de uso de NexoCaja"
doc.core_properties.subject = "Presentación y uso del piloto local para MYPE de Cusco"
doc.core_properties.author = "Proyecto NexoCaja"
doc.core_properties.keywords = "NexoCaja, MYPES Digitales, Cusco, flujo de caja, crédito"

# Cover
logo_line = doc.add_paragraph()
logo_line.alignment = WD_ALIGN_PARAGRAPH.CENTER
logo_line.paragraph_format.space_after = Pt(28)
pic1 = logo_line.add_run().add_picture(str(ASSETS / "mypes-digitales.png"), width=Inches(2.25))
add_alt_text(pic1, "Logotipo del Proyecto MYPES Digitales")
logo_line.add_run("     ")
pic2 = logo_line.add_run().add_picture(str(ASSETS / "gerepro-cusco.png"), width=Inches(3.1))
add_alt_text(pic2, "Logotipo de la Gerencia Regional de Producción del Gobierno Regional Cusco")

cover_spacer = doc.add_paragraph()
cover_spacer.paragraph_format.space_after = Pt(30)

p = doc.add_paragraph(style="Title")
p.alignment = WD_ALIGN_PARAGRAPH.LEFT
p.paragraph_format.space_after = Pt(14)
r = p.add_run("Manual institucional y de uso de NexoCaja")
set_run_font(r, name="Aptos Display", size=32, bold=True, color="000000")

p = doc.add_paragraph()
p.paragraph_format.space_after = Pt(24)
r = p.add_run("Piloto local para planificación financiera y evaluación de créditos de micro y pequeñas empresas")
set_run_font(r, size=15, color=MUTED)

cover_table = add_table(
    doc,
    ["Documento", "Ámbito", "Versión"],
    [["Presentación y manual de uso", "Cusco, Perú", "Piloto 0.3"]],
    widths=[2.6, 2.1, 1.6],
    header_fill=MAROON,
    font_size=10,
)

p = doc.add_paragraph()
p.paragraph_format.space_before = Pt(12)
p.paragraph_format.space_after = Pt(0)
r = p.add_run("Setiembre de 2026")
set_run_font(r, size=11, bold=True, color=RED)
p = doc.add_paragraph()
p.paragraph_format.space_after = Pt(0)
r = p.add_run("Documento para presentación, demostración y capacitación del piloto")
set_run_font(r, size=10, color=MUTED)

add_page_break(doc)

# Page 2
add_section_label(doc, "Presentación institucional")
add_title(doc, "Propósito del proyecto", "Una lectura ejecutiva antes de utilizar la herramienta")
add_body(
    doc,
    "NexoCaja reúne el registro diario, la proyección de caja y la evaluación de créditos en una aplicación local. Su objetivo es ayudar a una persona emprendedora o una MYPE a ordenar información básica y anticipar decisiones de liquidez con una interfaz visual y breve.",
)
add_body(
    doc,
    "El piloto utiliza la identidad del Proyecto MYPES Digitales y de la Gerencia Regional de Producción del Gobierno Regional Cusco. La solución está preparada para demostración y validación local. Antes de un despliegue institucional con información real se requiere revisión contable, evaluación de seguridad y pruebas con usuarios.",
)
add_heading(doc, "Qué permite hacer", 2)
add_bullets(doc, [
    "Registrar ingresos, egresos y categorías del negocio.",
    "Proyectar doce meses de caja con políticas de cobro y pago.",
    "Estimar IGV, aportes, renta y compromisos pendientes con tasas configurables.",
    "Simular créditos y generar un cronograma de cuota, interés y amortización.",
    "Importar y exportar movimientos y cuentas mediante hojas de cálculo.",
    "Presentar resultados con indicadores, alertas, tablas y gráficos.",
])
add_heading(doc, "Usuarios previstos", 2)
add_table(
    doc,
    ["Perfil", "Finalidad", "Límite principal"],
    [
        ["Usuario MYPE", "Gestiona el plan financiero de su negocio", "Solo ve información propia"],
        ["Gestor", "Brinda soporte y administra cuentas normales", "No accede a información financiera"],
        ["Cuenta maestra", "Administra gestores y usuarios", "No sustituye el perfil financiero"],
    ],
    widths=[1.45, 3.0, 1.85],
)
add_body(doc, "Alcance del documento. ", bold_lead="", space_after=0)
doc.paragraphs[-1].clear()
r = doc.paragraphs[-1].add_run("Alcance del documento. ")
set_run_font(r, size=10.5, bold=True, color=TEXT)
r = doc.paragraphs[-1].add_run("Las páginas siguientes explican cómo iniciar el piloto, recorrer cada módulo y presentar su valor sin atribuirle funciones que todavía no forman parte del alcance.")
set_run_font(r, size=10.5, color=TEXT)

add_page_break(doc)

# Page 3
add_section_label(doc, "Inicio rápido")
add_title(doc, "Preparación de la demostración", "Iniciadores, accesos y condiciones de uso")
add_heading(doc, "Iniciar según el equipo", 2)
add_table(
    doc,
    ["Equipo", "Archivo", "Uso"],
    [
        ["Windows", "INICIAR_NEXOCAJA.bat", "Abre el piloto en este equipo"],
        ["Mac", "INICIAR_NEXOCAJA_MAC.command", "Abre el piloto en este equipo"],
        ["iPhone o iPad desde Windows", "INICIAR_NEXOCAJA_IPHONE_IPAD.bat", "Muestra una dirección para Safari"],
        ["iPhone o iPad desde Mac", "COMPARTIR_NEXOCAJA_IPHONE_IPAD.command", "Muestra una dirección para Safari"],
    ],
    widths=[1.55, 2.95, 1.8],
    font_size=8.9,
)
add_body(doc, "En iPhone y iPad la aplicación se ejecuta en la PC o Mac anfitriona. Ambos equipos deben estar conectados a la misma red Wi-Fi. En Safari puede utilizarse Compartir y Agregar a inicio para dejar un acceso similar al de una aplicación.")
add_heading(doc, "Cuentas de demostración", 2)
add_table(
    doc,
    ["Perfil", "Usuario", "Contraseña"],
    [
        ["Cuenta maestra", "master", "demo1234"],
        ["Gestor", "gestor", "demo1234"],
        ["Usuario financiero", "usuario", "demo1234"],
    ],
    widths=[2.5, 1.9, 1.9],
    header_fill=RED,
)
add_heading(doc, "Antes de presentar", 2)
add_bullets(doc, [
    "Confirmar que Node.js 22.13 o superior está instalado.",
    "Abrir el iniciador y mantener su ventana activa durante la demostración.",
    "Comprobar los tres accesos y revisar que la base demo contiene información de ejemplo.",
    "Usar una red de confianza si la demostración incluye iPhone o iPad.",
    "No introducir datos financieros reales en una sesión pública de demostración.",
])

add_page_break(doc)

# Page 4
add_section_label(doc, "Acceso")
add_title(doc, "Inicio de sesión", "Una entrada simple para cada perfil")
add_picture(doc, SHOTS / "01_inicio_sesion.png", Inches(6.45), "Pantalla de inicio de sesión de NexoCaja", space_after=4)
add_caption(doc, "Pantalla inicial con accesos de demostración y presencia institucional")
add_numbered(doc, [
    "Seleccionar Iniciar sesión.",
    "Escribir el usuario corto o el correo de la cuenta.",
    "Introducir la contraseña y pulsar Ingresar.",
    "Si la contraseña es temporal, crear una nueva antes de continuar.",
])
add_body(doc, "La opción Crear cuenta está disponible en el modo de prueba para registrar un usuario financiero. En un uso controlado, las altas deben seguir el procedimiento definido por el equipo responsable.")

add_page_break(doc)

# Page 5
add_section_label(doc, "Usuario MYPE")
add_title(doc, "Panel financiero", "Indicadores para explicar la situación proyectada")
add_picture(doc, SHOTS / "02_panel_financiero.png", Inches(5.0), "Panel financiero con indicadores y gráfico de evolución de caja", space_after=4)
add_caption(doc, "El panel resume caja, ventas, utilidad y meses en riesgo")
add_table(
    doc,
    ["Indicador", "Lectura sugerida"],
    [
        ["Saldo final", "Caja estimada al cierre del periodo"],
        ["Ventas", "Ventas anuales sin IGV utilizadas por la proyección"],
        ["Utilidad neta", "Resultado estimado después de costos, gastos e impuesto anual"],
        ["Meses en riesgo", "Cantidad de meses cuyo saldo final es negativo"],
    ],
    widths=[1.7, 4.6],
)
add_body(doc, "Los botones Caja, Flujo y Categorías cambian la visualización sin modificar los datos. Las señales del plan resaltan compromisos pendientes o condiciones de liquidez que conviene revisar.")

add_page_break(doc)

# Page 6
add_section_label(doc, "Usuario MYPE")
add_title(doc, "Movimientos y categorías", "Registro diario e intercambio con hojas de cálculo")
add_picture(doc, SHOTS / "03_movimientos.png", Inches(4.8), "Módulo de movimientos con ingresos egresos y opciones de Excel", space_after=4)
add_caption(doc, "Los movimientos se distinguen por tipo, categoría, importe y fecha")
add_heading(doc, "Registrar un movimiento", 2)
add_numbered(doc, [
    "Pulsar Ingreso o Egreso.",
    "Seleccionar la categoría y completar importe, fecha y detalle.",
    "Guardar y verificar el nuevo saldo en el resumen del módulo.",
])
add_heading(doc, "Trabajar con Excel", 2)
add_bullets(doc, [
    "Plantilla descarga un archivo con los encabezados esperados.",
    "Importar acepta XLSX o CSV y muestra una vista previa antes de guardar.",
    "Exportar genera un XLSX con los movimientos del usuario.",
])
add_body(doc, "Para evitar errores, conviene descargar la plantilla actual antes de preparar una carga masiva. La API vuelve a validar tipos, importes, fechas y propiedad de los datos.")

add_page_break(doc)

# Page 7
add_section_label(doc, "Usuario MYPE")
add_title(doc, "Plan de caja", "Datos mensuales y reglas de proyección")
add_body(doc, "El Plan de caja convierte las entradas mensuales en una proyección de doce meses. El usuario configura el saldo inicial, las tasas y las políticas de cobranza y pago; después completa ventas, compras, planilla, servicios y otros conceptos.")
add_heading(doc, "Secuencia recomendada", 2)
add_numbered(doc, [
    "Definir el nombre del negocio, el año y el saldo inicial.",
    "Revisar las tasas configurables antes de ingresar cifras.",
    "Confirmar la política de cobro y pago para contado, 30, 60 y 90 días.",
    "Completar las cifras mensuales. Las compras pueden calcularse desde el costo de ventas si se dejan automáticas.",
    "Guardar y revisar el Resumen y los Reportes.",
])
add_heading(doc, "Conceptos calculados", 2)
add_table(
    doc,
    ["Concepto", "Tratamiento en el piloto"],
    [
        ["Cobranzas y proveedores", "Se distribuyen según las políticas de cuatro plazos"],
        ["IGV", "Compensa débito y crédito y arrastra crédito fiscal"],
        ["IGV Justo", "Desplaza el pago estimado tres meses"],
        ["Planilla", "Calcula descuento previsional, neto y EsSalud"],
        ["Renta", "Estima adelantos mensuales y regularización anual"],
        ["Créditos", "Integra desembolso y cuotas al flujo mensual"],
    ],
    widths=[1.7, 4.6],
)
add_body(doc, "Las tasas son parámetros de planificación. Antes de utilizar resultados para una decisión tributaria deben revisarse con un profesional contable y con la normativa vigente.")

add_page_break(doc)

# Page 8
add_section_label(doc, "Usuario MYPE")
add_title(doc, "Créditos y cronograma", "Comparación de ofertas mediante cuota fija")
add_picture(doc, SHOTS / "04_credito_tea.png", Inches(6.35), "Simulador de crédito con entidad manual y fórmula visual de TEA", space_after=4)
add_caption(doc, "El botón informativo de TEA muestra la tasa mensual y la fórmula de cuota fija")
add_numbered(doc, [
    "Escribir el nombre de la entidad, banco, caja o cooperativa.",
    "Asignar un nombre que permita reconocer el crédito.",
    "Introducir el capital, la TEA, el número de cuotas y el mes de desembolso.",
    "Revisar la cuota preliminar y pulsar Agregar al flujo.",
    "Consultar el cronograma con saldo inicial, interés, amortización, cuota y saldo final.",
])
add_body(doc, "Interpretación. ", bold_lead="", space_after=0)
doc.paragraphs[-1].clear()
r = doc.paragraphs[-1].add_run("Interpretación. ")
set_run_font(r, size=10.5, bold=True, color=TEXT)
r = doc.paragraphs[-1].add_run("La TEA se convierte a tasa efectiva mensual. El interés de cada cuota se calcula sobre el saldo pendiente; por eso la amortización aumenta a medida que el saldo disminuye. La TCEA ofrecida por la entidad debe revisarse por separado cuando existan comisiones, seguros u otros costos.")
set_run_font(r, size=10.5, color=TEXT)

add_page_break(doc)

# Page 9
add_section_label(doc, "Usuario MYPE")
add_title(doc, "Reportes y gráficos", "Lecturas distintas sobre una misma proyección")
add_picture(doc, SHOTS / "05_reportes_graficos.png", Inches(4.7), "Vista de reportes gráficos con evolución de caja", space_after=4)
add_caption(doc, "La vista gráfica permite cambiar entre caja, flujo, impuestos y resultado")
add_table(
    doc,
    ["Vista", "Pregunta que ayuda a responder"],
    [
        ["Flujo de caja", "¿Cuánto ingresa, sale y queda cada mes?"],
        ["Impuestos", "¿Qué pagos y créditos tributarios se estiman?"],
        ["Resultados", "¿Cómo se forma la utilidad estimada?"],
        ["Gráficos", "¿Qué tendencia o diferencia merece atención?"],
    ],
    widths=[1.55, 4.75],
)
add_body(doc, "Durante una presentación conviene elegir primero una pregunta y luego la gráfica correspondiente. Los valores de la vista de demostración son datos de ejemplo y no representan resultados de una empresa real.")

add_page_break(doc)

# Page 10
add_section_label(doc, "Soporte")
add_title(doc, "Trabajo del gestor", "Administración de cuentas sin acceso financiero")
add_picture(doc, SHOTS / "06_gestion_usuarios.png", Inches(4.7), "Directorio del gestor con importación exportación y plantilla", space_after=4)
add_caption(doc, "El gestor visualiza cuentas normales y acciones de soporte")
add_heading(doc, "Acciones disponibles", 2)
add_bullets(doc, [
    "Crear una cuenta normal con contraseña temporal.",
    "Buscar cuentas por nombre o correo.",
    "Importar cuentas normales desde XLSX o CSV.",
    "Exportar el directorio sin contraseñas.",
    "Activar o desactivar una cuenta.",
    "Restablecer la contraseña temporal y cerrar sesiones anteriores.",
    "Registrar atención pendiente, en curso o resuelta con una nota breve.",
])
add_body(doc, "El gestor no puede crear gestores, administrar la cuenta maestra ni consultar movimientos, importes, categorías, créditos o reportes de los usuarios.")

add_page_break(doc)

# Page 11
add_section_label(doc, "Administración")
add_title(doc, "Cuenta maestra y control de accesos", "Gobierno de cuentas para el piloto")
add_body(doc, "La cuenta maestra administra gestores y usuarios. Puede revisar estadísticas generales, crear cuentas, importar lotes, cambiar estados, restablecer accesos y consultar la bitácora de acciones administrativas.")
add_table(
    doc,
    ["Tarea", "Cuenta maestra", "Gestor"],
    [
        ["Crear usuario", "Sí", "Sí"],
        ["Crear gestor", "Sí", "No"],
        ["Importar usuarios", "Usuarios y gestores", "Solo usuarios"],
        ["Cambiar estado", "Usuarios y gestores", "Solo usuarios"],
        ["Restablecer acceso", "Usuarios y gestores", "Solo usuarios"],
        ["Ver información financiera", "No", "No"],
    ],
    widths=[3.2, 1.55, 1.55],
)
add_heading(doc, "Controles incorporados", 2)
add_bullets(doc, [
    "Contraseñas derivadas con scrypt y sal aleatoria.",
    "Sesiones identificadas por cookie HttpOnly y token almacenado por hash.",
    "Cierre de sesiones cuando una cuenta se desactiva o recupera su acceso.",
    "Bitácora administrativa sin importes ni detalles financieros.",
    "Base demo independiente de la base normal.",
])
add_heading(doc, "Responsabilidades operativas", 2)
add_body(doc, "El equipo responsable debe asignar cuentas personales, retirar accesos cuando corresponda, proteger los respaldos y evitar el uso de las credenciales demo con información real. El piloto no incluye recuperación por correo ni autenticación multifactor.")

add_page_break(doc)

# Page 12
add_section_label(doc, "Presentación")
add_title(doc, "Guion sugerido para la demostración", "Recorrido de diez minutos ante un equipo institucional")
add_table(
    doc,
    ["Tiempo", "Acción", "Mensaje principal"],
    [
        ["1 min", "Presentar propósito y alcance", "Apoyo a la planificación de una MYPE en un entorno local"],
        ["1 min", "Mostrar los tres perfiles", "Separación entre uso financiero, soporte y administración"],
        ["2 min", "Registrar ingreso y egreso", "Captura sencilla y exportable a Excel"],
        ["2 min", "Revisar plan de caja", "Proyección de doce meses con reglas configurables"],
        ["2 min", "Simular un crédito", "Cuota, interés y amortización integrados al flujo"],
        ["1 min", "Cambiar gráficos", "Lectura visual de caja, impuestos y resultados"],
        ["1 min", "Entrar como gestor", "Soporte de cuentas sin acceso a finanzas"],
    ],
    widths=[0.75, 2.15, 3.4],
    font_size=8.8,
)
add_heading(doc, "Mensajes que conviene precisar", 2)
add_bullets(doc, [
    "Los datos mostrados son de demostración.",
    "La aplicación funciona localmente y conserva la información en SQLite.",
    "El acceso desde iPhone o iPad depende del equipo anfitrión y de la red local.",
    "Las tasas son configurables y necesitan validación contable para uso institucional.",
    "El siguiente paso del piloto es validar la experiencia con usuarios y formalizar operación, respaldo y seguridad.",
])
add_heading(doc, "Preguntas frecuentes", 2)
add_table(
    doc,
    ["Pregunta", "Respuesta breve"],
    [
        ["¿El gestor ve mis finanzas?", "No. Solo administra cuentas y estados de atención."],
        ["¿Se pueden tener varios créditos?", "Sí. Cada crédito genera su propio cronograma."],
        ["¿Funciona sin internet?", "Sí en el equipo local; iOS necesita la red local del anfitrión."],
        ["¿Reemplaza al contador?", "No. Es una herramienta de planificación y simulación."],
    ],
    widths=[2.25, 4.05],
    font_size=9,
)

add_page_break(doc)

# Page 13
add_section_label(doc, "Operación responsable")
add_title(doc, "Respaldo soporte y cierre", "Prácticas mínimas para cuidar el piloto")
add_heading(doc, "Respaldo", 2)
add_numbered(doc, [
    "Cerrar la ventana del servidor.",
    "Copiar data/nexocaja.sqlite y los archivos auxiliares -wal o -shm si existen.",
    "Guardar la copia en una ubicación institucional protegida.",
    "Registrar fecha, responsable y resultado de una restauración de prueba.",
])
add_heading(doc, "Atención de una incidencia", 2)
add_table(
    doc,
    ["Situación", "Primera revisión"],
    [
        ["No abre el navegador", "Abrir la URL indicada por el iniciador"],
        ["No inicia el servidor", "Revisar versión de Node.js y puerto 4174"],
        ["Una cuenta no ingresa", "Verificar usuario, contraseña y estado activo"],
        ["La hoja no importa", "Usar la plantilla y revisar encabezados y duplicados"],
        ["iPhone no conecta", "Confirmar misma Wi-Fi, IP local y firewall"],
    ],
    widths=[2.1, 4.2],
)
add_heading(doc, "Cierre de una sesión de demostración", 2)
add_bullets(doc, [
    "Cerrar las sesiones abiertas en el navegador.",
    "Detener el servidor con Ctrl+C o cerrar el iniciador.",
    "Eliminar archivos exportados que contengan información de prueba innecesaria.",
    "Registrar cualquier error, duda del público o solicitud de mejora.",
])
add_body(doc, "La documentación técnica y la bitácora del proyecto se encuentran en la carpeta documentacion. Cada agente o equipo que modifique NexoCaja debe dejar una nota para que el siguiente responsable pueda continuar con contexto verificable.")

add_page_break(doc)

# Page 14
add_section_label(doc, "Siguiente etapa")
add_title(doc, "Condiciones para avanzar", "Validaciones recomendadas antes de un despliegue institucional")
add_body(doc, "NexoCaja ya permite demostrar el recorrido completo desde el registro de un movimiento hasta la lectura de una proyección y el soporte de cuentas. El paso siguiente es validar esa experiencia con las personas que utilizarán el sistema y cerrar los requisitos de operación institucional.")
add_table(
    doc,
    ["Frente", "Resultado esperado"],
    [
        ["Usuarios MYPE", "Comprensión de tareas, tiempos y términos utilizados"],
        ["Contabilidad", "Conformidad con fórmulas, tasas, advertencias y ejemplos"],
        ["Seguridad", "Tratamiento de riesgos, accesos, respaldo y datos personales"],
        ["Operación", "Responsables, soporte, recuperación y continuidad"],
        ["Tecnología", "Decisión sobre continuidad local o despliegue web seguro"],
    ],
    widths=[1.55, 4.75],
)
add_heading(doc, "Criterio de cierre del piloto", 2)
add_body(doc, "El piloto puede pasar a evaluación institucional cuando los flujos críticos estén validados por usuarios, los cálculos cuenten con conformidad contable, exista un procedimiento probado de respaldo y la revisión de seguridad no mantenga riesgos críticos sin tratamiento.")
add_heading(doc, "Documentos de referencia", 2)
add_bullets(doc, [
    "README.md para instalación y descripción general.",
    "documentacion/00_LEEME_PRIMERO.md para el contexto compartido.",
    "documentacion/REGISTRO_CAMBIOS.md para la trazabilidad.",
    "documentacion/DECISIONES_TECNICAS.md para decisiones vigentes.",
    "documentacion/ANTIGRAVITY.md para coordinación entre agentes.",
])

p = doc.add_paragraph()
p.paragraph_format.space_before = Pt(20)
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
r = p.add_run("NexoCaja")
set_run_font(r, name="Aptos Display", size=22, bold=True, color=MAROON)
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
r = p.add_run("Manual institucional y de uso del piloto local")
set_run_font(r, size=10.5, color=MUTED)

doc.save(OUTPUT)
print(OUTPUT)
