import pathlib
import tempfile
import unittest
import zipfile
import os
from concurrent.futures import ThreadPoolExecutor
from renderer import prepare, fromstring, font_env, run, A, P

class PreparationTests(unittest.TestCase):
    @unittest.skipUnless(os.environ.get("FONT_FIXTURE"), "Approved local font fixture required")
    def test_full_name_alias_selects_uploaded_font(self):
        from fontTools.ttLib import TTFont
        import shutil
        with tempfile.TemporaryDirectory() as directory:
            job=pathlib.Path(directory); (job/'fonts').mkdir()
            target=job/'fonts'/'fixture.otf';shutil.copyfile(os.environ['FONT_FIXTURE'],target)
            with TTFont(target) as font:
                names={font['name'].getDebugName(i) for i in (1,4,16)}-{None}
            env=font_env(job,job/'fonts')
            for name in names:
                self.assertEqual(pathlib.Path(run(['fc-match','--format','%{file}',name],env)).resolve(),target.resolve(),name)
    @unittest.skipUnless(os.environ.get("FONT_FIXTURE"), "Local font fixture required for conflicting-family isolation acceptance")
    def test_concurrent_conflicting_families_remain_project_local(self):
        from fontTools.ttLib import TTFont
        with tempfile.TemporaryDirectory() as directory:
            jobs = [pathlib.Path(directory) / "project-a", pathlib.Path(directory) / "project-b"]
            for index, job in enumerate(jobs):
                (job / "fonts").mkdir(parents=True)
                with TTFont(os.environ["FONT_FIXTURE"]) as font:
                    for record in list(font["name"].names):
                        if record.nameID in (1, 16):
                            font["name"].setName("DocuDeck Isolation Fixture", record.nameID, record.platformID, record.platEncID, record.langID)
                    font["OS/2"].usWeightClass = 300 + index * 100
                    font.save(job / "fonts" / "conflict.otf")
            def selected(job):
                result = run(["fc-match", "--format", "%{file}", "DocuDeck Isolation Fixture"], font_env(job, job / "fonts"))
                return pathlib.Path(result).resolve()
            with ThreadPoolExecutor(max_workers=2) as executor:
                selections = list(executor.map(selected, jobs))
            self.assertEqual(selections, [(job / "fonts" / "conflict.otf").resolve() for job in jobs])

    def test_explicit_formatting_text_and_namespaces_are_preserved(self):
        xml = b'''<p:sld xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:p><a:pPr rtl="0" algn="ctr"><a:defRPr lang="en-GB"/></a:pPr><a:r><a:rPr lang="fr-FR"/><a:t>Original text</a:t></a:r></a:p><a:p><a:r><a:t>Untouched</a:t></a:r></a:p></p:sld>'''
        with tempfile.TemporaryDirectory() as directory:
            source = pathlib.Path(directory) / "source.pptx"
            target = pathlib.Path(directory) / "prepared.pptx"
            with zipfile.ZipFile(source, "w") as archive:
                archive.writestr("ppt/slides/slide1.xml", xml)
                archive.writestr("ppt/presentation.xml", b'<p:presentation xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><p:defaultTextStyle><a:lvl1pPr rtl="0"><a:defRPr lang="en-GB"/></a:lvl1pPr></p:defaultTextStyle></p:presentation>')
                archive.writestr("ppt/slideMasters/slideMaster1.xml", xml)
            prepare(source, target, "ar", "auto")
            with zipfile.ZipFile(target) as archive:
                root = fromstring(archive.read("ppt/slides/slide1.xml"))
                self.assertEqual(archive.read("ppt/slides/slide1.xml"), xml)
                self.assertEqual(archive.read("ppt/slideMasters/slideMaster1.xml"), xml)
                presentation = fromstring(archive.read("ppt/presentation.xml"))
            paragraphs = list(root.iter(A + "p"))
            self.assertEqual(paragraphs[0].find(A + "pPr").get("rtl"), "0")
            self.assertEqual(paragraphs[0].find(A + "pPr").get("algn"), "ctr")
            self.assertEqual(paragraphs[0].find(A + "pPr").find(A + "defRPr").get("lang"), "en-GB")
            self.assertEqual(root.find(".//" + A + "rPr").get("lang"), "fr-FR")
            self.assertEqual([node.text for node in root.iter(A + "t")], ["Original text", "Untouched"])
            self.assertIsNone(paragraphs[1].find(A + "pPr"))
            default = presentation.find(P + "defaultTextStyle").find(A + "defPPr")
            self.assertEqual(default.get("rtl"), "1")
            self.assertEqual(default.find(A + "defRPr").get("lang"), "ar-SA")
            explicit = presentation.find(P + "defaultTextStyle").find(A + "lvl1pPr")
            self.assertEqual(explicit.get("rtl"), "0")
            self.assertEqual(explicit.find(A + "defRPr").get("lang"), "en-GB")
            self.assertEqual(root.nsmap["a"], "http://schemas.openxmlformats.org/drawingml/2006/main")

    def test_external_entities_are_rejected(self):
        with self.assertRaises(ValueError):
            fromstring(b'<!DOCTYPE root [<!ENTITY a SYSTEM "file:///etc/passwd">]><root>&a;</root>')

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