"""Tests for the griffe → MDX API reference generator."""
import importlib.util
import sys
from pathlib import Path

import pytest

SCRIPT_PATH = Path(__file__).resolve().parents[1] / "build_api_reference.py"


def _load_module():
    spec = importlib.util.spec_from_file_location("build_api_reference", SCRIPT_PATH)
    assert spec and spec.loader, f"cannot load {SCRIPT_PATH}"
    mod = importlib.util.module_from_spec(spec)
    sys.modules["build_api_reference"] = mod
    spec.loader.exec_module(mod)
    return mod


def test_render_signature_includes_return_annotation():
    mod = _load_module()
    rendered = mod.render_signature(
        name="run",
        parameters=[("self", None, None), ("x", "int", None)],
        returns="str",
    )
    assert "run(self, x: int) -> str" in rendered


def test_render_docstring_parses_google_sections():
    mod = _load_module()
    doc = (
        "Summary line.\n\n"
        "Args:\n"
        "    name (str): The name.\n\n"
        "Returns:\n"
        "    bool: Whether it worked.\n"
    )
    out = mod.render_docstring(doc, ref_index={})
    assert "Summary line." in out
    assert "**Args**" in out
    assert "`name`" in out
    assert "**Returns**" in out


def test_rst_class_ref_links_when_in_index():
    mod = _load_module()
    ref_index = {"Tracer": "./cubepi-tracing#tracer"}
    out = mod._rst_to_mdx_refs(
        "Build a :class:`Tracer`, attach it.", ref_index=ref_index
    )
    assert "[`Tracer`](./cubepi-tracing#tracer)" in out


def test_rst_class_ref_falls_back_to_inline_code():
    mod = _load_module()
    out = mod._rst_to_mdx_refs(
        "Wraps a :class:`SpanExporter` from OTel.", ref_index={}
    )
    # External name: inline code, no link
    assert "`SpanExporter`" in out
    assert "](" not in out


def test_rst_class_ref_with_tilde_shortens_display():
    mod = _load_module()
    ref_index = {"Tracer": "./cubepi-tracing#tracer"}
    out = mod._rst_to_mdx_refs(
        "See :class:`~cubepi.tracing.Tracer` for details.",
        ref_index=ref_index,
    )
    # ~ prefix means show only the last component
    assert "[`Tracer`]" in out
    assert "[`cubepi.tracing.Tracer`]" not in out


def test_rst_meth_ref_resolves_class_member():
    mod = _load_module()
    ref_index = {"Tracer.attach": "./cubepi-tracing#tracer-attach"}
    out = mod._rst_to_mdx_refs(
        "Each :meth:`Tracer.attach` call wires…", ref_index=ref_index
    )
    assert "[`Tracer.attach`](./cubepi-tracing#tracer-attach)" in out


def test_strip_rst_double_backticks():
    mod = _load_module()
    out = mod._strip_rst_role_inline_code("Set ``record_content=True`` for opt-in.")
    assert "Set `record_content=True` for opt-in." == out


def test_rst_literal_block_marker_starts_code_fence():
    mod = _load_module()
    doc = (
        "Use it like this::\n\n"
        "    from cubepi import Agent\n"
        "    agent = Agent(...)\n"
    )
    out = mod.render_docstring(doc, ref_index={})
    assert "```python" in out
    assert "from cubepi import Agent" in out


def test_rst_underline_heading_converts_to_bold():
    mod = _load_module()
    doc = (
        "Summary.\n\n"
        "Example\n"
        "-------\n"
        "    x = 1\n"
    )
    out = mod.render_docstring(doc, ref_index={})
    assert "**Example**" in out
    # And the heading underline itself should NOT pass through
    assert "-------" not in out


def test_anchor_lowercases_and_dehyphens():
    mod = _load_module()
    assert mod._anchor("Tracer") == "tracer"
    assert mod._anchor("attach_async") == "attach-async"
    assert mod._anchor("__init__") == "init"


def test_source_relpath_strips_worktree_prefix():
    mod = _load_module()
    path = (
        "/home/chris/cubepi/.worktrees/2026-06-05-release-0.7-review/"
        "cubepi/agent/agent.py"
    )

    assert mod._source_relpath(path) == "cubepi/agent/agent.py"


def test_build_ref_index_includes_class_members():
    mod = _load_module()

    # Fake griffe-like Class with .name and .members
    class _FakeClass:
        def __init__(self, name, members):
            self.name = name
            self.members = members

    class _FakeMember:
        pass

    tracer = _FakeClass("Tracer", {"attach": _FakeMember(), "_private": _FakeMember()})

    index = mod.build_ref_index({"cubepi.tracing": [tracer]})
    assert index["Tracer"] == "./cubepi-tracing#tracer"
    assert index["Tracer.attach"] == "./cubepi-tracing#tracer-attach"
    # Private members are skipped
    assert "Tracer._private" not in index


def test_emit_module_writes_generated_marker(tmp_path):
    mod = _load_module()
    module_path = tmp_path / "cubepi-utils.mdx"
    mod.emit_module(
        out_path=module_path,
        module_name="cubepi.utils",
        sidebar_position=6,
        symbols=[],
        ref_index={},
        source_ref="main",
    )
    body = module_path.read_text(encoding="utf-8")
    assert body.startswith("---\n")
    assert "title: cubepi.utils" in body
    assert "<!-- GENERATED by build_api_reference.py — DO NOT EDIT -->" in body
