from __future__ import annotations

import json
import re
from html import escape
from pathlib import Path

from reportlab.lib import colors
from reportlab.lib.enums import TA_CENTER, TA_JUSTIFY, TA_LEFT
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
from reportlab.lib.units import mm
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.platypus import (
    BaseDocTemplate,
    Frame,
    Image,
    KeepTogether,
    ListFlowable,
    ListItem,
    LongTable,
    PageBreak,
    PageTemplate,
    Paragraph,
    Spacer,
    Table,
    TableStyle,
)

ROOT = Path(__file__).resolve().parents[2]
DIR = Path(__file__).resolve().parent
META = json.loads((DIR / "paper-metadata.json").read_text(encoding="utf-8"))
MARKDOWN = (DIR / "paper.md").read_text(encoding="utf-8").replace("\r\n", "\n")
FONT_PATH = ROOT / "public" / "fonts" / "NotoSansTC-Regular.ttf"
PDF_PATH = DIR / "paper.pdf"
CONTENT_PATH = DIR / "paper-content.json"


def clean_inline(text: str) -> str:
    text = re.sub(r"\[([^\]]+)\]\((https?://[^)]+)\)", r"\1", text)
    text = re.sub(r"\*\*(.+?)\*\*", r"\1", text)
    text = re.sub(r"`([^`]+)`", r"\1", text)
    return text.strip()


def paragraph_html(text: str) -> str:
    value = escape(text)
    value = re.sub(r"\[([^\]]+)\]\((https?://[^)]+)\)", r'<a href="\2">\1</a>', value)
    value = re.sub(r"\*\*(.+?)\*\*", r"<b>\1</b>", value)
    value = re.sub(r"`([^`]+)`", r"<font name='NotoTC'>\1</font>", value)
    return value


def section_lines() -> list[tuple[str, list[str]]]:
    sections: list[tuple[str, list[str]]] = []
    current = "front"
    buffer: list[str] = []
    for line in MARKDOWN.splitlines():
        if line.startswith("## "):
            if current != "front":
                sections.append((current, buffer))
            current = line[3:].strip()
            buffer = []
        else:
            buffer.append(line)
    if current != "front":
        sections.append((current, buffer))
    return sections


SECTIONS = section_lines()
SECTION_MAP = {title: lines for title, lines in SECTIONS}


def paragraphs(lines: list[str]) -> list[str]:
    output: list[str] = []
    buf: list[str] = []
    for raw in lines:
        line = raw.strip()
        if not line:
            if buf:
                output.append(" ".join(buf))
                buf = []
        elif not line.startswith(("### ", "|", "![", "- ")) and not re.match(r"^\d+\.\s", line):
            buf.append(line)
        elif buf:
            output.append(" ".join(buf))
            buf = []
    if buf:
        output.append(" ".join(buf))
    return output


def parse_blocks(lines: list[str]) -> list[dict]:
    blocks: list[dict] = []
    i = 0
    paragraph_buffer: list[str] = []

    def flush() -> None:
        nonlocal paragraph_buffer
        if paragraph_buffer:
            blocks.append({"type": "paragraph", "text": clean_inline(" ".join(paragraph_buffer))})
            paragraph_buffer = []

    while i < len(lines):
        line = lines[i].strip()
        if not line:
            flush()
            i += 1
            continue
        if line.startswith("### "):
            flush()
            blocks.append({"type": "heading", "text": clean_inline(line[4:])})
            i += 1
            continue
        image = re.match(r"!\[([^\]]+)\]\(([^)]+)\)", line)
        if image:
            flush()
            blocks.append({"type": "figure", "caption": clean_inline(image.group(1)), "filename": Path(image.group(2)).name})
            i += 1
            continue
        if line.startswith("|") and i + 1 < len(lines) and "---" in lines[i + 1]:
            flush()
            table_lines: list[str] = []
            while i < len(lines) and lines[i].strip().startswith("|"):
                table_lines.append(lines[i].strip())
                i += 1
            rows = [[clean_inline(cell) for cell in row.strip("|").split("|")] for row in table_lines]
            blocks.append({"type": "table", "headers": rows[0], "rows": rows[2:]})
            continue
        ordered = re.match(r"^\d+\.\s+(.+)", line)
        if ordered:
            flush()
            items: list[str] = []
            while i < len(lines):
                match = re.match(r"^\d+\.\s+(.+)", lines[i].strip())
                if not match:
                    break
                items.append(clean_inline(match.group(1)))
                i += 1
            blocks.append({"type": "list", "ordered": True, "items": items})
            continue
        if line.startswith("- "):
            flush()
            items: list[str] = []
            while i < len(lines) and lines[i].strip().startswith("- "):
                items.append(clean_inline(lines[i].strip()[2:]))
                i += 1
            blocks.append({"type": "list", "ordered": False, "items": items})
            continue
        paragraph_buffer.append(line)
        i += 1
    flush()
    return blocks


