"""Tests for godotiq_project_summary tool."""

from __future__ import annotations

import pytest

from godotiq.session import GodotIQSession


class TestProjectSummaryBrief:
    def test_summary_brief_has_project_name(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "brief")
        assert result["project_name"] == "Sample Project"

    def test_summary_brief_has_counts(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "brief")
        assert "counts" in result
        assert result["counts"]["scenes"] >= 2
        assert result["counts"]["scripts"] >= 4

    def test_summary_brief_has_autoloads(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "brief")
        assert "autoloads" in result
        assert "GameManager" in result["autoloads"]
        assert "EconomyManager" in result["autoloads"]

    def test_summary_brief_has_engine(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "brief")
        assert "engine" in result
        assert result["engine"] == "godot_4"

    def test_summary_brief_has_type(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "brief")
        assert "type" in result


class TestProjectSummaryNormal:
    def test_summary_normal_has_architecture(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "normal")
        assert "architecture" in result
        assert "autoload_details" in result["architecture"]

    def test_summary_normal_has_main_scene(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "normal")
        assert result["architecture"]["main_scene"] == "res://scenes/main.tscn"

    def test_summary_normal_no_scene_structure(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "normal")
        assert "scene_structure" not in result


class TestProjectSummaryFull:
    def test_summary_full_has_autoload_analysis(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "full")
        assert "autoload_analysis" in result
        assert len(result["autoload_analysis"]) >= 2

    def test_summary_full_autoload_has_signals(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "full")
        econ = result["autoload_analysis"]["EconomyManager"]
        assert "cash_changed" in econ["signals"]

    def test_summary_full_autoload_has_public_functions(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "full")
        econ = result["autoload_analysis"]["EconomyManager"]
        func_names = [f["name"] for f in econ["public_functions"]]
        assert "add_cash" in func_names
        assert "spend_cash" in func_names

    def test_summary_full_autoload_has_referenced_by(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "full")
        econ = result["autoload_analysis"]["EconomyManager"]
        assert "res://scripts/autoloads/progression_manager.gd" in econ["referenced_by"]

    def test_summary_full_has_scene_structure(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "full")
        assert "scene_structure" in result
        assert len(result["scene_structure"]) >= 2

    def test_summary_full_scene_structure_capped(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "full")
        assert len(result["scene_structure"]) <= 10

    def test_summary_full_scene_has_root_type(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "full")
        main_scene = result["scene_structure"]["res://scenes/main.tscn"]
        assert main_scene["root_type"] == "Node3D"

    def test_summary_full_scene_has_instances(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "full")
        main_scene = result["scene_structure"]["res://scenes/main.tscn"]
        assert "res://scenes/equipment/fdm_printer.tscn" in main_scene["instances"]

    def test_summary_full_public_functions_capped(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "full")
        for _name, analysis in result["autoload_analysis"].items():
            assert len(analysis["public_functions"]) <= 5

    def test_summary_full_has_file_tree(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "full")
        assert "file_tree" in result
        assert "scenes" in result["file_tree"]
        assert "scripts" in result["file_tree"]

    def test_summary_full_has_detected_patterns(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "full")
        assert "detected_patterns" in result
        assert result["detected_patterns"]["uses_signals"] is True
        assert result["detected_patterns"]["uses_autoloads"] is True


class TestProjectSummaryRequiredFields:
    """Every detail level must include the brief fields."""

    @pytest.mark.parametrize("detail", ["brief", "normal", "full"])
    def test_always_has_project_name(self, session: GodotIQSession, detail: str) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, detail)
        assert "project_name" in result

    @pytest.mark.parametrize("detail", ["brief", "normal", "full"])
    def test_always_has_counts(self, session: GodotIQSession, detail: str) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, detail)
        assert "counts" in result

    @pytest.mark.parametrize("detail", ["brief", "normal", "full"])
    def test_always_has_autoloads(self, session: GodotIQSession, detail: str) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, detail)
        assert "autoloads" in result


