"""Tests for the cyclomatic complexity analyzer."""

from __future__ import annotations

import tempfile
import textwrap
from pathlib import Path

import pytest

from debtmap.analyzers.complexity import ComplexityAnalyzer, FunctionComplexity


@pytest.fixture
def analyzer() -> ComplexityAnalyzer:
    return ComplexityAnalyzer()


@pytest.fixture
def sample_file(tmp_path: Path) -> Path:
    code = textwrap.dedent("""\
        def simple_function(x):
            return x + 1

        def moderate_function(x, y):
            if x > 0:
                if y > 0:
                    return x + y
                else:
                    return x - y
            elif x == 0:
                return y
            else:
                for i in range(y):
                    if i % 2 == 0:
                        print(i)
                return -1

        def complex_function(data):
            result = []
            for item in data:
                if item.get("type") == "a":
                    if item.get("status") == "active":
                        result.append(item)
                    elif item.get("status") == "pending":
                        if item.get("priority") == "high":
                            result.append(item)
                        elif item.get("priority") == "medium" and item.get("age") > 30:
                            result.append(item)
                elif item.get("type") == "b":
                    try:
                        val = process(item)
                        if val > 0:
                            result.append(val)
                    except ValueError:
                        pass
                    except KeyError:
                        if item.get("fallback"):
                            result.append(item["fallback"])
            return result

        class MyClass:
            def method_one(self, x):
                return x * 2

            def method_two(self, items):
                total = 0
                for item in items:
                    if item > 0 and item < 100:
                        total += item
                    elif item >= 100:
                        total += 100
                return total
    """)
    f = tmp_path / "sample.py"
    f.write_text(code, encoding="utf-8")
    return f


