import unittest
import os
from pathlib import Path

from dungeonsheets import make_sheets, character, monsters, random_tables


EG_DIR = Path(__file__).parent.parent.resolve() / "examples"
CHARFILE = EG_DIR / "rogue1.py"
GMFILE = EG_DIR / "gm-session-notes.py"


class MakeSheetsTestCase(unittest.TestCase):
    char_pdf = Path(f"{CHARFILE.stem}.pdf")
    gm_pdf = Path(f"{GMFILE.stem}.pdf").resolve()

    def tearDown(self):
        if self.char_pdf.exists():
            self.char_pdf.unlink()
        if self.gm_pdf.exists():
            self.gm_pdf.unlink()

    def test_main(self):
        make_sheets.main(args=[str(CHARFILE), "--debug"])

    def test_make_sheets(self):
        # Character PDF
        make_sheets.make_sheet(sheet_file=CHARFILE)
        # Was the PDF created?
        self.assertTrue(self.char_pdf.exists(),
                        f"Character PDF ({self.char_pdf.resolve()}) not created.")
        # GM PDF
        make_sheets.make_sheet(sheet_file=GMFILE)
        self.assertTrue(self.gm_pdf.exists)
        # Was the PDF created?
        self.assertTrue(self.gm_pdf.exists(),
                        f"GM PDF ({self.gm_pdf.resolve()}) not created.")

    def test_make_fancy_sheets(self):
        # Character PDF
        make_sheets.make_sheet(sheet_file=CHARFILE,
                               fancy_decorations=True)
        # Was the PDF created?
        self.assertTrue(self.char_pdf.exists(),
                        f"Character PDF ({self.char_pdf.resolve()}) not created.")
        # GM PDF
        make_sheets.make_sheet(sheet_file=GMFILE,
                               fancy_decorations=True)
        self.assertTrue(self.gm_pdf.exists)
        # Was the PDF created?
        self.assertTrue(self.gm_pdf.exists(),
                        f"GM PDF ({self.gm_pdf.resolve()}) not created.")


class EpubOutputTestCase(unittest.TestCase):
    gm_epub = Path(f"{GMFILE.stem}.epub").resolve()
    char_epub = Path(f"{CHARFILE.stem}.epub").resolve()

    def new_character(self):
        char = character.Character(
            name="Dr. Who",
            classes=["Monk", "Druid", "Artificer"],
            levels=[1, 1, 1],
            subclasses=["way of the open hand", None, None],
            magic_items=["cloak of protection"],
            spells=["invisibility"],
            wild_shapes=["crocodile"],
            infusions=["boots of the winding path"]
        )
        return char

    def tearDown(self):
        for f in [self.gm_epub, self.char_epub]:
            if f.exists():
                f.unlink()

    def test_character_html_content(self):
        my_char = self.new_character()
        my_char.magic_items_text
        html = make_sheets.make_character_content(character=my_char,
                                                  content_format="html")
        html = "".join(html)
        # Make sure the various sections get rendered
        self.assertIn("Subclasses</h1>", html)
        self.assertIn("Features</h1>", html)
        self.assertIn("Magic Items</h1>", html)
        self.assertIn("Spells</h1>", html)
        self.assertIn("Infusions</h1>", html)
        self.assertIn("Known Beasts</h1>", html)
        # Check the character sheet
        self.assertIn("Character Sheet</h1>", html)
        self.assertIn("Dr. Who", html)

    def test_gm_file_created(self):
        # Check that a file is created once the function is run
        make_sheets.make_gm_sheet(gm_file=GMFILE, output_format="epub")
        self.assertTrue(self.gm_epub.exists(), f"{self.gm_epub} not created.")

    def test_character_file_created(self):
        # Check that a file is created once the function is run
        make_sheets.make_character_sheet(char_file=CHARFILE, output_format="epub")
        self.assertTrue(self.char_epub.exists(), f"{self.char_epub} not created.")        


class PdfOutputTestCase(unittest.TestCase):
    basename = "clara"

    def tearDown(self):
        temp_files = [f"{self.basename}.pdf"]
        for f in temp_files:
            if os.path.exists(f):
                os.remove(f)

    def test_file_created(self):
        # Check that a file is created once the function is run
        pdf_name = f"{self.basename}.pdf"
        # self.assertFalse(os.path.exists(pdf_name), f'{pdf_name} already exists.')
        char = character.Character(name="Clara")
        char.saving_throw_proficiencies = ["strength"]
        make_sheets.create_character_pdf_template(
            character=char, basename=self.basename
        )
        self.assertTrue(os.path.exists(pdf_name), f"{pdf_name} not created.")


