# Huésped Manager PMS — plantilla común de los manuales en PDF (reportlab)
import os, datetime
from reportlab.lib.pagesizes import letter
from reportlab.lib.units import mm
from reportlab.lib import colors
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.platypus import (BaseDocTemplate, PageTemplate, Frame, Paragraph, Spacer, Image, Table,
                                TableStyle, PageBreak, KeepTogether, NextPageTemplate, CondPageBreak)
from reportlab.platypus.tableofcontents import TableOfContents
from PIL import Image as PILImage

F = '/tmp/fonts/'
pdfmetrics.registerFont(TTFont('Manrope', F + 'Manrope-400.ttf'))
pdfmetrics.registerFont(TTFont('Manrope-Semi', F + 'Manrope-600.ttf'))
pdfmetrics.registerFont(TTFont('Manrope-Bold', F + 'Manrope-700.ttf'))
pdfmetrics.registerFont(TTFont('Sora-Bold', F + 'Sora-700.ttf'))
pdfmetrics.registerFont(TTFont('Sora-Semi', F + 'Sora-600.ttf'))
from reportlab.lib.fonts import addMapping
addMapping('Manrope', 0, 0, 'Manrope'); addMapping('Manrope', 1, 0, 'Manrope-Bold')
addMapping('Manrope', 0, 1, 'Manrope'); addMapping('Manrope', 1, 1, 'Manrope-Bold')

# Paleta "Ola" (colores del logo)
PETROLEO = colors.HexColor('#0F5C80'); AZUL = colors.HexColor('#1576A8'); OLA = colors.HexColor('#1789BE')
TURQUESA = colors.HexColor('#26A9B5'); TURQ_CLARO = colors.HexColor('#6CCCD8')
TINTA = colors.HexColor('#23303D'); GRIS = colors.HexColor('#56667A'); LINEA = colors.HexColor('#DDE7F0')
FONDO = colors.HexColor('#F1F6FA'); SUAVE = colors.HexColor('#E4F2FA')
OK_F = colors.HexColor('#E6F4EC'); OK_L = colors.HexColor('#A9D8BC'); OK_T = colors.HexColor('#14532D')
AV_F = colors.HexColor('#FEF3C7'); AV_L = colors.HexColor('#F3D48A'); AV_T = colors.HexColor('#92400E')
PE_F = colors.HexColor('#FDECEA'); PE_L = colors.HexColor('#F5B5AE'); PE_T = colors.HexColor('#7A1A12')

ANCHO, ALTO = letter
MARGEN = 20 * mm
UTIL = ANCHO - 2 * MARGEN

ES = {
    'h1': ParagraphStyle('h1', fontName='Sora-Bold', fontSize=19, leading=24, textColor=TINTA, spaceBefore=4, spaceAfter=10, keepWithNext=1),
    'h2': ParagraphStyle('h2', fontName='Sora-Semi', fontSize=13.5, leading=18, textColor=PETROLEO, spaceBefore=12, spaceAfter=6, keepWithNext=1),
    'p': ParagraphStyle('p', fontName='Manrope', fontSize=10, leading=15.2, textColor=TINTA, spaceAfter=6),
    'p_gris': ParagraphStyle('pg', fontName='Manrope', fontSize=9, leading=13, textColor=GRIS, spaceAfter=4),
    'paso': ParagraphStyle('paso', fontName='Manrope', fontSize=10, leading=14.5, textColor=TINTA),
    'pie': ParagraphStyle('pie', fontName='Manrope', fontSize=8.5, leading=11, textColor=GRIS, alignment=TA_CENTER, spaceBefore=4, spaceAfter=10),
    'caja': ParagraphStyle('caja', fontName='Manrope', fontSize=9.5, leading=14, textColor=TINTA),
    'celda': ParagraphStyle('celda', fontName='Manrope', fontSize=9, leading=12.5, textColor=TINTA),
    'celda_b': ParagraphStyle('celdab', fontName='Manrope-Bold', fontSize=9, leading=12.5, textColor=TINTA),
    'cab': ParagraphStyle('cab', fontName='Manrope-Bold', fontSize=8.5, leading=11, textColor=GRIS),
    'toc1': ParagraphStyle('toc1', fontName='Manrope-Semi', fontSize=10.5, leading=17, textColor=TINTA, leftIndent=0),
    'toc2': ParagraphStyle('toc2', fontName='Manrope', fontSize=9.5, leading=14, textColor=GRIS, leftIndent=14),
}

SHOTS = '/tmp/shots/manual/'