class TestComplexityAnalyzer:
    def test_simple_function_complexity(self, analyzer: ComplexityAnalyzer, sample_file: Path) -> None:
        result = analyzer.analyze_file(sample_file)
        funcs = {f.name: f for f in result.functions}

        assert "simple_function" in funcs
        # simple_function: just a return, complexity = 1
        assert funcs["simple_function"].complexity == 1

    def test_moderate_function_complexity(self, analyzer: ComplexityAnalyzer, sample_file: Path) -> None:
        result = analyzer.analyze_file(sample_file)
        funcs = {f.name: f for f in result.functions}

        assert "moderate_function" in funcs
        # if, elif, else, for, if => 1 + 4 = at least 5
        assert funcs["moderate_function"].complexity >= 5

    def test_complex_function_is_highest(self, analyzer: ComplexityAnalyzer, sample_file: Path) -> None:
        result = analyzer.analyze_file(sample_file)
        funcs = {f.name: f for f in result.functions}

        assert "complex_function" in funcs
        # Should be the most complex
        assert funcs["complex_function"].complexity > funcs["moderate_function"].complexity

    def test_method_detection(self, analyzer: ComplexityAnalyzer, sample_file: Path) -> None:
        result = analyzer.analyze_file(sample_file)
        methods = [f for f in result.functions if f.is_method]

        assert len(methods) >= 2
        method_names = {m.name for m in methods}
        assert "MyClass.method_one" in method_names
        assert "MyClass.method_two" in method_names

    def test_rating_classification(self) -> None:
        fc = FunctionComplexity(name="test", file_path="test.py", line_number=1, complexity=1, line_count=1)
        assert fc.rating == "simple"

        fc.complexity = 8
        assert fc.rating == "moderate"

        fc.complexity = 15
        assert fc.rating == "complex"

        fc.complexity = 30
        assert fc.rating == "very_complex"

        fc.complexity = 60
        assert fc.rating == "unmaintainable"

    def test_file_aggregate_metrics(self, analyzer: ComplexityAnalyzer, sample_file: Path) -> None:
        result = analyzer.analyze_file(sample_file)

        assert result.max_complexity > 0
        assert result.avg_complexity > 0
        assert result.total_complexity > 0
        assert len(result.functions) >= 5

    def test_syntax_error_handling(self, analyzer: ComplexityAnalyzer, tmp_path: Path) -> None:
        bad_file = tmp_path / "bad.py"
        bad_file.write_text("def broken(:\n  pass", encoding="utf-8")

        result = analyzer.analyze_file(bad_file)
        assert result.parse_error is not None
        assert len(result.functions) == 0

    def test_empty_file(self, analyzer: ComplexityAnalyzer, tmp_path: Path) -> None:
        empty = tmp_path / "empty.py"
        empty.write_text("", encoding="utf-8")

        result = analyzer.analyze_file(empty)
        assert len(result.functions) == 0
        assert result.max_complexity == 0

    def test_boolean_operators_increase_complexity(self, analyzer: ComplexityAnalyzer, tmp_path: Path) -> None:
        code = textwrap.dedent("""\
            def check(a, b, c, d):
                if a and b and c or d:
                    return True
                return False
        """)
        f = tmp_path / "bool.py"
        f.write_text(code, encoding="utf-8")

        result = analyzer.analyze_file(f)
        # 1 base + 1 if + 2 and + 1 or = 5
        assert result.functions[0].complexity >= 4

    def test_comprehension_complexity(self, analyzer: ComplexityAnalyzer, tmp_path: Path) -> None:
        code = textwrap.dedent("""\
            def filter_items(data):
                return [x for x in data if x > 0 if x < 100]
        """)
        f = tmp_path / "comp.py"
        f.write_text(code, encoding="utf-8")

        result = analyzer.analyze_file(f)
        # 1 base + 1 for + 2 ifs = 4
        assert result.functions[0].complexity >= 3

    def test_hotspots(self, analyzer: ComplexityAnalyzer, sample_file: Path) -> None:
        results = [analyzer.analyze_file(sample_file)]
        hotspots = analyzer.get_hotspots(results, threshold=5)

        assert len(hotspots) > 0
        # Should be sorted by complexity descending
        for i in range(len(hotspots) - 1):
            assert hotspots[i].complexity >= hotspots[i + 1].complexity

    def test_directory_scan(self, analyzer: ComplexityAnalyzer, tmp_path: Path) -> None:
        (tmp_path / "mod_a.py").write_text("def foo():\n    return 1\n", encoding="utf-8")
        (tmp_path / "mod_b.py").write_text("def bar(x):\n    if x:\n        return 1\n    return 0\n", encoding="utf-8")

        results = analyzer.analyze_directory(tmp_path)
        assert len(results) == 2

    def test_summary(self, analyzer: ComplexityAnalyzer, sample_file: Path) -> None:
        results = [analyzer.analyze_file(sample_file)]
        summary = analyzer.summary(results)

        assert summary["total_functions"] >= 5
        assert summary["avg_complexity"] > 0
        assert summary["max_complexity"] > 0
        assert "simple_count" in summary

    def test_async_function_support(self, analyzer: ComplexityAnalyzer, tmp_path: Path) -> None:
        code = textwrap.dedent("""\
            async def fetch(url):
                if url.startswith("http"):
                    return await get(url)
                return None
        """)
        f = tmp_path / "async_mod.py"
        f.write_text(code, encoding="utf-8")

        result = analyzer.analyze_file(f)
        assert len(result.functions) == 1
        assert result.functions[0].complexity >= 2

    def test_to_dict(self, analyzer: ComplexityAnalyzer, sample_file: Path) -> None:
        result = analyzer.analyze_file(sample_file)
        d = result.to_dict()
        assert "file_path" in d
        assert "functions" in d
        assert isinstance(d["functions"], list)

    def test_non_python_file_ignored(self, analyzer: ComplexityAnalyzer, tmp_path: Path) -> None:
        f = tmp_path / "readme.md"
        f.write_text("# Hello", encoding="utf-8")
        result = analyzer.analyze_file(f)
        assert len(result.functions) == 0