class TestProjectSummaryFullAutoloadEnrichment:
    """Fix 5a: autoload analysis includes signal/reference counts at full detail."""

    def test_summary_full_autoload_has_signals_defined(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "full")
        for _name, analysis in result["autoload_analysis"].items():
            assert "signals_defined" in analysis
            assert isinstance(analysis["signals_defined"], int)

    def test_summary_full_autoload_has_referenced_by_count(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "full")
        for _name, analysis in result["autoload_analysis"].items():
            assert "referenced_by_count" in analysis
            assert isinstance(analysis["referenced_by_count"], int)

    def test_summary_full_autoload_has_signals_emitted(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "full")
        for _name, analysis in result["autoload_analysis"].items():
            assert "signals_emitted" in analysis
            assert isinstance(analysis["signals_emitted"], int)

    def test_summary_full_autoload_signals_defined_matches_list(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "full")
        econ = result["autoload_analysis"]["EconomyManager"]
        assert econ["signals_defined"] == len(econ["signals"])


class TestProjectSummaryFullSignalHealth:
    """Fix 5b: signal_health summary at full detail."""

    def test_summary_full_has_signal_health(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "full")
        assert "signal_health" in result
        sh = result["signal_health"]
        assert "total_defined" in sh
        assert "total_emissions" in sh
        assert "total_connections" in sh
        assert "orphan_count" in sh
        assert "top_signals" in sh

    def test_signal_health_top_signals_structure(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "full")
        top = result["signal_health"]["top_signals"]
        assert isinstance(top, list)
        for entry in top:
            assert "name" in entry
            assert "listeners" in entry

    def test_signal_health_orphan_count_matches_fixture(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "full")
        sh = result["signal_health"]
        # Sample project has 4 emitted signals and 0 code-based connections,
        # so all 4 emitted signals are orphans
        emissions = session.get_all_signal_emissions()
        assert sh["orphan_count"] == len(emissions)

    def test_signal_health_totals_are_nonnegative(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "full")
        sh = result["signal_health"]
        assert sh["total_defined"] >= 0
        assert sh["total_emissions"] >= 0
        assert sh["total_connections"] >= 0
        assert sh["orphan_count"] >= 0


class TestProjectSummaryFullHealth:
    """Fix 5c: project health summary at full detail."""

    def test_summary_full_has_health(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "full")
        assert "health" in result
        h = result["health"]
        assert "orphan_signals" in h
        assert "scripts_without_class_name" in h
        assert "note" in h

    def test_health_orphan_signals_nonnegative(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "full")
        assert result["health"]["orphan_signals"] >= 0

    def test_health_scripts_without_class_name_nonnegative(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "full")
        assert result["health"]["scripts_without_class_name"] >= 0


class TestProjectSummaryNoExtrasAtLowerDetail:
    """signal_health and health should NOT appear at normal or brief."""

    def test_normal_no_signal_health(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "normal")
        assert "signal_health" not in result
        assert "health" not in result

    def test_brief_no_signal_health(self, session: GodotIQSession) -> None:
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "brief")
        assert "signal_health" not in result
        assert "health" not in result
        assert "autoload_analysis" not in result


class TestProjectSummarySignalHealthNoBuiltins:
    """top_signals should only contain project-defined signals, not built-in Godot signals."""

    def test_signal_health_top_signals_no_builtins(self, session: GodotIQSession) -> None:
        """top_signals contains only signals declared in project .gd files."""
        from godotiq.tools.memory import _build_project_summary

        result = _build_project_summary(session, "full")
        top = result["signal_health"]["top_signals"]

        # Build the project-defined signal set
        project_signals: set[str] = set()
        for gd in session._gd_scripts.values():
            for sig in gd.signals:
                project_signals.add(sig.name)

        # Every signal in top_signals must be project-defined
        for entry in top:
            assert entry["name"] in project_signals, (
                f"Built-in signal '{entry['name']}' should not appear in top_signals"
            )


class TestProjectSummaryMCPRegistration:
    """Verify the tool is registered on the MCP server."""

    @pytest.mark.asyncio
    async def test_tool_registered(self) -> None:
        from godotiq.server import mcp

        tools = await mcp.list_tools()
        tool_names = [t.name for t in tools]
        assert "godotiq_project_summary" in tool_names
