"""Tests for dead code detection."""

from __future__ import annotations

from pathlib import Path

from vibelexity.parsers.python import parse
from vibelexity.dead_code.parsers.python import PythonDeadCodeParser


def test_extract_imports():
    code = """
import os
import sys as system
from os.path import join, exists
from typing import List
"""
    parser = PythonDeadCodeParser()
    root = parse(code)
    imports = parser.extract_imports(root, code)

    assert len(imports) >= 3
    import_names = [imp.name for imp in imports]
    assert "os" in import_names
    assert "system" in import_names or "sys" in import_names
    assert "join" in import_names or "exists" in import_names


def test_extract_definitions():
    code = """
def foo():
    pass

class Bar:
    pass
"""
    parser = PythonDeadCodeParser()
    root = parse(code)
    defs = parser.extract_definitions(root, code)

    assert len(defs) == 2
    def_names = [d.name for d in defs]
    assert "foo" in def_names
    assert "Bar" in def_names


def test_extract_references():
    code = """
def foo(x):
    return x

y = foo(1)
bar = y
"""
    parser = PythonDeadCodeParser()
    root = parse(code)
    refs = parser.extract_references(root, code)

    ref_names = [r.name for r in refs]
    assert "x" in ref_names
    assert "foo" in ref_names
    assert "y" in ref_names
    assert "bar" in ref_names


def test_unused_import():
    from vibelexity.dead_code.core import (
        build_cross_reference_graph,
        find_unused_imports,
    )

    code = """
import os
def foo():
    return 1
"""
    root = parse(code)
    parser = PythonDeadCodeParser()

    imports = parser.extract_imports(root, code)
    refs = parser.extract_references(root, code)

    assert len(imports) == 1
    assert len(refs) > 0


def test_build_cross_reference_graph_with_unused_imports():
    """Cross-reference graph building with unused imports."""
    import tempfile
    from vibelexity.dead_code.core import build_cross_reference_graph

    code = """
import os
import sys

def foo():
    return 1
"""
    with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
        f.write(code)
        filepath = f.name

    try:
        graph, _, _, _ = build_cross_reference_graph([Path(filepath)], lang="python")
        import_keys = [k for k in graph.keys() if "import:" in k]
        assert len(import_keys) >= 2
    finally:
        Path(filepath).unlink()


def test_build_cross_reference_graph_unused_symbols():
    """Dead symbol detection with unused functions."""
    import tempfile
    from vibelexity.dead_code.core import build_cross_reference_graph, find_dead_symbols

    code = """
def unused_function():
    pass

def used_function():
    return 1
"""
    with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
        f.write(code)
        filepath = f.name

    try:
        graph, _, _, _ = build_cross_reference_graph([Path(filepath)], lang="python")
        violations = find_dead_symbols(
            graph, abstract_classes=set(), abstract_methods=set(), lang="python"
        )
        unused_funcs = [v for v in violations if v.symbol == "unused_function"]
        assert len(unused_funcs) >= 1
    finally:
        Path(filepath).unlink()


def test_build_cross_reference_graph_dunder_methods():
    """Dunder methods should not be flagged as dead."""
    import tempfile
    from vibelexity.dead_code.core import build_cross_reference_graph, find_dead_symbols

    code = """
class MyClass:
    def __init__(self):
        self.x = 1

    def __str__(self):
        return str(self.x)
"""
    with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
        f.write(code)
        filepath = f.name

    try:
        graph, _, _, _ = build_cross_reference_graph([Path(filepath)], lang="python")
        violations = find_dead_symbols(
            graph, abstract_classes=set(), abstract_methods=set(), lang="python"
        )
        dunder_violations = [
            v
            for v in violations
            if v.symbol and v.symbol.startswith("__") and v.symbol.endswith("__")
        ]
        assert len(dunder_violations) == 0
    finally:
        Path(filepath).unlink()