class Doc(BaseDocTemplate):
    def __init__(self, ruta, titulo, perfil, **kw):
        super().__init__(ruta, pagesize=letter, leftMargin=MARGEN, rightMargin=MARGEN, topMargin=24 * mm, bottomMargin=20 * mm,
                         title='Huésped Manager PMS · ' + titulo, author='Softmanager Solutions', subject=titulo, creator='Huésped Manager PMS', **kw)
        self.titulo, self.perfil = titulo, perfil
        cuerpo = Frame(MARGEN, 20 * mm, UTIL, ALTO - 44 * mm, id='cuerpo')
        portada = Frame(MARGEN, 20 * mm, UTIL, ALTO - 40 * mm, id='portada')
        self.addPageTemplates([PageTemplate('portada', [portada], onPage=self._portada),
                               PageTemplate('normal', [cuerpo], onPage=self._normal)])

    def afterFlowable(self, f):
        if isinstance(f, Paragraph) and f.style.name in ('h1', 'h2'):
            nivel = 0 if f.style.name == 'h1' else 1
            texto = f.getPlainText()
            clave = 'k%d' % id(f)
            self.canv.bookmarkPage(clave)
            self.canv.addOutlineEntry(texto, clave, nivel, closed=nivel > 0)
            self.notify('TOCEntry', (nivel, texto, self.page, clave))

    def _portada(self, c, d):
        c.saveState()
        # banda superior en degradado (petróleo -> azul de la ola)
        pasos = 60
        for i in range(pasos):
            t = i / (pasos - 1)
            col = colors.Color(PETROLEO.red + (OLA.red - PETROLEO.red) * t, PETROLEO.green + (OLA.green - PETROLEO.green) * t,
                               PETROLEO.blue + (OLA.blue - PETROLEO.blue) * t)
            c.setFillColor(col); c.rect(ANCHO * i / pasos, ALTO - 118 * mm, ANCHO / pasos + 1, 118 * mm, stroke=0, fill=1)
        # franja "ola"
        for i, col in enumerate((TURQ_CLARO, TURQUESA, OLA)):
            c.setFillColor(col); c.rect(ANCHO * i / 3, ALTO - 121 * mm, ANCHO / 3 + 1, 3 * mm, stroke=0, fill=1)
        pin(c, MARGEN, ALTO - 44 * mm, 17 * mm, blanco=True)
        c.setFillColor(colors.white)
        c.setFont('Sora-Bold', 15); c.drawString(MARGEN + 22 * mm, ALTO - 33 * mm, 'Huésped Manager PMS')
        c.setFont('Manrope', 10); c.setFillColor(colors.HexColor('#C9E6F4')); c.drawString(MARGEN + 22 * mm, ALTO - 39 * mm, 'Sistema de gestión de huéspedes para copropiedades')
        c.setFillColor(colors.HexColor('#C9E6F4')); c.setFont('Manrope-Semi', 11); c.drawString(MARGEN, ALTO - 78 * mm, 'MANUAL DE USUARIO')
        c.setFillColor(colors.white); c.setFont('Sora-Bold', 30); c.drawString(MARGEN, ALTO - 92 * mm, self.titulo)
        c.setFont('Manrope', 11.5); c.setFillColor(colors.HexColor('#E4F2FA')); c.drawString(MARGEN, ALTO - 102 * mm, self.perfil)
        # pie de portada
        c.setFillColor(GRIS); c.setFont('Manrope', 8.5)
        c.drawString(MARGEN, 18 * mm, 'Versión ' + datetime.date.today().strftime('%d/%m/%Y') + '  ·  Softmanager Solutions')
        c.drawRightString(ANCHO - MARGEN, 18 * mm, 'Documento de uso interno')
        c.restoreState()

    def _normal(self, c, d):
        c.saveState()
        c.setFillColor(PETROLEO); c.rect(0, ALTO - 12 * mm, ANCHO, 12 * mm, stroke=0, fill=1)
        for i, col in enumerate((TURQ_CLARO, TURQUESA, OLA)):
            c.setFillColor(col); c.rect(ANCHO * i / 3, ALTO - 13 * mm, ANCHO / 3 + 1, 1 * mm, stroke=0, fill=1)
        pin(c, MARGEN, ALTO - 10.2 * mm, 7.5 * mm, blanco=True)
        c.setFillColor(colors.white); c.setFont('Manrope-Bold', 9); c.drawString(MARGEN + 9 * mm, ALTO - 7.6 * mm, 'Huésped Manager PMS')
        c.setFont('Manrope', 9); c.setFillColor(colors.HexColor('#C9E6F4')); c.drawRightString(ANCHO - MARGEN, ALTO - 7.6 * mm, 'Manual · ' + self.titulo)
        c.setStrokeColor(LINEA); c.setLineWidth(0.6); c.line(MARGEN, 14 * mm, ANCHO - MARGEN, 14 * mm)
        c.setFillColor(GRIS); c.setFont('Manrope', 8.5)
        c.drawString(MARGEN, 9 * mm, self.perfil)
        c.drawRightString(ANCHO - MARGEN, 9 * mm, 'Página %d' % d.page)
        c.restoreState()


