from pathlib import Path
from zipfile import ZIP_DEFLATED, ZipFile
from xml.sax.saxutils import escape


OUT = Path("Pengujian_Blackbox_ConfiVoice.docx")

TITLE = (
    "Klasifikasi Tingkat Percaya Diri Berdasarkan Analisis Suara "
    "Menggunakan Pendekatan Machine Learning Berbasis Mobile"
)

USER_ROWS = [
    ("1", "Halaman Login", "Pengguna membuka aplikasi ConfiVoice.", "Sistem menampilkan form login, tombol daftar akun, dan pengaturan endpoint API."),
    ("2", "Halaman Daftar", "Pengguna memilih menu daftar dan mengisi nama lengkap, username, password, serta konfirmasi password.", "Sistem membuat akun baru dan mengarahkan pengguna kembali ke halaman login."),
    ("3", "Halaman Login", "Pengguna mengisi username dan password yang valid.", "Sistem berhasil masuk dan mengarahkan pengguna ke halaman utama analisis."),
    ("4", "Halaman Utama", "Pengguna mengisi nama siswa dan memilih jenis kelamin.", "Sistem menerima data siswa tanpa menampilkan error."),
    ("5", "Halaman Utama", "Pengguna memilih file audio dari perangkat.", "Sistem menampilkan audio yang dipilih dan menampilkan preview gelombang suara."),
    ("6", "Halaman Utama", "Pengguna menekan tombol rekam, berbicara, lalu menghentikan rekaman.", "Sistem menyimpan rekaman dalam format audio dan menampilkan preview hasil rekaman."),
    ("7", "Halaman Prediksi", "Pengguna menekan tombol Prediksi setelah data siswa dan audio sudah lengkap.", "Sistem mengirim audio ke backend dan menampilkan hasil klasifikasi percaya diri atau tidak percaya diri beserta nilai persentase PD dan TPD."),
    ("8", "Halaman Prediksi", "Pengguna menekan tombol Prediksi ketika nama, jenis kelamin, atau audio belum lengkap.", "Sistem menampilkan pesan peringatan agar pengguna melengkapi data terlebih dahulu."),
    ("9", "Halaman Hasil Prediksi", "Pengguna menekan tombol Simpan Hasil setelah hasil prediksi muncul.", "Sistem menyimpan hasil analisis ke database dan mengosongkan form untuk pengujian siswa berikutnya."),
    ("10", "Halaman Lihat Analisis", "Pengguna membuka menu Lihat Analisis.", "Sistem menampilkan daftar riwayat hasil analisis yang sudah tersimpan berdasarkan data siswa."),
    ("11", "Halaman Detail Analisis", "Pengguna memilih salah satu data siswa pada riwayat analisis.", "Sistem menampilkan detail hasil prediksi, jenis kelamin, label prediksi, nilai PD, dan nilai TPD."),
    ("12", "Logout", "Pengguna menekan tombol logout.", "Sistem mengakhiri sesi pengguna dan kembali ke halaman login."),
]

ADMIN_ROWS = [
    ("1", "Halaman Login Admin", "Admin membuka halaman login web admin.", "Sistem menampilkan form masuk admin dan pilihan daftar akun."),
    ("2", "Halaman Register Admin", "Admin mengisi nama lengkap, username, password, dan konfirmasi password.", "Sistem membuat akun admin baru dan mengarahkan admin ke halaman login."),
    ("3", "Halaman Login Admin", "Admin mengisi username dan password yang valid.", "Sistem berhasil masuk dan mengarahkan admin ke dashboard."),
    ("4", "Dashboard Admin", "Admin membuka halaman dashboard.", "Sistem menampilkan statistik total prediksi, jumlah PD, jumlah TPD, rata-rata PD, serta tabel hasil analisis."),
    ("5", "Filter Dashboard", "Admin menggunakan pencarian atau filter label pada data prediksi.", "Sistem menampilkan data hasil analisis sesuai kata kunci atau label yang dipilih."),
    ("6", "Tambah Data Prediksi", "Admin membuka halaman tambah data dan mengisi form hasil analisis secara manual.", "Sistem menyimpan data prediksi baru ke database."),
    ("7", "Edit Data Prediksi", "Admin membuka halaman edit pada salah satu data prediksi dan mengubah isian data.", "Sistem memperbarui data prediksi pada database."),
    ("8", "Hapus Data Prediksi", "Admin menekan tombol hapus pada salah satu data prediksi.", "Sistem menampilkan konfirmasi dan menghapus data setelah admin menyetujui."),
    ("9", "Detail Siswa", "Admin membuka detail riwayat salah satu siswa.", "Sistem menampilkan kumpulan hasil analisis berdasarkan nama siswa yang dipilih."),
    ("10", "Kelola User", "Admin membuka halaman kelola user.", "Sistem menampilkan daftar user dan form untuk menambahkan user."),
    ("11", "Edit/Hapus User", "Admin mengubah atau menghapus data user.", "Sistem memperbarui atau menghapus data user dari database."),
    ("12", "Logout Admin", "Admin memilih menu logout.", "Sistem menghapus sesi admin dan mengarahkan kembali ke halaman login admin."),
]