def first_keyword_line(lines: list[str]) -> list[str]:
    for line in lines:
        if line.strip().startswith("**關鍵詞：**"):
            value = clean_inline(line).replace("關鍵詞：", "", 1)
            return [item.strip() for item in re.split(r"[、，,]", value) if item.strip()]
    return []


abstract_zh_parts = [p for p in paragraphs(SECTION_MAP["摘要"]) if not clean_inline(p).startswith("關鍵詞：")]
abstract_en_parts = paragraphs(SECTION_MAP["Abstract"])
references: list[list[str]] = []
for line in SECTION_MAP["參考文獻與資料來源"]:
    match = re.match(r"^\d+\.\s+(.+)", line.strip())
    if match:
        label = clean_inline(match.group(1))
        url_match = re.search(r"\((https?://[^)]+)\)", line)
        references.append([label, url_match.group(1) if url_match else ""])

web_sections = []
for title, lines in SECTIONS:
    if re.match(r"^\d+\.\s", title):
        web_sections.append({"id": f"section-{len(web_sections) + 1}", "title": title, "blocks": parse_blocks(lines)})

content = {
    "reportNumber": META["reportNumber"],
    "version": META["version"],
    "date": META["datePublished"],
    "revisionDate": META["dateModified"],
    "revisionNote": "版本1.0：完成20筆申請軌、18個官方網址、六個合成企業、120組資格情境、三種權重、十二段證據閘門、十項安全政策測試、六張原創圖表與公開重現資料包。",
    "doi": META["doi"],
    "zenodoUrl": META["zenodoUrl"],
    "status": "公開工作論文・未經外部同儕審查",
    "license": META["license"],
    "licenseUrl": META["licenseUrl"],
    "titleZh": META["title"],
    "titleEn": META["titleEnglish"],
    "author": META["author"]["name"],
    "authorEn": META["author"]["nameEnglish"],
    "orcid": META["author"]["orcid"],
    "email": META["author"]["email"],
    "abstractZh": "\n\n".join(clean_inline(p) for p in abstract_zh_parts),
    "abstractEn": "\n\n".join(clean_inline(p) for p in abstract_en_parts),
    "keywords": first_keyword_line(SECTION_MAP["摘要"]),
    "highlights": [["20", "申請軌"], ["120", "企業—機會情境"], ["10/10", "安全政策測試"], ["6", "原創圖表"]],
    "sections": web_sections,
    "references": references,
}
CONTENT_PATH.write_text(json.dumps(content, ensure_ascii=False, indent=2), encoding="utf-8")