class VashtaNerada(monsters.Monster):
    """Scariest monster ever, but luckily only for testing."""
    name = "Vashta Nerada"
    speed = 35
    fly_speed = 45
    swim_speed = 55
    burrow_speed = 65
    condition_immunities = "petrified"
    saving_throws = "Dex +8"
    damage_immunities = "Bludgeoning"
    damage_resistances = "Lightning"
    damage_vulnerabilities = "Wood-based"
    challenge_rating = 30
    spells = ["wish"]


class HtmlCreatorTestCase(unittest.TestCase):
    def new_character(self):
        char = character.Character(
            name="Dr. Who",
            classes=["Monk", "Druid", "Artificer"],
            levels=[1, 1, 1],
            subclasses=["way of the open hand", None, None],
            magic_items=["cloak of protection"],
            spells=["invisibility"],
            wild_shapes=["crocodile"],
            infusions=["boots of the winding path"]
        )
        return char
    
    def test_create_monsters_html(self):
        monsters_ = [monsters.Priest()]
        html = make_sheets.create_monsters_content(monsters=monsters_, suffix="html")
        self.assertIn(r"Priest", html)
        monsters_ = [VashtaNerada()]
        html = make_sheets.create_monsters_content(monsters=monsters_, suffix="html")
        # Check summary table
        self.assertIn("monster-table", html)
        # Check extended properties
        self.assertIn(r"Vashta Nerada", html)
        self.assertIn(r"35", html)
        self.assertIn(r"45 fly", html)
        self.assertIn(r"55 swim", html)
        self.assertIn(r"65 burrow", html)
        self.assertIn(r"petrified", html)
        self.assertIn(r"Saving Throws", html)
        self.assertIn(r"Damage Immunities", html)
        self.assertIn(r"Damage Resistances", html)
        self.assertIn(r"Damage Vulnerabilities", html)
        self.assertIn(r"Senses", html)
        self.assertIn(r"Challenge", html)
        self.assertIn(r"(155,000 XP)", html)
        self.assertIn(r"Languages", html)
        self.assertIn(r"Skills", html)
        self.assertIn(r"petrified", html)
        self.assertIn(r"Dex +8", html)
        # Check spells and spell descriptions
        self.assertIn(r"<dt>Level 9</dt>", html)
        self.assertIn(r"Wish", html)        
        # Check fancy extended properties
        html = make_sheets.create_monsters_content(monsters=monsters_,
                                                   suffix="html",
                                                   use_dnd_decorations=True)

    def test_create_party_summary_html(self):
        char = self.new_character()
        html = make_sheets.create_party_summary_content(party=[char], suffix="html", summary_rst="")
        self.assertIn('<h1 id="gm-party">Party</h1>', html)
        self.assertIn(char.name, html)
        # Check for passive perception/insight/investigation
        self.assertIn('<th>Pass. Per.</th>', html)
        self.assertIn(f'<td class="passive-perception">{10+char.perception.modifier}</td>', html)
        self.assertIn('<th>Pass. Ins.</th>', html)
        self.assertIn(f'<td class="passive-insight">{10+char.insight.modifier}</td>', html)        
        self.assertIn('<th>Pass. Inv.</th>', html)
        self.assertIn(f'<td class="passive-investigation">{10+char.investigation.modifier}</td>', html)

    def test_create_extra_gm_content(self):
        class MySection():
            name = "My D&D Homebrew Content"

        html = make_sheets.create_extra_gm_content(sections=[MySection], suffix="html")
        self.assertIn('<h1 id="extra-My-DD-Homebrew-Content">', html)
        tex = make_sheets.create_extra_gm_content(sections=[MySection], suffix="tex")
        self.assertIn(r'\section*{My D&D Homebrew Content', tex)


