"""
Utilidades para generación de diplomas y herramientas del LMS.
"""

import io
import os
import hashlib
import datetime
from django.conf import settings
from django.core.files.base import ContentFile
from django.utils import timezone
from reportlab.lib.pagesizes import letter, landscape
from reportlab.lib.colors import HexColor
from reportlab.pdfgen import canvas
from reportlab.lib.units import inch
from reportlab.lib.utils import ImageReader
from pypdf import PdfReader, PdfWriter
from pyhanko.sign import signers, fields
from pyhanko.pdf_utils.incremental_writer import IncrementalPdfFileWriter
from pyhanko.stamp import TextStampStyle
from cryptography import x509
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.serialization import pkcs12, BestAvailableEncryption
from cryptography.hazmat.backends import default_backend


def generate_certificate_pdf(certificate):
    """
    Genera una constancia/diploma de capacitación oficial en PDF de alta resolución,
    con diseño arquitectónico moderno: panel lateral izquierdo y contenido minimalista a la derecha.
    Incluye blindaje de seguridad para evitar errores 500 en servidores cPanel/Pillow.
    Returns: BytesIO buffer con el PDF generado en memoria.
    """
    try:
        return _build_certificate_canvas(certificate, use_images=True)
    except Exception:
        # Si el servidor cPanel tiene incompatibilidad con Pillow/RGBA en drawImage/save(),
        # se genera de inmediato la versión vectorial pura 100% segura sin lanzar error 500.
        return _build_certificate_canvas(certificate, use_images=False)