pdfmetrics.registerFont(TTFont("NotoTC", str(FONT_PATH)))
styles = getSampleStyleSheet()
base = ParagraphStyle("BodyTC", fontName="NotoTC", fontSize=9.1, leading=15.1, textColor=colors.HexColor("#241c17"), alignment=TA_JUSTIFY, spaceAfter=7)
h1 = ParagraphStyle("TitleTC", parent=base, fontSize=20, leading=31, alignment=TA_CENTER, spaceAfter=12)
h2 = ParagraphStyle("H2TC", parent=base, fontSize=15, leading=22, textColor=colors.HexColor("#6f3519"), spaceBefore=12, spaceAfter=9, keepWithNext=True)
h3 = ParagraphStyle("H3TC", parent=base, fontSize=11.5, leading=18, spaceBefore=9, spaceAfter=5, keepWithNext=True)
meta_style = ParagraphStyle("MetaTC", parent=base, fontSize=8.5, leading=14, alignment=TA_CENTER, textColor=colors.HexColor("#625b55"))
caption_style = ParagraphStyle("CaptionTC", parent=base, fontSize=7.6, leading=11, alignment=TA_CENTER, textColor=colors.HexColor("#625b55"), spaceAfter=8)
cell_style = ParagraphStyle("CellTC", parent=base, fontSize=7.2, leading=10, alignment=TA_LEFT, spaceAfter=0)
reference_style = ParagraphStyle("ReferenceTC", parent=base, fontSize=7.0, leading=9.5, alignment=TA_LEFT, spaceAfter=1)


def footer(canvas, doc) -> None:
    canvas.saveState()
    canvas.setFont("NotoTC", 7)
    canvas.setFillColor(colors.HexColor("#857b73"))
    canvas.drawString(16 * mm, 9 * mm, f"{META['reportNumber']}｜公開工作論文 v{META['version']}｜CC BY 4.0｜未經外部同儕審查")
    canvas.drawRightString(A4[0] - 16 * mm, 9 * mm, str(doc.page))
    canvas.restoreState()


doc = BaseDocTemplate(str(PDF_PATH), pagesize=A4, leftMargin=16 * mm, rightMargin=16 * mm, topMargin=16 * mm, bottomMargin=17 * mm, title=META["title"], author=META["author"]["name"])
frame = Frame(doc.leftMargin, doc.bottomMargin, doc.width, doc.height, id="normal")
doc.addPageTemplates([PageTemplate(id="paper", frames=[frame], onPage=footer)])
story = [Spacer(1, 12 * mm), Paragraph(f"{META['reportNumber']}｜公開工作論文｜版本 {META['version']}", meta_style), Spacer(1, 13 * mm), Paragraph(escape(META["title"]), h1), Paragraph(escape(META["titleEnglish"]), meta_style), Spacer(1, 13 * mm), Paragraph(f"{META['author']['name']} / {META['author']['nameEnglish']}", meta_style), Spacer(1, 4 * mm), Paragraph(f"ORCID：{META['author']['orcid']}｜{META['author']['email']}<br/>公開日期：{META['datePublished']}｜未經外部同儕審查<br/>DOI：<a href='https://doi.org/{META['doi']}'>{META['doi']}</a><br/>CC BY 4.0（僅適用於作者有權授權內容）", meta_style), Spacer(1, 17 * mm)]
cover_box = Table([[Paragraph("<b>研究結論摘要</b><br/><br/>可行產品不是「AI幫你拿補助」，而是具官方來源追溯、三態初步資格閘門、企業證據庫、權限分離與人工提交控制的決策支援系統。核心原則是：自動找、自動比、自動起草；證據不足即停，永不自動送件。本文分數不是核准率。", ParagraphStyle("CoverBox", parent=base, fontSize=10, leading=17, alignment=TA_CENTER))]], colWidths=[155 * mm])
cover_box.setStyle(TableStyle([("BACKGROUND", (0, 0), (-1, -1), colors.HexColor("#f5eadf")), ("BOX", (0, 0), (-1, -1), 0.7, colors.HexColor("#d9cbbd")), ("LEFTPADDING", (0, 0), (-1, -1), 9 * mm), ("RIGHTPADDING", (0, 0), (-1, -1), 9 * mm), ("TOPPADDING", (0, 0), (-1, -1), 6 * mm), ("BOTTOMPADDING", (0, 0), (-1, -1), 6 * mm)]))
story.extend([cover_box, Spacer(1, 12 * mm), Paragraph("研究定位：20筆目的性選取官方徵案文件之探索性內容分析，加上六個合成企業情境的規則原型驗證。本文不是全臺補助普查、正式資格認定、核准預測、代辦服務、法律或財務建議。所有期限與資格在行動前均須回到最新官方公告與完整附件查核。", meta_style), PageBreak()])


