"""Tests for explorer.py decomposition (P3-001).

Acceptance criteria:
- explorer.py must be under 1500 lines.
- Tab modules must define their build functions locally (not re-export from explorer).
- No circular imports between explorer.py and analysis.tabs.
"""

import ast
import inspect
import pathlib


class TestExplorerDecomposition:
    """RED test: explorer.py must be under 1500 lines."""

    def test_explorer_line_count_under_1500(self):
        path = pathlib.Path("explorer.py")
        lines = path.read_text(encoding="utf-8").splitlines()
        assert len(lines) < 1500, (
            f"explorer.py has {len(lines)} lines; target is < 1500. "
            f"Extract tab functions and rendering helpers into analysis/tabs/."
        )

    def test_tab_modules_define_functions_locally(self):
        """Each tab module must define its build_*_tab without delegating to explorer."""
        tabs = [
            ("analysis/tabs/compass.py", "build_compass_tab"),
            ("analysis/tabs/trajectories.py", "build_trajectories_tab"),
            ("analysis/tabs/components.py", "build_svd_components_tab"),
            ("analysis/tabs/quiz.py", "build_mp_quiz_tab"),
        ]
        for module_path, func_name in tabs:
            source = pathlib.Path(module_path).read_text(encoding="utf-8")
            tree = ast.parse(source)
            func_def = None
            for node in ast.walk(tree):
                if isinstance(node, ast.FunctionDef) and node.name == func_name:
                    func_def = node
                    break
            assert func_def is not None, (
                f"{module_path} must define {func_name}"
            )
            # Ensure it's not a one-liner stub that imports from explorer
            body = func_def.body
            assert len(body) > 3, (
                f"{module_path}.{func_name} looks like a stub ({len(body)} lines). "
                f"Extract the real implementation from explorer.py."
            )

    def test_rendering_helpers_extracted(self):
        """Rendering helpers should not live in explorer.py."""
        helpers = [
            "_render_scree_plot",
            "_build_party_axis_figure",
            "_render_party_axis_chart",
            "_render_party_axis_chart_1d",
            "_render_svd_time_trajectory",
            "_render_voting_results",
            "_add_y_direction_annotations",
        ]
        source = pathlib.Path("explorer.py").read_text(encoding="utf-8")
        tree = ast.parse(source)
        defined = {node.name for node in ast.walk(tree) if isinstance(node, ast.FunctionDef)}
        for helper in helpers:
            assert helper not in defined, (
                f"{helper} should be extracted from explorer.py "
                f"into analysis/tabs/_rendering.py"
            )

    def test_no_circular_import_tabs_to_explorer(self):
        """Tab modules must not import from explorer."""
        tab_modules = [
            "analysis/tabs/compass.py",
            "analysis/tabs/trajectories.py",
            "analysis/tabs/components.py",
            "analysis/tabs/quiz.py",
            "analysis/tabs/_rendering.py",
        ]
        for module_path in tab_modules:
            if not pathlib.Path(module_path).exists():
                continue
            source = pathlib.Path(module_path).read_text(encoding="utf-8")
            assert "from explorer import" not in source, (
                f"{module_path} imports from explorer.py — "
                f"move shared helpers to explorer_data.py or _rendering.py instead"
            )
            assert "import explorer" not in source, (
                f"{module_path} imports explorer module — "
                f"move shared helpers to explorer_data.py or _rendering.py instead"
            )