def _build_certificate_canvas(certificate, use_images=True):
    buffer = io.BytesIO()

    # Orientación horizontal (Letter Landscape -> 792 x 612 puntos)
    width, height = landscape(letter)
    c = canvas.Canvas(buffer, pagesize=(width, height))

    # Paleta de Colores Lo Justo
    emerald_dark = HexColor("#044d29")   # Verde oscuro institucional
    emerald_mid = HexColor("#065f46")    # Verde subtítulos y textos de soporte
    emerald_light = HexColor("#10b981")  # Verde acento lateral
    text_dark = HexColor("#18181b")      # Negro casi puro para nombres y títulos principales
    text_muted = HexColor("#52525b")     # Gris para metadatos e ISOs
    bg_white = HexColor("#ffffff")

    # 1. Fondo general blanco puro
    c.setFillColor(bg_white)
    c.rect(0, 0, width, height, fill=1, stroke=0)

    # 2. PANEL LATERAL IZQUIERDO (X: 0 a 210)
    side_width = 210

    # Verificamos si existe una imagen de collage personalizada
    side_image_path = os.path.join(str(settings.BASE_DIR), "static", "img", "diploma_side.png")
    if not os.path.exists(side_image_path):
        side_image_path = os.path.join(str(settings.BASE_DIR), "static", "img", "diploma_side.jpg")

    drawn_side_img = False
    if use_images and os.path.exists(side_image_path):
        try:
            img_side = ImageReader(side_image_path)
            c.drawImage(img_side, 0, 0, width=side_width, height=height, mask='auto')
            drawn_side_img = True
        except Exception:
            drawn_side_img = False

    if not drawn_side_img:
        _draw_geometric_side_panel(c, side_width, height, emerald_dark, emerald_mid, emerald_light, text_dark)

    # Línea divisoria vertical fina separando el panel lateral del cuerpo
    c.setStrokeColor(HexColor("#e4e4e7"))
    c.setLineWidth(1)
    c.line(side_width, 0, side_width, height)

    # 3. ÁREA DE CONTENIDO PRINCIPAL (X: 210 a 792 -> Centro X_center = 501)
    x_center = side_width + (width - side_width) / 2.0

    # A) Código de Constancia en Esquina Superior Derecha
    c.setFont("Helvetica", 11)
    c.setFillColor(HexColor("#71717a"))
    c.drawRightString(width - 30, height - 35, f"{certificate.certificate_code}")

    # B) Logo Central Institucional e ISOs debajo
    logo_path = os.path.join(str(settings.BASE_DIR), "static", "img", "logo.png")
    drawn_logo = False
    if use_images and os.path.exists(logo_path):
        try:
            logo_img = ImageReader(logo_path)
            # Centrado en X_center (ancho 260, alto 76 para gran impacto visual)
            c.drawImage(logo_img, x_center - 130, height - 118, width=260, height=76, mask='auto')
            drawn_logo = True
        except Exception:
            try:
                logo_img = ImageReader(logo_path)
                c.drawImage(logo_img, x_center - 130, height - 118, width=260, height=76)
                drawn_logo = True
            except Exception:
                drawn_logo = False

    if not drawn_logo:
        c.setFont("Helvetica-Bold", 32)
        c.setFillColor(emerald_dark)
        c.drawCentredString(x_center, height - 85, "LO JUSTO LMS")

    # Normas ISO debajo del Logo
    c.setFont("Helvetica-Bold", 10)
    c.setFillColor(text_muted)
    c.drawCentredString(x_center - 95, height - 132, "ISO 17025")
    c.drawCentredString(x_center + 95, height - 132, "ISO 17034")

    # C) Título Principal: CONSTANCIA DE CAPACITACIÓN
    c.setFont("Helvetica-Bold", 28)
    c.setFillColor(emerald_dark)
    c.drawCentredString(x_center, height - 175, "CONSTANCIA DE")
    c.drawCentredString(x_center, height - 208, "CAPACITACIÓN")

    # D) Bloque "Otorgado a:" y Nombre del Alumno
    c.setFont("Helvetica", 13.5)
    c.setFillColor(emerald_mid)
    c.drawString(side_width + 50, height - 250, "Otorgado a:")

    try:
        student_name = certificate.enrollment.user.get_full_name()
        if not student_name:
            student_name = certificate.enrollment.user.username.replace("_", " ").title()
    except Exception:
        student_name = "Participante Metrológico"

    c.setFont("Helvetica-Bold", 30)
    c.setFillColor(text_dark)
    c.drawCentredString(x_center, height - 295, student_name.title())

    # E) Bloque del Programa y Participación
    c.setFont("Helvetica", 13)
    c.setFillColor(emerald_mid)
    c.drawCentredString(x_center, height - 345, "Por haber participado satisfactoriamente en la capacitación:")

    try:
        course_title = certificate.enrollment.course.title
    except Exception:
        course_title = "Especialización Metrológica & Calidad"

    # Título del Curso entre comillas
    c.setFont("Helvetica-Bold", 19)
    c.setFillColor(text_dark)
    c.drawCentredString(x_center, height - 390, f'"{course_title}"')

    # Párrafo de detalles de fecha, modalidad y duración
    c.setFont("Helvetica", 11)
    c.setFillColor(emerald_mid)

    try:
        if certificate.issued_at and hasattr(certificate.issued_at, "strftime"):
            date_str = certificate.issued_at.strftime("%d de %B del %Y")
        elif certificate.issued_at:
            date_str = str(certificate.issued_at).split(" ")[0]
        else:
            date_str = "fecha de emisión oficial"
    except Exception:
        date_str = "fecha de emisión oficial"

    # Formateo español meses
    meses_es = {"January": "enero", "February": "febrero", "March": "marzo", "April": "abril", "May": "mayo", "June": "junio",
                "July": "julio", "August": "agosto", "September": "septiembre", "October": "octubre", "November": "noviembre", "December": "diciembre"}
    for eng, esp in meses_es.items():
        date_str = date_str.replace(eng, esp)

    c.drawCentredString(x_center, height - 435, f"Realizado el día {date_str}, en modalidad virtual con una duración")
    c.drawCentredString(x_center, height - 452, "cronológica técnica, demostrando su notable interés, participación activa y")
    c.drawCentredString(x_center, height - 469, "compromiso con la calidad y excelencia operativa.")

    # F) Sección Inferior: QR a la Izquierda y Firma a la Derecha
    qr_x = side_width + 50
    qr_y = 30

    # Textos de verificación sobre el QR
    c.setFont("Helvetica-Bold", 8)
    c.setFillColor(text_dark)
    c.drawString(qr_x, qr_y + 75, "Esta constancia es válida.")
    c.setFont("Helvetica", 7.5)
    c.setFillColor(text_muted)
    c.drawString(qr_x, qr_y + 65, "Verifique su autenticidad escaneando el código QR")

    # QR Code Imagen
    drawn_qr = False
    if use_images and certificate.qr_code_image and hasattr(certificate.qr_code_image, "name") and certificate.qr_code_image.name:
        try:
            qr_path = certificate.qr_code_image.path
            if os.path.exists(qr_path):
                img_qr = ImageReader(qr_path)
                c.drawImage(img_qr, qr_x, qr_y, width=60, height=60, mask='auto')
                c.setStrokeColor(emerald_dark)
                c.setLineWidth(1)
                c.rect(qr_x, qr_y, 60, 60, fill=0, stroke=1)
                drawn_qr = True
        except Exception:
            drawn_qr = False

    if not drawn_qr:
        _draw_qr_placeholder(c, qr_x, qr_y, certificate.certificate_code)

    # Línea y Cargo de Firma
    sig_center_x = x_center + 90
    c.setStrokeColor(text_dark)
    c.setLineWidth(1.2)
    c.line(sig_center_x - 85, qr_y + 40, sig_center_x + 85, qr_y + 40)

    c.setFont("Helvetica-Bold", 11)
    c.setFillColor(text_dark)
    c.drawCentredString(sig_center_x, qr_y + 25, "Director Técnico & Calidad")

    c.setFont("Helvetica-Bold", 10)
    c.setFillColor(text_dark)
    c.drawCentredString(sig_center_x, qr_y + 12, "Supervisor de Laboratorio")

    # G) Código Vertical en el Margen Derecho (AQP/MOQ/LIM)
    c.saveState()
    c.translate(width - 16, qr_y + 30)
    c.rotate(90)
    c.setFont("Helvetica-Bold", 9)
    c.setFillColor(emerald_mid)
    c.drawString(0, 0, "AQP/MOQ/LIM • LO JUSTO METROLOGÍA ISO 17025")
    c.restoreState()

    c.showPage()
    c.save()
    buffer.seek(0)
    return buffer