def test_build_cross_reference_graph_abstract_classes():
    """Abstract classes are not flagged as dead."""
    import tempfile
    from vibelexity.dead_code.core import build_cross_reference_graph, find_dead_symbols

    code = """
from abc import ABC, abstractmethod

class AbstractClass(ABC):
    @abstractmethod
    def method(self):
        pass

class ConcreteClass(AbstractClass):
    def method(self):
        pass
"""
    with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
        f.write(code)
        filepath = f.name

    try:
        graph, abstract_classes, abstract_methods, _ = build_cross_reference_graph(
            [Path(filepath)], lang="python"
        )
        violations = find_dead_symbols(
            graph, abstract_classes, abstract_methods, lang="python"
        )
        abstract_violations = [v for v in violations if v.symbol == "AbstractClass"]
        assert len(abstract_violations) == 0
    finally:
        Path(filepath).unlink()


def test_build_cross_reference_graph_exported_symbols():
    """Exported symbols (in TS/Go) are not flagged as dead."""
    import tempfile
    from vibelexity.dead_code.core import build_cross_reference_graph, find_dead_symbols

    code = """
def foo():
    pass
"""
    with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
        f.write(code)
        filepath = f.name

    try:
        graph, _, _, _ = build_cross_reference_graph([Path(filepath)], lang="python")
        for key, info in graph.items():
            info.exported = True
        violations = find_dead_symbols(
            graph, abstract_classes=set(), abstract_methods=set(), lang="python"
        )
        assert len(violations) == 0
    finally:
        Path(filepath).unlink()


def test_build_cross_reference_graph_empty_file():
    """Empty file doesn't crash analysis."""
    import tempfile
    from vibelexity.dead_code.core import build_cross_reference_graph

    code = ""
    with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
        f.write(code)
        filepath = f.name

    try:
        graph, abstract_classes, abstract_methods, _ = build_cross_reference_graph(
            [Path(filepath)], lang="python"
        )
        assert isinstance(graph, dict)
    finally:
        Path(filepath).unlink()


def test_build_cross_reference_graph_multiple_files():
    """Cross-reference graph builds across multiple files."""
    import tempfile
    from vibelexity.dead_code.core import build_cross_reference_graph

    code1 = """
def foo():
    pass
"""
    code2 = """
from app import foo

def bar():
    foo()
"""
    with (
        tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f1,
        tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f2,
    ):
        f1.write(code1)
        f2.write(code2)
        filepath1 = f1.name
        filepath2 = f2.name

    try:
        graph, _, _, _ = build_cross_reference_graph(
            [Path(filepath1), Path(filepath2)], lang="python"
        )
        foo_refs = []
        for key, info in graph.items():
            if "foo" in key:
                foo_refs.extend(info.references)
        assert len(foo_refs) >= 1
    finally:
        Path(filepath1).unlink()
        Path(filepath2).unlink()


def test_unused_import_with_alias():
    """Detect unused imports with aliases."""
    import tempfile
    from vibelexity.dead_code.core import (
        build_cross_reference_graph,
        find_unused_imports,
    )

    code = """
import os as operating_system
import sys

def foo():
    return 1
"""
    with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
        f.write(code)
        filepath = f.name

    try:
        graph, _, _, _ = build_cross_reference_graph([Path(filepath)], lang="python")
        violations = find_unused_imports(graph)
        import_violations = [v for v in violations if v.type == "unused_import"]
        assert len(import_violations) >= 1
    finally:
        Path(filepath).unlink()


def test_import_with_usage():
    """Imports that are used should not be flagged."""
    import tempfile
    from vibelexity.dead_code.core import (
        build_cross_reference_graph,
        find_unused_imports,
    )

    code = """
import os

def foo():
    return os.getcwd()
"""
    with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
        f.write(code)
        filepath = f.name

    try:
        graph, _, _, _ = build_cross_reference_graph([Path(filepath)], lang="python")
        violations = find_unused_imports(graph)
        import_violations = [v for v in violations if v.type == "unused_import"]
        assert len(import_violations) == 0
    finally:
        Path(filepath).unlink()
