from __future__ import annotations

import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch

sys.path.insert(0, str(Path(__file__).resolve().parents[1]))

from tools.office_to_pdf import OfficeToPdfTool
from tools import office_to_pdf as office_module


_ALL_FORMATS = frozenset({
    ".doc", ".docx", ".odt", ".xls", ".xlsx", ".ods", ".ppt", ".pptx", ".odp",
})


class TestOfficeToPdfTool(unittest.TestCase):
    def _tool(self, root: Path, task: Path) -> OfficeToPdfTool:
        return OfficeToPdfTool(base_dir=task, user_root=root)

    def test_converts_container_path_on_host_and_publishes_requested_output(self):
        with tempfile.TemporaryDirectory() as td:
            root = Path(td)
            task = root / "task"
            task.mkdir()
            (task / "source.docx").write_bytes(b"docx")

            def fake_run(cmd, **kwargs):
                outdir = Path(cmd[cmd.index("--outdir") + 1])
                (outdir / "source.pdf").write_bytes(b"%PDF-1.4 test")
                return subprocess.CompletedProcess(cmd, 0, b"", b"")

            with patch(
                "tools.office_to_pdf.office_supported_suffixes", return_value=_ALL_FORMATS
            ), patch("tools.office_to_pdf.find_soffice", return_value="soffice"), patch(
                "tools.office_to_pdf.subprocess.run", side_effect=fake_run
            ):
                result = self._tool(root, task).execute(
                    source="task/source.docx",
                    output="task/converted/final.pdf",
                )

            out = task / "converted" / "final.pdf"
            self.assertEqual(out.read_bytes(), b"%PDF-1.4 test")
            self.assertIn("[OK] PDF created: task/converted/final.pdf", result)

    def test_rejects_source_outside_user_workspace(self):
        with tempfile.TemporaryDirectory() as td:
            root = Path(td) / "user"
            task = root / "task"
            task.mkdir(parents=True)
            result = self._tool(root, task).execute(source="../../outside.docx")
            self.assertIn("[Error] source path out of user workspace", result)

    def test_rejects_non_office_source(self):
        with tempfile.TemporaryDirectory() as td:
            root = Path(td)
            task = root / "task"
            task.mkdir()
            (task / "notes.md").write_text("x", encoding="utf-8")
            result = self._tool(root, task).execute(source="notes.md")
            self.assertIn("[Error] unsupported Office format", result)

    def test_reports_missing_writer_component_before_conversion(self):
        with tempfile.TemporaryDirectory() as td:
            root = Path(td)
            task = root / "task"
            task.mkdir()
            (task / "legacy.doc").write_bytes(b"doc")
            with patch(
                "tools.office_to_pdf.office_supported_suffixes",
                return_value=frozenset({".ppt", ".pptx", ".odp"}),
            ), patch("tools.office_to_pdf.subprocess.run") as run:
                tool = self._tool(root, task)
                result = tool.execute(source="legacy.doc")

            self.assertIn("lacks the Writer component", result)
            self.assertIn("PPT", tool.description)
            self.assertNotIn("DOC,", tool.description)
            run.assert_not_called()

    def test_debian_component_detection_limits_advertised_formats(self):
        office_module.office_supported_suffixes.cache_clear()
        try:
            with patch("tools.office_to_pdf.find_soffice", return_value="/usr/bin/soffice"), patch(
                "tools.office_to_pdf._debian_office_families",
                return_value=frozenset({"Impress"}),
            ):
                supported = office_module.office_supported_suffixes()
            self.assertEqual(supported, frozenset({".ppt", ".pptx", ".odp"}))
        finally:
            office_module.office_supported_suffixes.cache_clear()


if __name__ == "__main__":
    unittest.main()