def _draw_geometric_side_panel(c, width, height, color_top, color_mid, color_light, color_dark):
    """
    Dibuja un panel lateral geométrico de alto impacto (estilo collage/cortes angulares modernos)
    cuando no se ha cargado una imagen externa personalizada.
    """
    # Fondo base verde oscuro
    c.setFillColor(color_top)
    c.rect(0, 0, width, height, fill=1, stroke=0)

    # Polígono Superior (Verde esmeralda oscuro)
    p1 = c.beginPath()
    p1.moveTo(0, height)
    p1.lineTo(width, height)
    p1.lineTo(width, height - 160)
    p1.lineTo(0, height - 110)
    p1.close()
    c.setFillColor(color_top)
    c.drawPath(p1, fill=1, stroke=0)

    # Polígono Medio 1 (Verde acento brillante)
    p2 = c.beginPath()
    p2.moveTo(0, height - 110)
    p2.lineTo(width, height - 160)
    p2.lineTo(width, height - 340)
    p2.lineTo(0, height - 280)
    p2.close()
    c.setFillColor(color_mid)
    c.drawPath(p2, fill=1, stroke=0)

    # Polígono Medio 2 (Verde claro / esmeralda fresco)
    p3 = c.beginPath()
    p3.moveTo(0, height - 280)
    p3.lineTo(width, height - 340)
    p3.lineTo(width, height - 490)
    p3.lineTo(0, height - 440)
    p3.close()
    c.setFillColor(color_light)
    c.drawPath(p3, fill=1, stroke=0)

    # Polígono Inferior (Gris carbón ejecutivo)
    p4 = c.beginPath()
    p4.moveTo(0, height - 440)
    p4.lineTo(width, height - 490)
    p4.lineTo(width, 0)
    p4.lineTo(0, 0)
    p4.close()
    c.setFillColor(color_dark)
    c.drawPath(p4, fill=1, stroke=0)

    # Líneas blancas divisorias angulares entre los segmentos (estilo collage)
    c.setStrokeColor(HexColor("#ffffff"))
    c.setLineWidth(4.5)
    c.line(0, height - 110, width, height - 160)
    c.line(0, height - 280, width, height - 340)
    c.line(0, height - 440, width, height - 490)

    # Emblema / Monograma blanco sutil central en el panel
    c.setFont("Helvetica-Bold", 40)
    c.setFillColor(HexColor("#ffffff"))
    c.drawCentredString(width / 2.0, height / 2.0 - 15, "LJ")
    c.setFont("Helvetica-Bold", 9)
    c.drawCentredString(width / 2.0, height / 2.0 - 32, "ISO/IEC 17025")


def _draw_qr_placeholder(c, x, y, code):
    """
    Dibuja un cuadro de validación limpio si la imagen QR aún no ha sido adjuntada en el servidor.
    """
    c.setStrokeColor(HexColor("#065f46"))
    c.setLineWidth(1)
    c.rect(x, y, 60, 60, fill=0, stroke=1)
    c.setFont("Helvetica-Bold", 7)
    c.setFillColor(HexColor("#065f46"))
    c.drawCentredString(x + 30, y + 33, "VALIDACIÓN")
    c.drawCentredString(x + 30, y + 22, "ONLINE QR")


