"""Tests for scripts/docx_append.py — faculty Office-artifact preservation.

Guardrail 4: Faculty .docx artifacts are copied then minimally extended,
never rebuilt from scratch.

Tests:
- copy_docx preserves all original paragraphs and tables.
- append_heading adds a heading without removing any prior content.
- append_table adds a table without removing any prior content; original
  paragraphs/tables remain; new table rows are present.
"""

import pathlib
import sys
import tempfile

import pytest

SCRIPTS_DIR = pathlib.Path(__file__).resolve().parents[1] / "scripts"
sys.path.insert(0, str(SCRIPTS_DIR))

try:
    from docx import Document
    from docx_append import copy_docx, append_heading, append_table
    _DOCX_AVAILABLE = True
except ImportError:
    _DOCX_AVAILABLE = False

pytestmark = pytest.mark.skipif(
    not _DOCX_AVAILABLE,
    reason="python-docx not installed",
)


def _make_fixture_docx(tmp_path, name="source.docx"):
    """Create a tiny faculty-authored .docx with 2 paragraphs and 1 table."""
    doc = Document()
    doc.add_paragraph("Faculty paragraph one.")
    doc.add_paragraph("Faculty paragraph two.")
    table = doc.add_table(rows=2, cols=2)
    table.rows[0].cells[0].text = "Header A"
    table.rows[0].cells[1].text = "Header B"
    table.rows[1].cells[0].text = "Value 1"
    table.rows[1].cells[1].text = "Value 2"
    path = tmp_path / name
    doc.save(str(path))
    return str(path)


class TestCopyDocx:
    def test_copy_preserves_paragraphs(self, tmp_path):
        src = _make_fixture_docx(tmp_path)
        dst = str(tmp_path / "copy.docx")
        copy_docx(src, dst)
        doc = Document(dst)
        texts = [p.text for p in doc.paragraphs if p.text.strip()]
        assert "Faculty paragraph one." in texts
        assert "Faculty paragraph two." in texts

    def test_copy_preserves_tables(self, tmp_path):
        src = _make_fixture_docx(tmp_path)
        dst = str(tmp_path / "copy.docx")
        copy_docx(src, dst)
        doc = Document(dst)
        assert len(doc.tables) >= 1
        cell_texts = [
            doc.tables[0].rows[0].cells[i].text for i in range(2)
        ]
        assert "Header A" in cell_texts
        assert "Header B" in cell_texts

    def test_copy_nonexistent_raises(self, tmp_path):
        import pytest
        with pytest.raises(FileNotFoundError):
            copy_docx(str(tmp_path / "nonexistent.docx"), str(tmp_path / "dst.docx"))


class TestAppendHeading:
    def test_heading_appended_does_not_remove_original(self, tmp_path):
        src = _make_fixture_docx(tmp_path)
        append_heading(src, "New Section", level=2)
        doc = Document(src)
        texts = [p.text for p in doc.paragraphs if p.text.strip()]
        # Original paragraphs still present.
        assert "Faculty paragraph one." in texts
        assert "Faculty paragraph two." in texts
        # New heading added.
        assert "New Section" in texts

    def test_heading_style_applied(self, tmp_path):
        src = _make_fixture_docx(tmp_path)
        append_heading(src, "My Heading", level=1)
        doc = Document(src)
        # Find the appended heading paragraph.
        heading_paras = [
            p for p in doc.paragraphs
            if p.style.name.startswith("Heading") and p.text == "My Heading"
        ]
        assert heading_paras, "Expected a Heading-styled paragraph for 'My Heading'"


class TestAppendTable:
    def test_table_appended_preserves_original_paragraphs(self, tmp_path):
        src = _make_fixture_docx(tmp_path)
        append_table(src, [["Col1", "Col2"], ["Row2a", "Row2b"]])
        doc = Document(src)
        texts = [p.text for p in doc.paragraphs if p.text.strip()]
        assert "Faculty paragraph one." in texts
        assert "Faculty paragraph two." in texts

    def test_table_appended_preserves_original_table(self, tmp_path):
        src = _make_fixture_docx(tmp_path)
        original_table_count = len(Document(src).tables)
        append_table(src, [["A", "B"]])
        doc = Document(src)
        assert len(doc.tables) == original_table_count + 1, (
            "Expected original table(s) plus the new appended table"
        )

    def test_new_table_rows_present(self, tmp_path):
        src = _make_fixture_docx(tmp_path)
        rows_to_add = [["Name", "Score"], ["Alice", "95"], ["Bob", "88"]]
        append_table(src, rows_to_add)
        doc = Document(src)
        # The last table should be the one we just appended.
        last_table = doc.tables[-1]
        assert last_table.rows[0].cells[0].text == "Name"
        assert last_table.rows[1].cells[0].text == "Alice"
        assert last_table.rows[2].cells[1].text == "88"

    def test_header_row_bold(self, tmp_path):
        src = _make_fixture_docx(tmp_path)
        append_table(src, [["Header", "Value"], ["r1c1", "r1c2"]], header_row=True)
        doc = Document(src)
        last_table = doc.tables[-1]
        header_cell = last_table.rows[0].cells[0]
        # At least one run in the header cell should be bold.
        bold_runs = [
            run for para in header_cell.paragraphs for run in para.runs
            if run.bold
        ]
        assert bold_runs, "Header row cells should have at least one bold run"

    def test_empty_rows_raises(self, tmp_path):
        src = _make_fixture_docx(tmp_path)
        import pytest
        with pytest.raises(ValueError):
            append_table(src, [])

    def test_inconsistent_columns_raises(self, tmp_path):
        src = _make_fixture_docx(tmp_path)
        import pytest
        with pytest.raises(ValueError):
            append_table(src, [["a", "b"], ["x"]])