def add_table(rows: list[list[str]]) -> None:
    count = len(rows[0])
    weights = [1.55] + [1.0] * (count - 1)
    total = sum(weights)
    widths = [doc.width * w / total for w in weights]
    formatted = [[Paragraph(paragraph_html(cell), cell_style) for cell in row] for row in rows]
    table = LongTable(formatted, colWidths=widths, repeatRows=1, splitByRow=1, hAlign="LEFT")
    table.setStyle(TableStyle([("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#f5eadf")), ("GRID", (0, 0), (-1, -1), 0.35, colors.HexColor("#d9cbbd")), ("VALIGN", (0, 0), (-1, -1), "TOP"), ("LEFTPADDING", (0, 0), (-1, -1), 4), ("RIGHTPADDING", (0, 0), (-1, -1), 4), ("TOPPADDING", (0, 0), (-1, -1), 4), ("BOTTOMPADDING", (0, 0), (-1, -1), 4)]))
    story.extend([table, Spacer(1, 4 * mm)])


for title, lines in SECTIONS:
    if re.match(r"^(4|5|6|7|8|9|10)\.\s", title):
        story.append(PageBreak())
    story.append(Paragraph(escape(title), h2))
    i = 0
    buf: list[str] = []

    def flush_pdf() -> None:
        nonlocal_buf = list(buf)
        buf.clear()
        if nonlocal_buf:
            story.append(Paragraph(paragraph_html(" ".join(nonlocal_buf)), base))

    while i < len(lines):
        line = lines[i].strip()
        if not line:
            flush_pdf()
            i += 1
            continue
        if line.startswith("### "):
            flush_pdf()
            story.append(Paragraph(paragraph_html(line[4:]), h3))
            i += 1
            continue
        image_match = re.match(r"!\[([^\]]+)\]\(([^)]+)\)", line)
        if image_match:
            flush_pdf()
            image_path = DIR / image_match.group(2)
            img = Image(str(image_path))
            max_width, max_height = doc.width, 165 * mm
            scale = min(max_width / img.imageWidth, max_height / img.imageHeight)
            img.drawWidth = img.imageWidth * scale
            img.drawHeight = img.imageHeight * scale
            story.append(KeepTogether([img, Paragraph(paragraph_html(image_match.group(1)), caption_style)]))
            i += 1
            continue
        if line.startswith("|") and i + 1 < len(lines) and "---" in lines[i + 1]:
            flush_pdf()
            table_lines = []
            while i < len(lines) and lines[i].strip().startswith("|"):
                table_lines.append(lines[i].strip())
                i += 1
            cells = [[clean_inline(cell) for cell in row.strip("|").split("|")] for row in table_lines]
            add_table([cells[0]] + cells[2:])
            continue
        ordered = re.match(r"^\d+\.\s+(.+)", line)
        if ordered:
            flush_pdf()
            items = []
            list_style = reference_style if title == "參考文獻與資料來源" else base
            while i < len(lines):
                match = re.match(r"^\d+\.\s+(.+)", lines[i].strip())
                if not match:
                    break
                items.append(ListItem(Paragraph(paragraph_html(match.group(1)), list_style), leftIndent=10))
                i += 1
            story.append(ListFlowable(items, bulletType="1", start="1", leftIndent=18, bulletFontName="NotoTC", bulletFontSize=8))
            continue
        if line.startswith("- "):
            flush_pdf()
            items = []
            while i < len(lines) and lines[i].strip().startswith("- "):
                items.append(ListItem(Paragraph(paragraph_html(lines[i].strip()[2:]), base), leftIndent=10))
                i += 1
            story.append(ListFlowable(items, bulletType="bullet", leftIndent=18, bulletFontName="NotoTC", bulletFontSize=8))
            continue
        buf.append(line)
        i += 1
    flush_pdf()

doc.build(story)
print(json.dumps({"pdf": str(PDF_PATH), "content": str(CONTENT_PATH)}, ensure_ascii=False))