def pin(c, x, y, h, blanco=False):
    """Pin del logo: contorno de gota, círculo blanco y maleta turquesa."""
    w = h * 0.81
    c.saveState()
    p = c.beginPath()
    cx = x + w / 2
    p.moveTo(cx, y)
    p.curveTo(cx - w * 0.15, y + h * 0.18, x, y + h * 0.42, x, y + h * 0.62)
    p.curveTo(x, y + h * 0.86, x + w * 0.22, y + h, cx, y + h)
    p.curveTo(x + w * 0.78, y + h, x + w, y + h * 0.86, x + w, y + h * 0.62)
    p.curveTo(x + w, y + h * 0.42, cx + w * 0.15, y + h * 0.18, cx, y)
    c.setFillColor(colors.white if blanco else PETROLEO); c.drawPath(p, stroke=0, fill=1)
    c.setFillColor(PETROLEO if blanco else colors.white); c.circle(cx, y + h * 0.62, w * 0.33, stroke=0, fill=1)
    c.setFillColor(TURQUESA); c.roundRect(cx - w * 0.16, y + h * 0.47, w * 0.32, h * 0.29, w * 0.05, stroke=0, fill=1)
    c.setFillColor(TURQ_CLARO); c.roundRect(cx - w * 0.16, y + h * 0.47, w * 0.09, h * 0.29, w * 0.04, stroke=0, fill=1)
    c.restoreState()


# ---------------- bloques de contenido ----------------
def H1(t): return Paragraph(t, ES['h1'])
def H2(t): return Paragraph(t, ES['h2'])
def P(t): return Paragraph(t, ES['p'])
def PG(t): return Paragraph(t, ES['p_gris'])


def caja(texto, tipo='nota'):
    fondo, linea, color, titulo = {
        'nota': (SUAVE, colors.HexColor('#B9D9EE'), PETROLEO, 'Nota'),
        'ok': (OK_F, OK_L, OK_T, 'Recomendación'),
        'aviso': (AV_F, AV_L, AV_T, 'Importante'),
        'peligro': (PE_F, PE_L, PE_T, 'Atención'),
    }[tipo]
    par = Paragraph('<font name="Manrope-Bold" color="%s">%s.</font> %s' % (color.hexval().replace('0x', '#'), titulo, texto), ES['caja'])
    t = Table([[par]], colWidths=[UTIL])
    t.setStyle(TableStyle([('BACKGROUND', (0, 0), (-1, -1), fondo), ('BOX', (0, 0), (-1, -1), 0.8, linea),
                           ('LEFTPADDING', (0, 0), (-1, -1), 10), ('RIGHTPADDING', (0, 0), (-1, -1), 10),
                           ('TOPPADDING', (0, 0), (-1, -1), 7), ('BOTTOMPADDING', (0, 0), (-1, -1), 8),
                           ('ROUNDEDCORNERS', [6, 6, 6, 6])]))
    return KeepTogether([Spacer(1, 3), t, Spacer(1, 8)])


def pasos(lista):
    filas = []
    for i, txt in enumerate(lista, 1):
        num = Paragraph('<font name="Manrope-Bold" color="#FFFFFF">%d</font>' % i,
                        ParagraphStyle('n', fontName='Manrope-Bold', fontSize=9, leading=11, alignment=TA_CENTER))
        filas.append([num, Paragraph(txt, ES['paso'])])
    t = Table(filas, colWidths=[8 * mm, UTIL - 8 * mm])
    est = [('VALIGN', (0, 0), (-1, -1), 'TOP'), ('LEFTPADDING', (1, 0), (1, -1), 8), ('LEFTPADDING', (0, 0), (0, -1), 0),
           ('RIGHTPADDING', (0, 0), (0, -1), 0), ('TOPPADDING', (0, 0), (-1, -1), 3), ('BOTTOMPADDING', (0, 0), (-1, -1), 5)]
    for i in range(len(filas)):
        est.append(('BACKGROUND', (0, i), (0, i), AZUL))
    t.setStyle(TableStyle(est + [('ROUNDEDCORNERS', [0, 0, 0, 0])]))
    return KeepTogether([t, Spacer(1, 6)]) if len(filas) <= 7 else t


