import pandas as pd

from pathlib import Path

from datetime import datetime

from docx import Document

from docx.shared import Pt, Inches, Cm

from docx.enum.text import WD_ALIGN_PARAGRAPH

from docx.oxml import OxmlElement

from docx.oxml.ns import qn

from docx.enum.table import WD_TABLE_ALIGNMENT

import zipfile

import re


# ---------- Utils ----------


def safe_filename(name: str) -> str:

    s = str(name)

    s = s.replace('/', '/').replace('\\', '\')

    s = re.sub(r'[\:*?"<>|]', '_', s)

    return s



def add_hyperlink(paragraph, url, text=None):

    if text is None:

        text = url

    text = str(text)

    part = paragraph.part

    r_id = part.relate_to(url, reltype="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink", is_external=True)

    hyperlink = OxmlElement('w:hyperlink')

    hyperlink.set(qn('r:id'), r_id)

    new_run = OxmlElement('w:r')

    rPr = OxmlElement('w:rPr')

    u = OxmlElement('w:u')

    u.set(qn('w:val'), 'single')

    rPr.append(u)

    color = OxmlElement('w:color')

    color.set(qn('w:val'), '0563C1')

    rPr.append(color)

    new_run.append(rPr)

    text_elem = OxmlElement('w:t')

    text_elem.text = text

    new_run.append(text_elem)

    hyperlink.append(new_run)

    paragraph._p.append(hyperlink)

    paragraph.add_run("")



def is_image_url(url):

    return bool(re.search(r"\.(png|jpg|jpeg|gif|webp)(\?|$)", str(url or ''), re.I))



def set_doc_margins(doc: Document, top=1.8, bottom=1.8, left=1.8, right=1.8):

    sect = doc.sections[0]

    sect.top_margin = Cm(top)

    sect.bottom_margin = Cm(bottom)

    sect.left_margin = Cm(left)

    sect.right_margin = Cm(right)



def add_header_footer(doc: Document, header_text: str):

    sect = doc.sections[0]

    # Header

    hdr = sect.header

    p = hdr.paragraphs[0]

    p.text = header_text

    p.alignment = WD_ALIGN_PARAGRAPH.CENTER

    p.runs[0].font.size = Pt(10)

    # Footer with PAGE field

    f = sect.footer.paragraphs[0]

    f.alignment = WD_ALIGN_PARAGRAPH.CENTER

    run = f.add_run("第 ")

    run.font.size = Pt(10)

    r_field = f.add_run()

    fldChar1 = OxmlElement('w:fldChar'); fldChar1.set(qn('w:fldCharType'), 'begin')

    instrText = OxmlElement('w:instrText'); instrText.text = 'PAGE'

    fldChar2 = OxmlElement('w:fldChar'); fldChar2.set(qn('w:fldCharType'), 'end')

    r_field._r.append(fldChar1)

    r_field._r.append(instrText)

    r_field._r.append(fldChar2)

    f.add_run(" 頁").font.size = Pt(10)



def set_cell_border(cell, **kwargs):

    """Set cell border: set_cell_border(cell, top=(sz, val, color), bottom=..., left=..., right=...)"""

    tc = cell._tc

    tcPr = tc.get_or_add_tcPr()

    tcBorders = tcPr.first_child_found_in('w:tcBorders')

    if tcBorders is None:

        tcBorders = OxmlElement('w:tcBorders')

        tcPr.append(tcBorders)

    for edge in ('top', 'left', 'bottom', 'right'):

        if edge in kwargs:

            edge_data = kwargs[edge]

            tag = 'w:' + edge

            element = tcBorders.find(qn(tag))

            if element is None:

                element = OxmlElement(tag)

                tcBorders.append(element)

            sz, val, color = edge_data

            element.set(qn('w:val'), val)

            element.set(qn('w:sz'), str(sz))

            element.set(qn('w:color'), color)



def doc_add_title_block(doc: Document, section_name: str, subtitle: str):

    title_p = doc.add_paragraph()

    title_p.alignment = WD_ALIGN_PARAGRAPH.CENTER

    r = title_p.add_run(str(section_name))

    r.bold = True

    r.font.size = Pt(18)

    sub_p = doc.add_paragraph(subtitle)

    sub_p.alignment = WD_ALIGN_PARAGRAPH.CENTER

    sub_p.runs[0].font.size = Pt(11)

    head = doc.add_paragraph("班級:______________    姓名:______________    座號:______")

    head.alignment = WD_ALIGN_PARAGRAPH.LEFT

    head.paragraph_format.space_after = Pt(6)



def add_question_block_v4(doc: Document, idx: int, item: dict, with_answer: bool):

    topic = item.get('主題') or item.get('topic') or ''

    desc = item.get('附件說明') or item.get('desc') or ''

    url = item.get('附件連結') or item.get('url') or ''


    # 題號 + 主題

    p = doc.add_paragraph()

    p.paragraph_format.space_before = Pt(6)

    r1 = p.add_run(f"第 {idx} 題  ")

    r1.bold = True

    r1.font.size = Pt(12)

    if topic:

        p.add_run("主題:").bold = True

        p.add_run(str(topic))


    # 題面連結

    m = doc.add_paragraph("題面連結:")

    if url:

        if is_image_url(url):

            m.add_run("(圖片)")

        add_hyperlink(m, str(url), str(url))

    else:

        m.add_run("(無附件連結)")


    if with_answer:

        ap = doc.add_paragraph("參考解答:")

        ap.add_run(str(desc) if str(desc).strip() else "(此題答案待補)")

    else:

        # 12 行橫線 + 批改欄(以邊框畫線,線條更清楚)

        tbl = doc.add_table(rows=1, cols=2)

        tbl.alignment = WD_TABLE_ALIGNMENT.CENTER

        tbl.columns[0].width = Inches(5.5)

        tbl.columns[1].width = Inches(1.6)


        calc_cell = tbl.cell(0, 0)

        inner = calc_cell.add_table(rows=12, cols=1)

        for i, row in enumerate(inner.rows):

            row.height = Cm(0.8)

            c = row.cells[0]

            # 底線(加粗)

            set_cell_border(c, bottom=(12, 'single', 'AAAAAA'))

            # 清空段落的多餘間距

            p2 = c.paragraphs[0]

            p2.paragraph_format.space_after = Pt(0)

            p2.paragraph_format.space_before = Pt(0)


        # 批改欄

        mark = tbl.cell(0, 1)

        mp = mark.paragraphs[0]

        mp.add_run("配分:  / 得分:  ").bold = True

        mark.add_paragraph("□ 對  □ 誤  □ 部分給分")

        mark.add_paragraph("批註:")

        for _ in range(3):

            # 使用底線段落供書寫

            pp = mark.add_paragraph(" ")

            run = pp.runs[0]

            run.font.underline = True

    doc.add_paragraph("")



# ---------- Read CSV ----------

csv_path = Path('5.csv')

try:

    df = pd.read_csv(csv_path, encoding='utf-8')

except Exception:

    df = pd.read_csv(csv_path, encoding='utf-8-sig')


if '區段' not in df.columns:

    raise RuntimeError('CSV 缺少「區段」欄位,無法分組匯出。')


stamp = datetime.now().strftime('%Y%m%d_%H%M%S')

root = Path(f'批次匯出_v4_{stamp}')

docx_dir = root / 'docx'

docx_dir.mkdir(parents=True, exist_ok=True)


exported_files = []


for section, g in df.groupby('區段'):

    items = g.to_dict(orient='records')

    sec_safe = safe_filename(section)


    # 練習卷

    doc = Document()

    set_doc_margins(doc, 1.8, 1.8, 1.8, 1.8)

    add_header_footer(doc, f"{section}(練習卷)")

    doc_add_title_block(doc, section, "(練習卷)")

    for i, it in enumerate(items, start=1):

        add_question_block_v4(doc, i, it, with_answer=False)

    fn_practice = docx_dir / f"{sec_safe}_練習卷.docx"

    doc.save(fn_practice)

    exported_files.append(str(fn_practice))


    # 答案卷

    doc_a = Document()

    set_doc_margins(doc_a, 1.8, 1.8, 1.8, 1.8)

    add_header_footer(doc_a, f"{section}(答案卷/教師用)")

    doc_add_title_block(doc_a, section, "(答案卷/教師用)")

    for i, it in enumerate(items, start=1):

        add_question_block_v4(doc_a, i, it, with_answer=True)

    fn_answer = docx_dir / f"{sec_safe}_答案卷.docx"

    doc_a.save(fn_answer)

    exported_files.append(str(fn_answer))


zip_path = root / f"練習卷_答案卷_打包.zip"

with zipfile.ZipFile(zip_path, 'w', compression=zipfile.ZIP_DEFLATED) as z:

    for f in exported_files:

        z.write(f, arcname=Path(f).name)


print({'export_folder': str(root), 'zip': str(zip_path), 'docx_files_count': len(exported_files)})