from __future__ import annotations

from pathlib import Path
from xml.sax.saxutils import escape

from docx import Document
from docx.oxml.ns import qn
from reportlab.lib import colors
from reportlab.lib.enums import TA_CENTER
from reportlab.lib.pagesizes import LETTER
from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
from reportlab.lib.units import inch
from reportlab.platypus import Image, PageBreak, Paragraph, SimpleDocTemplate, Spacer, Table, TableStyle

ROOT = Path(__file__).resolve().parents[1]
DOCS = ROOT / "evidence-documents"
ASSETS = ROOT / "work" / "pdf-assets"


def footer(canvas, doc):
    canvas.saveState()
    canvas.setFont("Helvetica-Bold", 7.5)
    canvas.setFillColor(colors.HexColor("#6F6D82"))
    canvas.drawString(0.8 * inch, 10.55 * inch, "ENGINEERING COMPETENCY  ·  LEARNING PLANNER")
    canvas.setFont("Helvetica", 8)
    canvas.drawRightString(7.7 * inch, 0.42 * inch, f"Learning Planner · Competency evidence · {doc.page}")
    canvas.restoreState()


def styles():
    base = getSampleStyleSheet()
    return {
        "body": ParagraphStyle("Body", parent=base["BodyText"], fontName="Helvetica", fontSize=9.7, leading=12.3, textColor=colors.HexColor("#212037"), spaceAfter=5),
        "h2": ParagraphStyle("H2", parent=base["Heading2"], fontName="Helvetica-Bold", fontSize=12.2, leading=14, textColor=colors.HexColor("#2E74B5"), spaceBefore=8, spaceAfter=5, keepWithNext=True),
        "title": ParagraphStyle("Title", parent=base["Title"], fontName="Helvetica-Bold", fontSize=22, leading=24, textColor=colors.HexColor("#212037"), alignment=0, spaceAfter=4),
        "appendix_title": ParagraphStyle("AppendixTitle", parent=base["Heading1"], fontName="Helvetica-Bold", fontSize=15, leading=18, textColor=colors.HexColor("#212037"), spaceAfter=6),
        "subtitle": ParagraphStyle("Subtitle", parent=base["BodyText"], fontName="Helvetica", fontSize=11.2, leading=14, textColor=colors.HexColor("#6F6D82"), spaceAfter=10),
        "kicker": ParagraphStyle("Kicker", parent=base["BodyText"], fontName="Helvetica-Bold", fontSize=8.3, leading=10, textColor=colors.HexColor("#6257D9"), spaceAfter=2),
        "caption": ParagraphStyle("Caption", parent=base["BodyText"], fontName="Helvetica-Oblique", fontSize=8.5, leading=11, textColor=colors.HexColor("#6F6D82"), alignment=TA_CENTER, spaceBefore=4),
    }


def rich_text(paragraph) -> str:
    chunks = []
    for run in paragraph.runs:
        text = escape(run.text)
        if not text:
            continue
        if run.bold:
            text = f"<b>{text}</b>"
        if run.italic:
            text = f"<i>{text}</i>"
        chunks.append(text)
    return "".join(chunks) or escape(paragraph.text)


def has_page_break(paragraph) -> bool:
    return bool(paragraph._p.xpath('.//w:br[@w:type="page"]'))


def paragraph_image(paragraph, document, asset_dir: Path):
    blips = paragraph._p.xpath(".//a:blip")
    if not blips:
        return None
    rid = blips[0].get(qn("r:embed"))
    part = document.part.related_parts[rid]
    extension = Path(part.partname).suffix or ".png"
    path = asset_dir / f"image-{len(list(asset_dir.glob('image-*'))) + 1}{extension}"
    path.write_bytes(part.blob)
    image = Image(str(path))
    max_width, max_height = 6.25 * inch, 7.55 * inch
    scale = min(max_width / image.imageWidth, max_height / image.imageHeight)
    image.drawWidth = image.imageWidth * scale
    image.drawHeight = image.imageHeight * scale
    image.hAlign = "CENTER"
    return image


def convert(docx_path: Path):
    document = Document(docx_path)
    asset_dir = ASSETS / docx_path.stem
    asset_dir.mkdir(parents=True, exist_ok=True)
    pdf_path = docx_path.with_suffix(".pdf")
    story = []
    style = styles()

    for paragraph in document.paragraphs:
        if has_page_break(paragraph):
            story.append(PageBreak())
            continue

        image = paragraph_image(paragraph, document, asset_dir)
        if image is not None:
            story.append(Spacer(1, 3))
            story.append(image)
            continue

        text = paragraph.text.strip()
        if not text:
            story.append(Spacer(1, 3))
            continue

        first_size = next((run.font.size.pt for run in paragraph.runs if run.font.size), None)
        if paragraph.style.name == "Heading 2":
            story.append(Paragraph(rich_text(paragraph), style["h2"]))
        elif first_size and first_size >= 20:
            story.append(Paragraph(rich_text(paragraph), style["title"]))
        elif text.startswith("SCREENSHOT APPENDIX"):
            story.append(Paragraph(escape(text), style["kicker"]))
        elif first_size and first_size >= 14:
            story.append(Paragraph(rich_text(paragraph), style["appendix_title"]))
        elif text == "COMPETENCY EVIDENCE BRIEF":
            story.append(Paragraph(escape(text), style["kicker"]))
        elif first_size and first_size >= 11:
            story.append(Paragraph(rich_text(paragraph), style["subtitle"]))
        elif text.startswith("Figure "):
            story.append(Paragraph(escape(text), style["caption"]))
        elif paragraph._p.xpath(".//w:shd"):
            box = Table([[Paragraph(rich_text(paragraph), style["body"])]], colWidths=[6.2 * inch])
            box.setStyle(TableStyle([
                ("BACKGROUND", (0, 0), (-1, -1), colors.HexColor("#F2F4F7")),
                ("BOX", (0, 0), (-1, -1), 0.5, colors.HexColor("#D9DCE4")),
                ("LEFTPADDING", (0, 0), (-1, -1), 10),
                ("RIGHTPADDING", (0, 0), (-1, -1), 10),
                ("TOPPADDING", (0, 0), (-1, -1), 7),
                ("BOTTOMPADDING", (0, 0), (-1, -1), 4),
            ]))
            story.extend([Spacer(1, 3), box, Spacer(1, 5)])
        else:
            story.append(Paragraph(rich_text(paragraph), style["body"]))

    pdf = SimpleDocTemplate(
        str(pdf_path), pagesize=LETTER, rightMargin=0.8 * inch, leftMargin=0.8 * inch,
        topMargin=0.72 * inch, bottomMargin=0.65 * inch, title=document.core_properties.title,
        author="Learning Planner project", subject=document.core_properties.subject,
    )
    pdf.build(story, onFirstPage=footer, onLaterPages=footer)


def main():
    ASSETS.mkdir(parents=True, exist_ok=True)
    for path in sorted(DOCS.glob("0*-*.docx")):
        convert(path)


if __name__ == "__main__":
    main()