def vinetas(lista):
    return [Paragraph('<font color="#26A9B5">●</font>&nbsp;&nbsp;' + x, ParagraphStyle('v', parent=ES['p'], leftIndent=12, firstLineIndent=-12, spaceAfter=3)) for x in lista] + [Spacer(1, 4)]


def tabla(encabezados, filas, anchos=None):
    datos = [[Paragraph(h.upper(), ES['cab']) for h in encabezados]]
    for f in filas:
        datos.append([Paragraph(str(c), ES['celda_b'] if i == 0 else ES['celda']) for i, c in enumerate(f)])
    anchos = anchos or [UTIL / len(encabezados)] * len(encabezados)
    t = Table(datos, colWidths=[UTIL * a if a < 1 else a for a in anchos], repeatRows=1)
    t.setStyle(TableStyle([('BACKGROUND', (0, 0), (-1, 0), FONDO), ('LINEBELOW', (0, 0), (-1, 0), 0.8, LINEA),
                           ('LINEBELOW', (0, 1), (-1, -2), 0.4, colors.HexColor('#EAF0F6')),
                           ('BOX', (0, 0), (-1, -1), 0.8, LINEA), ('VALIGN', (0, 0), (-1, -1), 'TOP'),
                           ('LEFTPADDING', (0, 0), (-1, -1), 7), ('RIGHTPADDING', (0, 0), (-1, -1), 7),
                           ('TOPPADDING', (0, 0), (-1, -1), 6), ('BOTTOMPADDING', (0, 0), (-1, -1), 6)]))
    return [t, Spacer(1, 10)]


def captura(nombre, pie, ancho=1.0, recorte=None):
    ruta = SHOTS + nombre + '.png'
    im = PILImage.open(ruta)
    if recorte:
        im = im.crop(recorte); ruta = '/tmp/shots/manual/_r_' + nombre + '_%d.png' % hash(recorte)
        im.save(ruta)
    w, h = im.size
    ancho_pt = UTIL * ancho
    alto_pt = ancho_pt * h / w
    maximo = 130 * mm
    if alto_pt > maximo:
        alto_pt = maximo; ancho_pt = alto_pt * w / h
    img = Image(ruta, width=ancho_pt, height=alto_pt)
    marco = Table([[img]], colWidths=[ancho_pt + 2])
    marco.setStyle(TableStyle([('BOX', (0, 0), (-1, -1), 0.8, LINEA), ('LEFTPADDING', (0, 0), (-1, -1), 0), ('RIGHTPADDING', (0, 0), (-1, -1), 0),
                               ('TOPPADDING', (0, 0), (-1, -1), 0), ('BOTTOMPADDING', (0, 0), (-1, -1), 0)]))
    return KeepTogether([Spacer(1, 4), marco, Paragraph(pie, ES['pie'])])


def construir(ruta, titulo, perfil, intro_portada, contenido):
    doc = Doc(ruta, titulo, perfil)
    historia = [Spacer(1, 108 * mm), Paragraph(intro_portada, ParagraphStyle('ip', parent=ES['p'], fontSize=11, leading=17, textColor=TINTA)),
                NextPageTemplate('normal'), PageBreak()]
    toc = TableOfContents(); toc.levelStyles = [ES['toc1'], ES['toc2']]; toc.dotsMinLevel = 0
    historia += [Paragraph('Contenido', ES['h1'].clone('toc_h', spaceAfter=12)), toc, PageBreak()]
    # Cada título viaja con el bloque que le sigue (nunca queda solo al final de una página)
    agrupado, i = [], 0
    while i < len(contenido):
        f = contenido[i]
        if isinstance(f, Paragraph) and f.style.name in ('h1', 'h2'):
            grupo = [f]
            j = i + 1
            while j < len(contenido) and isinstance(contenido[j], Paragraph) and contenido[j].style.name in ('h1', 'h2'):
                grupo.append(contenido[j]); j += 1
            if j < len(contenido):
                grupo.append(contenido[j]); j += 1
            if f.style.name == 'h1':
                agrupado.append(CondPageBreak(70 * mm))
            agrupado.append(KeepTogether(grupo))
            i = j
        else:
            agrupado.append(f); i += 1
    historia += agrupado
    doc.multiBuild(historia)
    return ruta
