"""Tests for dead code detection in Go."""

from __future__ import annotations

from pathlib import Path

from vibelexity.parsers.go import parse
from vibelexity.dead_code.parsers.go import GoDeadCodeParser
from vibelexity.dead_code.core import find_unused_imports, build_cross_reference_graph


def test_unused_imports_basic():
    code = """
package main

import (
    "fmt"
    "os"
)

func main() {
    fmt.Println("Hello")
}
"""
    parser = GoDeadCodeParser()
    root = parse(code)
    imports = parser.extract_imports(root, code)

    import_names = [imp.name for imp in imports]
    assert "fmt" in import_names
    assert "os" in import_names


def test_blank_identifier_imports_skipped():
    code = """
package main

import (
    _ "github.com/pkg/errors"
    "fmt"
)

func main() {
    fmt.Println("Hello")
}
"""
    parser = GoDeadCodeParser()
    root = parse(code)
    imports = parser.extract_imports(root, code)

    import_names = [imp.name for imp in imports]
    assert "fmt" in import_names
    assert "errors" not in import_names  # _ identifier is skipped


def test_dot_imports_skipped():
    code = """
package main

import (
    . "fmt"
)

func main() {
    Println("Hello")
}
"""
    parser = GoDeadCodeParser()
    root = parse(code)
    imports = parser.extract_imports(root, code)

    # Dot imports should be skipped (name shows as 'dot')
    import_names = [imp.name for imp in imports]
    assert len(imports) == 0


def test_extract_definitions():
    code = """
package main

func foo() {
    bar()
}

func bar() {
}
"""
    parser = GoDeadCodeParser()
    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_method_definitions():
    code = """
package main

type MyType struct{}

func (m MyType) Method() {}
"""
    parser = GoDeadCodeParser()
    root = parse(code)
    defs = parser.extract_definitions(root, code)

    assert len(defs) == 1
    assert defs[0].name == "MyType.Method"


def test_unused_short_var_declarations():
    code = """
    package main
    
    func foo() {
        x := 1
        y := 2
        return x
    }
    """
    parser = GoDeadCodeParser()
    root = parse(code)
    refs = parser.extract_references(root, code)
    ref_names = [r.name for r in refs]

    assert "x" in ref_names
    assert "y" in ref_names  # y is declared, appears in references


def test_variables_in_loops():
    code = """
package main

func foo() {
    for i := 0; i < 10; i++ {
        println(i)
    }
}
"""
    parser = GoDeadCodeParser()
    root = parse(code)
    refs = parser.extract_references(root, code)
    ref_names = [r.name for r in refs]

    assert "i" in ref_names  # used in loop condition and body


def test_exported_symbols_detection():
    code = """
package main

func PublicFunc() {}

func privateFunc() {}
"""
    assert GoDeadCodeParser().is_exported("PublicFunc")
    assert not GoDeadCodeParser().is_exported("privateFunc")


def test_extract_references():
    code = """
package main

func foo(x int) int {
    y := x + 1
    return y
}
"""
    parser = GoDeadCodeParser()
    root = parse(code)
    refs = parser.extract_references(root, code)

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


def test_dead_functions_cross_reference():
    code1 = """
    package main
    
    func Used() {}
    func Unused() {}
    
    func main() {
        Used()
    }
    """
    parser = GoDeadCodeParser()
    root = parse(code1)

    defs = parser.extract_definitions(root, code1)
    refs = parser.extract_references(root, code1)

    def_names = [d.name for d in defs]
    ref_names = [r.name for r in refs]

    assert "Used" in def_names
    assert "Unused" in def_names
    assert "Used" in ref_names
    assert "Unused" in ref_names  # Unused appears in declaration


def test_interface_method_implementations():
    code = """
package main

type MyInterface interface {
    Method(x int) int
}

type MyType struct{}

func (m MyType) Method(x int) int {
    return x
}
"""
    parser = GoDeadCodeParser()
    root = parse(code)
    defs = parser.extract_definitions(root, code)

    assert len(defs) == 1
    assert defs[0].name == "MyType.Method"


def test_unused_import_detection_with_cross_ref():
    code = """
package main

import "fmt"

func main() {
    _ = fmt.Sprintf
}
"""
    parser = GoDeadCodeParser()
    root = parse(code)
    imports = parser.extract_imports(root, code)
    refs = parser.extract_references(root, code)

    import_names = [imp.name for imp in imports]
    ref_names = [r.name for r in refs]

    # fmt is used in _ assignment
    assert "fmt" in import_names
    assert "fmt" in ref_names
