"""Diagnostic PDFs using ReportLab, without browser or Pango/Cairo dependencies.

Clinical values come from PDFReportService's existing context builder. The HTML
preview remains available separately; this renderer preserves report modality,
flags, amendment notices, verification details, signatures and patient QR links.
"""

import base64
from io import BytesIO
from pathlib import Path
from typing import Any
from xml.sax.saxutils import escape

from reportlab.lib import colors
from reportlab.lib.enums import TA_CENTER, TA_RIGHT
from reportlab.lib.pagesizes import A4, landscape
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.units import mm
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfgen.canvas import Canvas
from reportlab.platypus import (
    Flowable,
    HRFlowable,
    Image,
    KeepTogether,
    Paragraph,
    SimpleDocTemplate,
    Spacer,
    Table,
    TableStyle,
)

FONT = "MunalSans"
BOLD_FONT = "MunalSans-Bold"
INK = colors.HexColor("#0f172a")
MUTED = colors.HexColor("#64748b")
LINE = colors.HexColor("#e2e8f0")
BLUE = colors.HexColor("#1e3a8a")
RED = colors.HexColor("#b91c1c")


def _register_fonts() -> None:
    # Bundled Unicode fonts make reports independent of server font installs.
    fonts_dir = Path(__file__).resolve().parent.parent / "assets" / "fonts"
    for name, filename in ((FONT, "DejaVuSans.ttf"), (BOLD_FONT, "DejaVuSans-Bold.ttf")):
        if name not in pdfmetrics.getRegisteredFontNames():
            pdfmetrics.registerFont(TTFont(name, str(fonts_dir / filename)))
    pdfmetrics.registerFontFamily(FONT, normal=FONT, bold=BOLD_FONT, italic=FONT, boldItalic=BOLD_FONT)


def _text(value: object) -> str:
    """Treat clinical text as text; never interpret user input as PDF markup."""
    return escape(str(value if value is not None else "—")).replace("\n", "<br/>")


def _paragraph(value: object, style: ParagraphStyle, *, bold: bool = False) -> Paragraph:
    text = _text(value)
    return Paragraph(f"<b>{text}</b>" if bold else text, style)


def _data_image(data_uri: str, max_width: float, max_height: float) -> Image:
    raw = base64.b64decode(data_uri.split(",", 1)[1], validate=True)
    image = Image(BytesIO(raw))
    scale = min(max_width / image.imageWidth, max_height / image.imageHeight)
    image.drawWidth = image.imageWidth * scale
    image.drawHeight = image.imageHeight * scale
    return image