def create_institutional_p12_certificate(save_path, password="lojusto_cert_2026"):
    """
    Genera un certificado digital institucional X.509 PKCS#12 (.p12) válido en backend.
    """
    private_key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=2048,
        backend=default_backend()
    )
    subject = issuer = x509.Name([
        x509.NameAttribute(x509.NameOID.COUNTRY_NAME, "CL"),
        x509.NameAttribute(x509.NameOID.STATE_OR_PROVINCE_NAME, "Santiago"),
        x509.NameAttribute(x509.NameOID.ORGANIZATION_NAME, "Lo Justo Metrología & E-Learning"),
        x509.NameAttribute(x509.NameOID.ORGANIZATIONAL_UNIT_NAME, "Dirección de Certificación Técnica"),
        x509.NameAttribute(x509.NameOID.COMMON_NAME, "Lo Justo Autoridad de Certificación"),
    ])
    now_utc = datetime.datetime.now(datetime.timezone.utc)
    cert = (
        x509.CertificateBuilder()
        .subject_name(subject)
        .issuer_name(issuer)
        .public_key(private_key.public_key())
        .serial_number(x509.random_serial_number())
        .not_valid_before(now_utc - datetime.timedelta(days=1))
        .not_valid_after(now_utc + datetime.timedelta(days=1825))
        .add_extension(x509.BasicConstraints(ca=True, path_length=None), critical=True)
        .sign(private_key, hashes.SHA256(), default_backend())
    )
    p12_bytes = pkcs12.serialize_key_and_certificates(
        name=b"Lo Justo Certificado Digital",
        key=private_key,
        cert=cert,
        cas=None,
        encryption_algorithm=BestAvailableEncryption(password.encode("utf-8"))
    )
    os.makedirs(os.path.dirname(os.path.abspath(save_path)), exist_ok=True)
    with open(save_path, "wb") as f:
        f.write(p12_bytes)
    return save_path


def generate_and_sign_certificate(certificate, signature_obj=None, password=None):
    """
    Construye el diploma oficial en PDF, aplica la firma digital criptográfica PAdES (si hay .p12 activo),
    calcula el hash SHA-256 para validación de integridad y guarda el archivo en el modelo.
    """
    from certificates.models import DigitalSignature

    # 1. Generar el PDF visual en memoria con ReportLab
    base_pdf_buffer = generate_certificate_pdf(certificate)
    base_pdf_bytes = base_pdf_buffer.getvalue()

    # 2. Buscar firma digital activa si no se especificó una
    if not signature_obj:
        signature_obj = DigitalSignature.objects.filter(is_active=True).first()

    signed_pdf_bytes = base_pdf_bytes
    is_signed = False
    signer_name = "Lo Justo Metrología & E-Learning"

    if signature_obj and signature_obj.pfx_file and os.path.exists(signature_obj.pfx_file.path):
        p12_path = signature_obj.pfx_file.path
        passphrase_str = password if password is not None else signature_obj.p12_password
        p12_password = passphrase_str.encode("utf-8") if passphrase_str else b""

        # Intentar extraer el nombre del titular desde el certificado X.509
        try:
            with open(p12_path, "rb") as f:
                p12_data = f.read()
            p12_info = pkcs12.load_key_and_certificates(p12_data, p12_password, backend=default_backend())
            cert_x509 = p12_info[1]
            if cert_x509:
                cn_attrs = cert_x509.subject.get_attributes_for_oid(x509.NameOID.COMMON_NAME)
                if cn_attrs:
                    signer_name = cn_attrs[0].value.split(" - ")[0].split(" RUC")[0].strip()
        except Exception:
            signer_name = signature_obj.signer_name or "Director Técnico & Calidad"

        # Aplicar firma criptográfica PAdES con PyHanko
        try:
            signer = signers.SimpleSigner.load_pkcs12(
                pfx_file=p12_path,
                passphrase=p12_password
            )
            out_buffer = io.BytesIO()
            blank_style = TextStampStyle(stamp_text="", border_width=0)
            signature_box = (500, 30, 750, 110)

            pdf_in = IncrementalPdfFileWriter(io.BytesIO(base_pdf_bytes))
            fields.append_signature_field(
                pdf_in,
                sig_field_spec=fields.SigFieldSpec(
                    sig_field_name="FirmaDigitalLoJusto",
                    on_page=0,
                    box=signature_box
                )
            )
            pdf_signer = signers.PdfSigner(
                signature_meta=signers.PdfSignatureMetadata(field_name="FirmaDigitalLoJusto"),
                signer=signer,
                stamp_style=blank_style
            )
            pdf_signer.sign_pdf(
                pdf_out=pdf_in,
                in_place=False,
                output=out_buffer
            )
            signed_pdf_bytes = out_buffer.getvalue()
            is_signed = True
        except Exception as e:
            print(f"Nota: No se pudo aplicar firma PAdES binaria ({e}), guardando versión con hash SHA-256.")
            signed_pdf_bytes = base_pdf_bytes

    # 3. Calcular hash criptográfico SHA-256 del PDF final
    sha256 = hashlib.sha256(signed_pdf_bytes).hexdigest()

    # 4. Guardar archivo en el modelo Certificate
    file_name = f"Diploma_{certificate.certificate_code}.pdf"
    certificate.pdf_file.save(file_name, ContentFile(signed_pdf_bytes), save=False)
    certificate.sha256_hash = sha256
    certificate.is_digitally_signed = is_signed
    certificate.signer_name = signer_name
    certificate.save(update_fields=["pdf_file", "sha256_hash", "is_digitally_signed", "signer_name"])

    return io.BytesIO(signed_pdf_bytes)
