"""Phase 1.5 API surface source: Blizzard's auto-generated API doc files.

Modern WoW source on Gethe/wow-ui-source documents its API in
`Interface/AddOns/Blizzard_APIDocumentationGenerated/*.lua`. Each file is a
structured Lua table listing namespaces, functions, and events. We extract
function names and event LiteralNames; the union is the per-build API surface.

Optimization: GitHub's git/trees endpoint returns a SHA per path. We only
re-fetch files whose SHA changed between two tags, so a typical patch only
costs us a handful of file fetches instead of ~600.
"""
import re
from bot.lib.http import fetch_json, fetch_text

TREE_URL = "https://api.github.com/repos/Gethe/wow-ui-source/git/trees/{tag}?recursive=1"
RAW_URL = "https://raw.githubusercontent.com/Gethe/wow-ui-source/{tag}/{path}"
DOC_PREFIX = "Interface/AddOns/Blizzard_APIDocumentationGenerated/"

_FUNCTION_RE = re.compile(
    r'Name\s*=\s*"([A-Z][A-Za-z0-9_]*)"\s*,\s*Type\s*=\s*"Function"'
)
_EVENT_RE = re.compile(
    r'Type\s*=\s*"Event"\s*,\s*LiteralName\s*=\s*"([A-Z][A-Z_0-9]*)"'
)


def extract_symbols(lua_source: str) -> set[str]:
    return set(_FUNCTION_RE.findall(lua_source)) | set(_EVENT_RE.findall(lua_source))


def fetch_doc_tree(tag: str) -> dict[str, str]:
    data = fetch_json(TREE_URL.format(tag=tag))
    out: dict[str, str] = {}
    for item in data.get("tree", []):
        path = item.get("path", "")
        if (
            item.get("type") == "blob"
            and path.startswith(DOC_PREFIX)
            and path.endswith(".lua")
        ):
            out[path] = item["sha"]
    return out


def _fetch_symbols(tag: str, path: str) -> set[str]:
    return extract_symbols(fetch_text(RAW_URL.format(tag=tag, path=path)))


def diff_api_surface(old_tag: str, new_tag: str) -> tuple[set[str], set[str]]:
    """Return (removed, added) sets of API symbols between two tags.

    Skips files whose tree SHA is identical between tags.
    """
    old_tree = fetch_doc_tree(old_tag)
    new_tree = fetch_doc_tree(new_tag)

    removed: set[str] = set()
    added: set[str] = set()

    for path in old_tree.keys() - new_tree.keys():
        removed |= _fetch_symbols(old_tag, path)

    for path in new_tree.keys() - old_tree.keys():
        added |= _fetch_symbols(new_tag, path)

    for path in old_tree.keys() & new_tree.keys():
        if old_tree[path] == new_tree[path]:
            continue
        old_syms = _fetch_symbols(old_tag, path)
        new_syms = _fetch_symbols(new_tag, path)
        removed |= old_syms - new_syms
        added |= new_syms - old_syms

    return removed, added