def render_diagnostic_pdf(context: dict[str, Any]) -> bytes:
    """Build a paginated A4 clinical report using the existing verified data."""
    _register_fonts()
    buffer = BytesIO()
    report, order, patient, lab = (context[key] for key in ("report", "order", "patient", "lab"))
    status = str(report.status.value)
    version = int(report.version or 1)
    width = A4[0] - 32 * mm
    body = ParagraphStyle("body", fontName=FONT, fontSize=8, leading=11, textColor=INK)
    small = ParagraphStyle("small", parent=body, fontSize=7, leading=9, textColor=MUTED)
    title = ParagraphStyle("title", parent=body, fontName=BOLD_FONT, fontSize=14, leading=17)
    heading = ParagraphStyle("heading", parent=body, fontName=BOLD_FONT, textColor=BLUE)
    right = ParagraphStyle("right", parent=body, alignment=TA_RIGHT)
    center = ParagraphStyle("center", parent=body, alignment=TA_CENTER)
    abnormal = ParagraphStyle("abnormal", parent=center, fontName=BOLD_FONT, textColor=RED)
    group_style = ParagraphStyle("group", parent=heading, textColor=colors.white)
    story: list[Flowable] = []
    lab_name = getattr(lab, "name", None) or "Munal Medical Laboratory"
    country = getattr(order, "destination_country", None)
    report_title = "PRE-DEPARTURE / VISA MEDICAL REPORT" if country else "DIAGNOSTIC LAB REPORT"
    status_labels = {
        "draft": "DRAFT",
        "pending_verification": "PENDING VERIFICATION",
        "verified": "VERIFIED",
        "released": "OFFICIAL REPORT",
        "rejected": "REJECTED",
    }
    header_right = [
        _paragraph(report_title, ParagraphStyle("report_title", parent=heading, alignment=TA_RIGHT)),
        _paragraph(status_labels.get(status, status.upper()), right, bold=True),
    ]
    if country:
        header_right.append(_paragraph(f"Abroad Clearance: {country}", right))
    if version > 1:
        header_right.append(_paragraph(f"Amended (Version {version})", right))
    header = Table(
        [
            [
                [
                    _paragraph(lab_name.upper(), title),
                    _paragraph(getattr(lab, "address", None) or "", body),
                    _paragraph("Diagnostic Pathology & Clinical Testing Laboratory", small),
                ],
                header_right,
            ]
        ],
        colWidths=[width * 0.56, width * 0.44],
        style=TableStyle([("VALIGN", (0, 0), (-1, -1), "TOP"), ("LEFTPADDING", (0, 0), (-1, -1), 0)]),
    )
    story.extend([header, Spacer(1, 6), HRFlowable(width="100%", thickness=1.5, color=INK), Spacer(1, 10)])

    demographics = [
        (
            "Patient Name",
            getattr(patient, "name", None),
            "Order / Bill No",
            getattr(order, "order_number", None),
        ),
        ("Patient ID / UHID", getattr(patient, "patient_code", None), "Order Date", context["order_date"]),
        ("Age / Gender", context["patient_age_gender"], "Specimen Sample", context["specimen_display"]),
        ("Contact / Phone", getattr(patient, "phone", None), "Reported Date", context["reported_date"]),
    ]
    referred = getattr(order, "referred_by_custom", None)
    if referred or country:
        demographics.append(
            ("Referred By", referred or "Self / Direct Walk-in", "Destination Country", country or "N/A")
        )
    rows = [
        [
            _paragraph(label1, small, bold=True),
            _paragraph(value1, body),
            _paragraph(label2, small, bold=True),
            _paragraph(value2, body),
        ]
        for label1, value1, label2, value2 in demographics
    ]
    story.append(
        Table(
            rows,
            colWidths=[width * f for f in (0.17, 0.33, 0.17, 0.33)],
            style=TableStyle(
                [
                    ("GRID", (0, 0), (-1, -1), 0.4, LINE),
                    ("BACKGROUND", (0, 0), (-1, -1), colors.HexColor("#f8fafc")),
                    ("VALIGN", (0, 0), (-1, -1), "TOP"),
                    ("TOPPADDING", (0, 0), (-1, -1), 5),
                    ("BOTTOMPADDING", (0, 0), (-1, -1), 5),
                ]
            ),
            splitInRow=1,
        )
    )
    story.append(Spacer(1, 10))

    reason = getattr(report, "verification_reason", None)
    if version > 1:
        story.append(
            _paragraph(
                f"AMENDMENT NOTICE (Version {version}): This document supersedes report version "
                f"{version - 1} for Order {getattr(order, 'order_number', '—')}.",
                heading,
            )
        )
        if reason:
            story.append(_paragraph(f"Remarks / Reason: {reason}", body))
        story.append(Spacer(1, 10))

    imaging_sections = context["imaging_sections"]
    imaging = context["report_modality"] == "imaging" and bool(imaging_sections)
    if imaging:
        for section in imaging_sections:
            name = section.get("test_name", "Imaging Study")
            code = section.get("test_code")
            story.append(_paragraph(f"{name} [{code}]" if code else name, heading))
            if section.get("views"):
                story.append(_paragraph("Views examined: " + ", ".join(section["views"]), small))
            for label, field in (("Remarks:", "findings"), ("Impression:", "impression")):
                story.append(_paragraph(label, heading))
                story.append(_paragraph(section.get(field) or "—", body))
                story.append(Spacer(1, 8))
    else:
        show_flags = status not in {"verified", "released"}
        for group in context["test_groups"]:
            labels = ["Investigation / Parameter", "Observed Value", "Unit", "Biological Ref. Interval"]
            fractions = [0.36, 0.20, 0.16, 0.28]
            if show_flags:
                labels.append("Flag")
                fractions = [0.32, 0.18, 0.14, 0.24, 0.12]
            label = f"{group.test_name} [{group.test_code}]" if group.test_code else group.test_name
            data: list[list[Any]] = [
                [_paragraph(label, group_style)] + [""] * (len(labels) - 1),
                [_paragraph(value, small, bold=True) for value in labels],
            ]
            for param in group.parameters:
                parameter = [_paragraph(param.name, body)]
                if param.notes:
                    parameter.append(_paragraph(param.notes, small))
                row: list[Any] = [
                    parameter,
                    _paragraph(param.value, abnormal if param.is_abnormal else center),
                    _paragraph(param.unit or "—", center),
                    _paragraph(param.reference_interval or "—", body),
                ]
                if show_flags:
                    flag = {"H": "HIGH", "L": "LOW", "N": "Normal"}.get(param.flag, param.flag or "—")
                    row.append(_paragraph(flag, abnormal if param.is_abnormal else center))
                data.append(row)
            story.append(
                Table(
                    data,
                    colWidths=[width * f for f in fractions],
                    repeatRows=2,
                    splitInRow=1,
                    style=TableStyle(
                        [
                            ("SPAN", (0, 0), (-1, 0)),
                            ("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#1e293b")),
                            ("BACKGROUND", (0, 1), (-1, 1), colors.HexColor("#f1f5f9")),
                            ("LINEBELOW", (0, 1), (-1, -1), 0.4, LINE),
                            ("VALIGN", (0, 0), (-1, -1), "TOP"),
                            ("TOPPADDING", (0, 0), (-1, -1), 5),
                            ("BOTTOMPADDING", (0, 0), (-1, -1), 5),
                        ]
                    ),
                )
            )
            story.append(Spacer(1, 10))

    if report.notes and not imaging:
        story.extend(
            [
                _paragraph("Clinical Remarks & Interpretation:", heading),
                _paragraph(report.notes, body),
                Spacer(1, 10),
            ]
        )
    if reason and version == 1:
        label = "Diagnostic Impression & Conclusion:" if imaging else "Verification Remarks:"
        story.extend([_paragraph(label, heading), _paragraph(reason, body), Spacer(1, 10)])

    verification: list[Flowable] = [
        _paragraph("Electronic Verification Record", heading),
        _paragraph(f"Report ID: {report.id}", small),
        _paragraph(f"Verification Status: {status.upper()}", small),
    ]
    if report.verified_at:
        verification.append(
            _paragraph(f"Verified At: {report.verified_at.strftime('%Y-%m-%d %H:%M UTC')}", small)
        )
    if status in {"verified", "released"}:
        verification.append(_paragraph("Verified and signed in Munal LMS by the listed authorizer.", small))
    signer: list[Flowable] = []
    verifier = getattr(report, "verifier", None)
    if verifier:
        if context.get("verifier_signature_b64"):
            signature = _data_image(context["verifier_signature_b64"], 140, 39)
            signature.hAlign = "RIGHT"
            signer.append(signature)
        signer.append(_paragraph(verifier.name, right, bold=True))
        if getattr(verifier, "license_no", None):
            signer.append(_paragraph(f"NMC No.: {verifier.license_no}", right, bold=True))
        role = getattr(verifier.role, "value", verifier.role)
        signer.append(_paragraph(f"{str(role).capitalize()} / Medical Officer", right))
        signer.append(
            _paragraph(
                "Electronically Signed & Authorized"
                if status in {"verified", "released"}
                else "Awaiting authorization",
                small,
            )
        )
    else:
        signer.append(_paragraph("Awaiting Verification", right))
    signoff = Table(
        [[verification, signer]],
        colWidths=[width * 0.60, width * 0.40],
        style=TableStyle(
            [
                ("VALIGN", (0, 0), (-1, -1), "TOP"),
                ("LINEABOVE", (0, 0), (-1, 0), 0.5, LINE),
                ("TOPPADDING", (0, 0), (-1, -1), 8),
                ("LEFTPADDING", (0, 0), (-1, -1), 0),
            ]
        ),
    )
    story.append(KeepTogether([Spacer(1, 8), signoff]))
    story.extend(
        [
            Spacer(1, 8),
            _paragraph(
                "Laboratory investigation results should be correlated clinically with patient history, "
                "physical examination, and other relevant diagnostic evaluations.",
                small,
            ),
        ]
    )
    if context.get("report_qr_b64"):
        qr_image = _data_image(context["report_qr_b64"], 68, 68)
        qr_image.hAlign = "CENTER"
        story.append(
            KeepTogether(
                [
                    Spacer(1, 10),
                    qr_image,
                    _paragraph(
                        "Scan to view this report online — no login required",
                        ParagraphStyle("qr", parent=small, alignment=TA_CENTER),
                    ),
                ]
            )
        )

    class ReportCanvas(Canvas):  # type: ignore[misc]  # ReportLab has no canvas typing stub
        """Replay pages once total page count is known; no clinical text is altered."""

        def __init__(self, *args: Any, **kwargs: Any) -> None:  # noqa: ANN401 - ReportLab canvas factory
            super().__init__(*args, **kwargs)
            self.pages: list[dict[str, Any]] = []

        def showPage(self) -> None:  # noqa: N802 - ReportLab canvas API
            self.pages.append(dict(self.__dict__))
            self._startPage()

        def save(self) -> None:
            total = len(self.pages)
            for page_state in self.pages:
                self.__dict__.update(page_state)
                self.saveState()
                self.setStrokeColor(LINE)
                self.line(16 * mm, 14 * mm, A4[0] - 16 * mm, 14 * mm)
                self.setFillColor(MUTED)
                self.setFont(FONT, 6.5)
                footer_label = f"{lab_name} | Medical Report"
                while pdfmetrics.stringWidth(footer_label, FONT, 6.5) > width - 80:
                    footer_label = footer_label[:-2] + "…"
                self.drawString(16 * mm, 10 * mm, footer_label)
                self.drawRightString(A4[0] - 16 * mm, 10 * mm, f"Page {self._pageNumber} of {total}")
                if context.get("watermark_text"):
                    self.translate(A4[0] / 2, A4[1] / 2)
                    self.rotate(30)
                    self.setFillColor(RED)
                    self.setFillAlpha(0.12)
                    self.setFont(BOLD_FONT, 24)
                    self.drawCentredString(0, 0, context["watermark_text"])
                self.restoreState()
                super().showPage()
            super().save()

    doc = SimpleDocTemplate(
        buffer,
        pagesize=A4,
        leftMargin=16 * mm,
        rightMargin=16 * mm,
        topMargin=14 * mm,
        bottomMargin=20 * mm,
        title=f"Diagnostic Report - {getattr(order, 'order_number', '')}",
        author=str(lab_name),
    )
    doc.build(story, canvasmaker=ReportCanvas)
    return buffer.getvalue()


