from __future__ import annotations

import os
import shutil
import sys
import tempfile
from pathlib import Path

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from vibelexity.circular_deps import find_cycles
from vibelexity.circular_deps.parsers.go import GoImportParser
from vibelexity.circular_deps.resolvers.go import GoPathResolver


def test_go_parser_extract_import():
    parser = GoImportParser()
    source = """
import (
    "fmt"
    "os"
    "mymodule/sub"
)
"""
    imports = parser.extract_imports(source, "test.go")
    assert len(imports) == 3
    assert imports[0].raw_module == "fmt"
    assert imports[1].raw_module == "os"
    assert imports[2].raw_module == "mymodule/sub"


def test_go_stdlib_filtering():
    from vibelexity.circular_deps.resolvers.go import GoPathResolver

    tmpdir = Path(tempfile.mkdtemp())
    try:
        resolver = GoPathResolver()

        path = resolver.resolve("fmt", "a.go", str(tmpdir))
        assert path is None

        path = resolver.resolve("encoding/json", "a.go", str(tmpdir))
        assert path is None

        sub_dir = tmpdir / "subdir"
        go_dir = sub_dir / "pkg"
        go_dir.mkdir(parents=True)
        go_file = go_dir / "pkg.go"
        go_file.write_text("")

        path = resolver.resolve("subdir/pkg", str(tmpdir), str(tmpdir))
        assert path == str(go_file)
    finally:
        shutil.rmtree(tmpdir)


def test_go_cycle_detection_simple():
    tmpdir = Path(tempfile.mkdtemp())
    try:
        a_go = tmpdir / "a.go"
        b_dir = tmpdir / "module" / "b"
        b_go = b_dir / "b.go"

        a_go.write_text('package main\nimport "mymodule/b"\n')
        b_dir.mkdir(parents=True, exist_ok=True)
        b_go.write_text('package b\nimport ".."\n')

        files = [a_go, b_go]
        cycles = find_cycles(files)

        assert isinstance(cycles, list)
    finally:
        shutil.rmtree(tmpdir)


def test_go_resolver_package_resolution():
    from vibelexity.circular_deps.resolvers.go import GoPathResolver

    tmpdir = Path(tempfile.mkdtemp())
    try:
        resolver = GoPathResolver()

        package_dir = tmpdir / "mypackage" / "subdir"
        package_dir.mkdir(parents=True)
        go_file = package_dir / "file.go"
        go_file.write_text("")

        path = resolver.resolve("mypackage/subdir", str(tmpdir), str(tmpdir))
        assert path == str(go_file)

        go_file2 = package_dir / "other.go"
        go_file2.write_text("")
        path = resolver.resolve("mypackage/subdir", str(tmpdir), str(tmpdir))
        assert path is not None and path.endswith(".go")
    finally:
        shutil.rmtree(tmpdir)


def test_go_resolver_finds_go_file():
    from vibelexity.circular_deps.resolvers.go import GoPathResolver

    tmpdir = Path(tempfile.mkdtemp())
    try:
        resolver = GoPathResolver()

        pkg_dir = tmpdir / "pkg"
        pkg_dir.mkdir()
        go_file = pkg_dir / "main.go"
        go_file.write_text("")

        path = resolver.resolve("pkg", str(tmpdir), str(tmpdir))
        assert path == str(go_file)

        direct_go = tmpdir / "direct.go"
        direct_go.write_text("")
        path = resolver.resolve("direct.go", str(tmpdir), str(tmpdir))
        assert path == str(direct_go)
    finally:
        shutil.rmtree(tmpdir)


def test_go_cycle_detection_in_src_subdirectory():
    """Test circular dependency detection with files in ./src subdirectory."""
    tmpdir = Path(tempfile.mkdtemp())
    try:
        src_dir = tmpdir / "src"
        src_dir.mkdir(parents=True, exist_ok=True)

        afet_dir = src_dir / "afet"
        bfet_dir = src_dir / "bfet"
        afet_dir.mkdir(parents=True, exist_ok=True)
        bfet_dir.mkdir(parents=True, exist_ok=True)

        a_go = afet_dir / "a.go"
        b_go = bfet_dir / "b.go"

        a_go.write_text('package afet\nimport "bfet"\n')
        b_go.write_text('package bfet\nimport "afet"\n')

        files = [a_go, b_go]
        cycles = find_cycles(files)

        assert len(cycles) == 1
        assert cycles[0].depth == 2
        assert cycles[0].severity == "warning"
    finally:
        shutil.rmtree(tmpdir)


def test_go_cycle_transitive_three_files():
    """Test transitive circular dependency with 3 files."""
    tmpdir = Path(tempfile.mkdtemp())
    try:
        a_dir = tmpdir / "a"
        b_dir = tmpdir / "b"
        c_dir = tmpdir / "c"

        a_dir.mkdir(parents=True, exist_ok=True)
        b_dir.mkdir(parents=True, exist_ok=True)
        c_dir.mkdir(parents=True, exist_ok=True)

        a_go = a_dir / "a.go"
        b_go = b_dir / "b.go"
        c_go = c_dir / "c.go"

        a_go.write_text('package a\nimport "b"\n')
        b_go.write_text('package b\nimport "c"\n')
        c_go.write_text('package c\nimport "a"\n')

        files = [a_go, b_go, c_go]
        cycles = find_cycles(files)

        assert len(cycles) == 1
        assert cycles[0].depth == 3
    finally:
        shutil.rmtree(tmpdir)


def test_go_cycle_nested_to_root():
    """Test circular dependency with nested package importing root."""
    tmpdir = Path(tempfile.mkdtemp())
    try:
        analysis_dir = tmpdir / "analysis"
        analysis_dir.mkdir(parents=True, exist_ok=True)

        verticality_go = analysis_dir / "verticality.go"
        index_go = tmpdir / "index.go"

        verticality_go.write_text('package analysis\nimport "."\n')
        index_go.write_text('package main\nimport "analysis"\n')

        files = [verticality_go, index_go]
        cycles = find_cycles(files)

        assert len(cycles) == 1
        assert cycles[0].depth == 2
    finally:
        shutil.rmtree(tmpdir)


def test_go_no_cycles_linear_chain():
    """Test linear import chain with no circular dependencies."""
    tmpdir = Path(tempfile.mkdtemp())
    try:
        a_dir = tmpdir / "a"
        b_dir = tmpdir / "b"
        c_dir = tmpdir / "c"

        a_dir.mkdir(parents=True, exist_ok=True)
        b_dir.mkdir(parents=True, exist_ok=True)
        c_dir.mkdir(parents=True, exist_ok=True)

        a_go = a_dir / "a.go"
        b_go = b_dir / "b.go"
        c_go = c_dir / "c.go"

        a_go.write_text('package a\nimport "b"\n')
        b_go.write_text('package b\nimport (\n\t"fmt"\n)\n')
        c_go.write_text('package c\nimport (\n\t"os"\n)\n')

        files = [a_go, b_go, c_go]
        cycles = find_cycles(files)

        assert len(cycles) == 0
    finally:
        shutil.rmtree(tmpdir)