GRID = [550, 2050, 3600, 5050, 1100, 1350]


def rpr(bold=False, size=24):
    bold_xml = "<w:b/>" if bold else ""
    return (
        f"<w:rPr><w:rFonts w:ascii=\"Times New Roman\" w:hAnsi=\"Times New Roman\" "
        f"w:cs=\"Times New Roman\"/><w:sz w:val=\"{size}\"/><w:szCs w:val=\"{size}\"/>"
        f"{bold_xml}</w:rPr>"
    )


def paragraph(text="", align=None, bold=False, size=24, spacing_after=120, page_break=False):
    jc = f"<w:jc w:val=\"{align}\"/>" if align else ""
    pb = "<w:pageBreakBefore/>" if page_break else ""
    ppr = f"<w:pPr>{pb}{jc}<w:spacing w:after=\"{spacing_after}\"/></w:pPr>"
    if text == "":
        return f"<w:p>{ppr}</w:p>"
    return (
        f"<w:p>{ppr}<w:r>{rpr(bold=bold, size=size)}"
        f"<w:t xml:space=\"preserve\">{escape(text)}</w:t></w:r></w:p>"
    )


def run_text(text, bold=False, size=24):
    return f"<w:r>{rpr(bold=bold, size=size)}<w:t xml:space=\"preserve\">{escape(text)}</w:t></w:r>"


def cell(text="", width=1000, align=None, bold=False, grid_span=None, vmerge=None, center_v=True):
    span = f"<w:gridSpan w:val=\"{grid_span}\"/>" if grid_span else ""
    merge = ""
    if vmerge == "restart":
        merge = "<w:vMerge w:val=\"restart\"/>"
    elif vmerge == "continue":
        merge = "<w:vMerge/>"
    valign = "<w:vAlign w:val=\"center\"/>" if center_v else ""
    p_align = f"<w:jc w:val=\"{align}\"/>" if align else ""
    p = (
        f"<w:p><w:pPr>{p_align}<w:spacing w:after=\"0\"/></w:pPr>"
        f"{run_text(text, bold=bold, size=22)}</w:p>"
    )
    return (
        "<w:tc>"
        f"<w:tcPr><w:tcW w:w=\"{width}\" w:type=\"dxa\"/>{span}{merge}{valign}</w:tcPr>"
        f"{p}"
        "</w:tc>"
    )


def row(cells):
    return f"<w:tr>{''.join(cells)}</w:tr>"


def meta(role):
    return "".join(
        [
            paragraph(f"Judul   : {TITLE}", bold=True, spacing_after=20),
            paragraph("Nama    : ............................................................", bold=True, spacing_after=20),
            paragraph(f"Jabatan : {role}", bold=True, spacing_after=220),
        ]
    )