def render_management_pdf(rows: list[list[str]], section: str) -> bytes:
    """Render the existing accounting export rows without changing any totals."""
    _register_fonts()
    buffer = BytesIO()
    page_size = landscape(A4)
    width = page_size[0] - 28 * mm
    body = ParagraphStyle("management", fontName=FONT, fontSize=7, leading=10, textColor=INK)
    title = ParagraphStyle("management_title", parent=body, fontName=BOLD_FONT, fontSize=17, leading=21)
    story: list[Flowable] = [
        _paragraph("Munal · Management reporting", title),
        _paragraph(f"{section.title()} · NPR · Nepal calendar-day boundaries", body),
        Spacer(1, 10),
    ]
    cells = [[_paragraph(cell, body, bold=index == 0) for cell in row] for index, row in enumerate(rows)]
    metadata_spans = [
        ("SPAN", (2, index), (-1, index))
        for index, row in enumerate(rows)
        if index > 0 and row[0] in {"Notes", "Generated", "Scope", "Period"}
    ]
    story.append(
        Table(
            cells,
            colWidths=[width * f for f in (0.19, 0.35, 0.13, 0.11, 0.10, 0.12)],
            repeatRows=1,
            splitInRow=1,
            style=TableStyle(
                [
                    *metadata_spans,
                    ("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#f1f5f9")),
                    ("LINEBELOW", (0, 0), (-1, -1), 0.4, LINE),
                    ("VALIGN", (0, 0), (-1, -1), "TOP"),
                    ("TOPPADDING", (0, 0), (-1, -1), 5),
                    ("BOTTOMPADDING", (0, 0), (-1, -1), 5),
                ]
            ),
        )
    )

    def footer(canvas: Canvas, doc: SimpleDocTemplate) -> None:
        canvas.saveState()
        canvas.setFont(FONT, 7)
        canvas.setFillColor(MUTED)
        canvas.drawRightString(page_size[0] - 14 * mm, 8 * mm, f"Page {doc.page}")
        canvas.restoreState()

    doc = SimpleDocTemplate(
        buffer,
        pagesize=page_size,
        leftMargin=14 * mm,
        rightMargin=14 * mm,
        topMargin=14 * mm,
        bottomMargin=16 * mm,
        title="Munal Management Reporting",
    )
    doc.build(story, onFirstPage=footer, onLaterPages=footer)
    return buffer.getvalue()