class TexCreatorTestCase(unittest.TestCase):
    """Test various helper functions for creating TeX from a character."""

    def new_character(self):
        char = character.Character(
            name="Dr. Who",
            classes=["Monk", "Druid", "Artificer"],
            levels=[1, 1, 1],
            subclasses=["way of the open hand", None, None],
            magic_items=["cloak of protection"],
            spells=["invisibility"],
            wild_shapes=["crocodile"],
            infusions=["boots of the winding path"]
        )
        return char

    def test_create_subclasses_tex(self):
        char = self.new_character()
        tex = make_sheets.create_subclasses_content(character=char, content_suffix="tex")
        self.assertIn(r"\section*{Subclasses}", tex)
        self.assertIn(r"\subsection*{Way of the Open Hand}", tex)

    def test_create_features_tex(self):
        char = self.new_character()
        tex = make_sheets.create_features_content(character=char, content_suffix="tex")
        self.assertIn(r"\section*{Features}", tex)
        self.assertIn(r"\subsection*{Martial Arts}", tex)

    def test_create_magic_items_tex(self):
        char = self.new_character()
        tex = make_sheets.create_magic_items_content(character=char, content_suffix="tex")
        self.assertIn(r"\section*{Magic Items}", tex)
        self.assertIn(r"\subsection*{Cloak of Protection}", tex)

    def test_create_spellbook_tex(self):
        char = self.new_character()
        tex = make_sheets.create_spellbook_content(character=char, content_suffix="tex")
        self.assertIn(r"\section*{Spells}", tex)
        self.assertIn(r"\section*{Invisibility}", tex)

    def test_create_infusions_tex(self):
        char = self.new_character()
        tex = make_sheets.create_infusions_content(character=char, content_suffix="tex")
        self.assertIn(r"\section*{Infusions}", tex)
        self.assertIn(r"\subsection*{Boots of the Winding Path}", tex)

    def test_create_druid_shapes_tex(self):
        char = self.new_character()
        tex = make_sheets.create_druid_shapes_content(character=char, content_suffix="tex")
        self.assertIn(r"\section*{Known Beasts}", tex)
        self.assertIn(r"\section*{Crocodile}", tex)

    def test_create_monsters_tex(self):
        monsters_ = [monsters.GiantEagle()]
        tex = make_sheets.create_monsters_content(monsters=monsters_, suffix="tex")
        self.assertIn(r"Giant Eagle", tex)
        monsters_ = [VashtaNerada()]
        tex = make_sheets.create_monsters_content(monsters=monsters_, suffix="tex")
        # Check that the monster summary table exists
        self.assertIn(r"Vashta Nerada & 10 & 1d6 & 10 & +0", tex)
        # Check extended properties
        self.assertIn(r"Vashta Nerada", tex)
        self.assertIn(r"35", tex)
        self.assertIn(r"45 fly", tex)
        self.assertIn(r"55 swim", tex)
        self.assertIn(r"65 burrow", tex)
        self.assertIn(r"petrified", tex)
        self.assertIn(r"Saving Throws:", tex)
        self.assertIn(r"Damage Immunities:", tex)
        self.assertIn(r"Damage Resistances:", tex)
        self.assertIn(r"Damage Vulnerabilities:", tex)
        self.assertIn(r"Senses:", tex)
        self.assertIn(r"Challenge:", tex)
        self.assertIn(r"(155,000 XP)", tex)
        self.assertIn(r"Languages:", tex)
        self.assertIn(r"Skills:", tex)
        # Check fancy extended properties
        tex = make_sheets.create_monsters_content(monsters=monsters_,
                                                  suffix="tex",
                                                  use_dnd_decorations=True)
        self.assertIn(r"Vashta Nerada", tex)
        self.assertIn(r"35 ft.", tex)
        self.assertIn(r"45 ft. fly", tex)
        self.assertIn(r"55 ft. swim", tex)
        self.assertIn(r"65 ft. burrow", tex)
        self.assertIn(r"petrified", tex)
        self.assertIn(r"saving-throws = {Dex +8}", tex)
    
    def test_create_party_summary_tex(self):
        char = self.new_character()
        tex = make_sheets.create_party_summary_content(party=[char], suffix="tex", summary_rst="")
        self.assertIn(r"\section*{Party}", tex)
        self.assertIn(char.name, tex)
        # Check for passive perception/insight/investigation
        print(char.passive_insight, char.passive_perception, char.passive_investigation)
        self.assertIn(f'& {char.passive_insight} % Passive insight', tex)
        self.assertIn(f'& {char.passive_perception} % Passive perception', tex)
        self.assertIn(f'& {char.passive_investigation} % Passive investigation', tex)
    
    def test_create_party_summary_tex_fancy(self):
        char = self.new_character()
        tex = make_sheets.create_party_summary_content(party=[char],
                                                       suffix="tex",
                                                       summary_rst="",
                                                       use_dnd_decorations=True)
        self.assertIn(r"\section*{Party}", tex)
        self.assertIn(char.name, tex)
        # Check for passive perception/insight/investigation
        self.assertIn(f'& {char.passive_insight} % Passive insight', tex)
        self.assertIn(f'& {char.passive_perception} % Passive perception', tex)
        self.assertIn(f'& {char.passive_investigation} % Passive investigation', tex)
    
    def test_create_summary_tex(self):
        rst = "The party's create *adventure*."
        tex = make_sheets.create_party_summary_content(party=[], suffix="tex", summary_rst=rst)
        self.assertIn(r"\section*{Summary}", tex)
        # Check that the RST is parsed
        self.assertIn(r"\emph{adventure}", tex)

    def test_random_tables_tex(self):
        tex = make_sheets.create_random_tables_content(
            tables=[random_tables.ConjureAnimals],
            suffix="tex",
        )
        self.assertIn(r"\subsection*{Conjure Animals}", tex)