def blackbox_table(rows):
    grid_xml = "".join(f"<w:gridCol w:w=\"{w}\"/>" for w in GRID)
    table_props = """
      <w:tblPr>
        <w:tblW w:w="13700" w:type="dxa"/>
        <w:tblInd w:w="0" w:type="dxa"/>
        <w:tblBorders>
          <w:top w:val="single" w:sz="8" w:space="0" w:color="000000"/>
          <w:left w:val="single" w:sz="8" w:space="0" w:color="000000"/>
          <w:bottom w:val="single" w:sz="8" w:space="0" w:color="000000"/>
          <w:right w:val="single" w:sz="8" w:space="0" w:color="000000"/>
          <w:insideH w:val="single" w:sz="8" w:space="0" w:color="000000"/>
          <w:insideV w:val="single" w:sz="8" w:space="0" w:color="000000"/>
        </w:tblBorders>
        <w:tblCellMar>
          <w:top w:w="120" w:type="dxa"/>
          <w:left w:w="120" w:type="dxa"/>
          <w:bottom w:w="120" w:type="dxa"/>
          <w:right w:w="120" w:type="dxa"/>
        </w:tblCellMar>
      </w:tblPr>
    """
    header1 = row(
        [
            cell("No.", GRID[0], align="center", bold=True, vmerge="restart"),
            cell("Halaman", GRID[1], align="center", bold=True, vmerge="restart"),
            cell("Skenario", GRID[2], align="center", bold=True, vmerge="restart"),
            cell("Hasil yang Diharapkan", GRID[3], align="center", bold=True, vmerge="restart"),
            cell("Keterangan", GRID[4] + GRID[5], align="center", bold=True, grid_span=2),
        ]
    )
    header2 = row(
        [
            cell("", GRID[0], vmerge="continue"),
            cell("", GRID[1], vmerge="continue"),
            cell("", GRID[2], vmerge="continue"),
            cell("", GRID[3], vmerge="continue"),
            cell("Berhasil", GRID[4], align="center", bold=True),
            cell("Tidak Berhasil", GRID[5], align="center", bold=True),
        ]
    )
    body = []
    for no, page, scenario, expected in rows:
        body.append(
            row(
                [
                    cell(no, GRID[0], align="center"),
                    cell(page, GRID[1]),
                    cell(scenario, GRID[2]),
                    cell(expected, GRID[3]),
                    cell("✓", GRID[4], align="center", bold=True),
                    cell("", GRID[5], align="center"),
                ]
            )
        )
    return f"<w:tbl>{table_props}<w:tblGrid>{grid_xml}</w:tblGrid>{header1}{header2}{''.join(body)}</w:tbl>"


def section(title, role, rows, first=False):
    return "".join(
        [
            paragraph(title, align="center", bold=True, size=32, spacing_after=360, page_break=not first),
            meta(role),
            blackbox_table(rows),
            paragraph("", spacing_after=240),
        ]
    )


DOCUMENT_XML = f"""<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
  xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
  <w:body>
    {section("Pengujian Blackbox User", "User/Guru", USER_ROWS, first=True)}
    {section("Pengujian Blackbox Web Admin", "Admin", ADMIN_ROWS)}
    <w:sectPr>
      <w:pgSz w:w="15840" w:h="12240" w:orient="landscape"/>
      <w:pgMar w:top="720" w:right="720" w:bottom="720" w:left="720" w:header="360" w:footer="360" w:gutter="0"/>
    </w:sectPr>
  </w:body>
</w:document>
"""


CONTENT_TYPES = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
  <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
  <Default Extension="xml" ContentType="application/xml"/>
  <Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
  <Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
  <Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
</Types>
"""

RELS = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
  <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
  <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
  <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
</Relationships>
"""

DOC_RELS = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"/>
"""

CORE = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:dcterms="http://purl.org/dc/terms/"
  xmlns:dcmitype="http://purl.org/dc/dcmitype/"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <dc:title>Pengujian Blackbox ConfiVoice</dc:title>
  <dc:creator>Codex</dc:creator>
  <cp:lastModifiedBy>Codex</cp:lastModifiedBy>
</cp:coreProperties>
"""

APP = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"
  xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
  <Application>Codex</Application>
</Properties>
"""


with ZipFile(OUT, "w", compression=ZIP_DEFLATED) as docx:
    docx.writestr("[Content_Types].xml", CONTENT_TYPES)
    docx.writestr("_rels/.rels", RELS)
    docx.writestr("word/_rels/document.xml.rels", DOC_RELS)
    docx.writestr("word/document.xml", DOCUMENT_XML)
    docx.writestr("docProps/core.xml", CORE)
    docx.writestr("docProps/app.xml", APP)

print(OUT)
