import unittest
from pathlib import Path
from unittest.mock import patch
from bot.lib import apidocs

FIX = Path(__file__).parent / "fixtures"


class TestExtractSymbols(unittest.TestCase):
    def test_extracts_functions_and_event_literal_names(self):
        text = (FIX / "apidocs-sample.lua").read_text(encoding="utf-8")
        syms = apidocs.extract_symbols(text)
        self.assertIn("GetIDFromBattleNetAccountGUID", syms)
        self.assertIn("IsGUIDBattleNetAccountType", syms)
        self.assertIn("ACCOUNT_INFO_UPDATED", syms)

    def test_skips_namespace_and_argument_names(self):
        text = (FIX / "apidocs-sample.lua").read_text(encoding="utf-8")
        syms = apidocs.extract_symbols(text)
        # namespace ("AccountInfo") and event camelcase Name ("AccountInfoUpdated")
        # are intentionally NOT captured by the function/event regexes.
        self.assertNotIn("AccountInfo", syms)
        self.assertNotIn("AccountInfoUpdated", syms)
        # argument names are lowercase, regex starts with [A-Z], so they are filtered
        self.assertNotIn("battleNetAccountGUID", syms)


class TestFetchDocTree(unittest.TestCase):
    def test_filters_to_doc_prefix_lua_blobs(self):
        fake = {
            "tree": [
                {"path": "Interface/AddOns/Blizzard_APIDocumentationGenerated/Foo.lua", "type": "blob", "sha": "a"},
                {"path": "Interface/AddOns/Blizzard_APIDocumentationGenerated/Bar.lua", "type": "blob", "sha": "b"},
                {"path": "Interface/AddOns/Other/Other.lua", "type": "blob", "sha": "c"},
                {"path": "Interface/AddOns/Blizzard_APIDocumentationGenerated/", "type": "tree", "sha": "d"},
                {"path": "Interface/AddOns/Blizzard_APIDocumentationGenerated/README.md", "type": "blob", "sha": "e"},
            ]
        }
        with patch("bot.lib.apidocs.fetch_json", return_value=fake):
            tree = apidocs.fetch_doc_tree("12.0.5")
        self.assertEqual(
            tree,
            {
                "Interface/AddOns/Blizzard_APIDocumentationGenerated/Foo.lua": "a",
                "Interface/AddOns/Blizzard_APIDocumentationGenerated/Bar.lua": "b",
            },
        )


class TestDiffApiSurface(unittest.TestCase):
    def test_added_removed_changed_paths(self):
        # old has Foo and Common; new has Bar and Common.
        # Common's SHA changed -> fetch both -> diff.
        old_tree = {
            "Interface/AddOns/Blizzard_APIDocumentationGenerated/Foo.lua": "shaFooOld",
            "Interface/AddOns/Blizzard_APIDocumentationGenerated/Common.lua": "shaCommonOld",
        }
        new_tree = {
            "Interface/AddOns/Blizzard_APIDocumentationGenerated/Bar.lua": "shaBarNew",
            "Interface/AddOns/Blizzard_APIDocumentationGenerated/Common.lua": "shaCommonNew",
        }
        # Symbols per file:
        symbols_by_url = {
            ("old", "Interface/AddOns/Blizzard_APIDocumentationGenerated/Foo.lua"): {"FooFunc"},
            ("new", "Interface/AddOns/Blizzard_APIDocumentationGenerated/Bar.lua"): {"BarFunc"},
            ("old", "Interface/AddOns/Blizzard_APIDocumentationGenerated/Common.lua"): {"KeptFunc", "OldOnlyFunc"},
            ("new", "Interface/AddOns/Blizzard_APIDocumentationGenerated/Common.lua"): {"KeptFunc", "NewOnlyFunc"},
        }

        def fake_tree(tag):
            return old_tree if tag == "old" else new_tree

        def fake_fetch_text(url):
            for tag in ("old", "new"):
                prefix = f"https://raw.githubusercontent.com/Gethe/wow-ui-source/{tag}/"
                if url.startswith(prefix):
                    path = url[len(prefix):]
                    syms = symbols_by_url.get((tag, path), set())
                    # Inline the funcs as Type="Function" so extract_symbols picks them up.
                    return "\n".join(
                        f'Name = "{s}", Type = "Function",' for s in syms
                    )
            raise AssertionError(f"unexpected url {url}")

        with patch("bot.lib.apidocs.fetch_doc_tree", side_effect=fake_tree), \
             patch("bot.lib.apidocs.fetch_text", side_effect=fake_fetch_text):
            removed, added = apidocs.diff_api_surface("old", "new")

        self.assertEqual(removed, {"FooFunc", "OldOnlyFunc"})
        self.assertEqual(added, {"BarFunc", "NewOnlyFunc"})

    def test_unchanged_files_not_fetched(self):
        # Both trees have the same path with same SHA -> no fetch.
        tree = {"Interface/AddOns/Blizzard_APIDocumentationGenerated/Same.lua": "sameSHA"}
        with patch("bot.lib.apidocs.fetch_doc_tree", return_value=tree), \
             patch("bot.lib.apidocs.fetch_text") as ft:
            removed, added = apidocs.diff_api_surface("old", "new")
        ft.assert_not_called()
        self.assertEqual(removed, set())
        self.assertEqual(added, set())


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