"""Additional tests for TypeScript resolver edge cases."""

import json
import shutil
import tempfile
from pathlib import Path

from vibelexity.circular_deps.resolvers.typescript import TypeScriptPathResolver


def test_resolver_default_no_args():
    """Default initialization (no args) uses node16."""
    resolver = TypeScriptPathResolver()
    assert resolver.module_resolution == "node16"


def test_resolver_nodenext_normalizes_to_node16():
    """nodenext is normalized to node16."""
    resolver = TypeScriptPathResolver(module_resolution="nodenext")
    assert resolver.module_resolution == "node16"


def test_resolver_invalid_mode_defaults_to_node16():
    """Invalid module resolution mode falls back to node16."""
    resolver = TypeScriptPathResolver(module_resolution="invalid")
    assert resolver.module_resolution == "node16"


def test_resolver_absolute_two_search_paths():
    """Absolute import searches in current_dir then root_dir."""
    tmpdir = Path(tempfile.mkdtemp())
    try:
        src_dir = tmpdir / "src"
        src_dir.mkdir()

        current_file = src_dir / "current.ts"
        current_file.write_text("")

        moa_ts = tmpdir / "moa.ts"
        moa_ts.write_text("")

        # First try current_dir, fall back to root_dir
        resolver = TypeScriptPathResolver(module_resolution="node")
        result = resolver.resolve("moa.ts", str(current_file), str(tmpdir))
        assert result is not None
        assert result.endswith("moa.ts")
    finally:
        shutil.rmtree(tmpdir, ignore_errors=True)


def test_resolver_node_with_extension_directory_index():
    """node mode: directory resolves to index.ts."""
    tmpdir = Path(tempfile.mkdtemp())
    try:
        moa_dir = tmpdir / "moa"
        moa_dir.mkdir()
        index_ts = moa_dir / "index.ts"
        index_ts.write_text("")
        a_ts = tmpdir / "a.ts"
        a_ts.write_text("")

        resolver = TypeScriptPathResolver(module_resolution="node")
        result = resolver.resolve("./moa", str(a_ts), str(tmpdir))
        assert result is not None
        assert result.endswith("index.ts")
    finally:
        shutil.rmtree(tmpdir, ignore_errors=True)


def test_resolver_node_with_extension_directory_main():
    """node mode: directory resolves to dirname.ext."""
    tmpdir = Path(tempfile.mkdtemp())
    try:
        moa_dir = tmpdir / "moa"
        moa_dir.mkdir()
        moa_ts = moa_dir / "moa.ts"
        moa_ts.write_text("")
        a_ts = tmpdir / "a.ts"
        a_ts.write_text("")

        resolver = TypeScriptPathResolver(module_resolution="node")
        result = resolver.resolve("./moa", str(a_ts), str(tmpdir))
        assert result is not None
        assert result.endswith("moa.ts")
    finally:
        shutil.rmtree(tmpdir, ignore_errors=True)


def test_resolver_node_without_extension_dirname():
    """node mode without extension: checks dirname.* first."""
    tmpdir = Path(tempfile.mkdtemp())
    try:
        moa_dir = tmpdir / "moa"
        moa_dir.mkdir()
        moa_tsx = moa_dir / "moa.tsx"
        moa_tsx.write_text("")
        a_ts = tmpdir / "a.ts"
        a_ts.write_text("")

        resolver = TypeScriptPathResolver(module_resolution="node")
        result = resolver.resolve("./moa", str(a_ts), str(tmpdir))
        assert result is not None
        assert result.endswith("moa.tsx")
    finally:
        shutil.rmtree(tmpdir, ignore_errors=True)


def test_resolver_node_without_extension_dts():
    """node mode without extension: checks .d.ts."""
    tmpdir = Path(tempfile.mkdtemp())
    try:
        moa_dts = tmpdir / "moa.d.ts"
        moa_dts.write_text("")
        a_ts = tmpdir / "a.ts"
        a_ts.write_text("")

        resolver = TypeScriptPathResolver(module_resolution="node")
        result = resolver.resolve("./moa", str(a_ts), str(tmpdir))
        assert result is not None
        assert result.endswith("moa.d.ts")
    finally:
        shutil.rmtree(tmpdir, ignore_errors=True)


def test_resolver_node16_without_extension_returns_none():
    """node16 mode without extension returns None (ESM requires extension)."""
    tmpdir = Path(tempfile.mkdtemp())
    try:
        moa_ts = tmpdir / "moa.ts"
        moa_ts.write_text("")
        a_ts = tmpdir / "a.ts"
        a_ts.write_text("")

        resolver = TypeScriptPathResolver(module_resolution="node16")
        result = resolver.resolve("./moa", str(a_ts), str(tmpdir))
        assert result is None
    finally:
        shutil.rmtree(tmpdir, ignore_errors=True)


def test_resolver_bundler_with_extension_wrong_type():
    """bundler mode: extension mismatch returns None."""
    tmpdir = Path(tempfile.mkdtemp())
    try:
        moa_ts = tmpdir / "moa.ts"
        moa_ts.write_text("")
        a_ts = tmpdir / "a.ts"
        a_ts.write_text("")

        resolver = TypeScriptPathResolver(module_resolution="bundler")
        # Requesting .js but only .ts exists
        result = resolver.resolve("./moa.js", str(a_ts), str(tmpdir))
        # Should try to find .ts instead
        assert result is not None
    finally:
        shutil.rmtree(tmpdir, ignore_errors=True)


def test_resolver_bundler_without_extension_tries_index():
    """bundler mode without extension tries index files."""
    tmpdir = Path(tempfile.mkdtemp())
    try:
        pkg_dir = tmpdir / "pkg"
        pkg_dir.mkdir()
        index_js = pkg_dir / "index.js"
        index_js.write_text("")
        a_ts = tmpdir / "a.ts"
        a_ts.write_text("")

        resolver = TypeScriptPathResolver(module_resolution="bundler")
        result = resolver.resolve("./pkg", str(a_ts), str(tmpdir))
        assert result is not None
        assert result.endswith("index.js")
    finally:
        shutil.rmtree(tmpdir, ignore_errors=True)


def test_resolver_bundler_without_extension_tries_extensions():
    """bundler mode without extension tries all extensions."""
    tmpdir = Path(tempfile.mkdtemp())
    try:
        moa_jsx = tmpdir / "moa.jsx"
        moa_jsx.write_text("")
        a_ts = tmpdir / "a.ts"
        a_ts.write_text("")

        resolver = TypeScriptPathResolver(module_resolution="bundler")
        result = resolver.resolve("./moa", str(a_ts), str(tmpdir))
        assert result is not None
        assert result.endswith("moa.jsx")
    finally:
        shutil.rmtree(tmpdir, ignore_errors=True)


def test_resolver_outside_root_returns_none():
    """External paths outside root dir return None."""
    tmpdir = Path(tempfile.mkdtemp())
    try:
        a_ts = tmpdir / "a.ts"
        a_ts.write_text("")
        b_ts = tmpdir / "b.ts"
        b_ts.write_text("")

        resolver = TypeScriptPathResolver(module_resolution="node")
        result = resolver.resolve("../../outside.ts", str(a_ts), "some_other_root")
        assert result is None
    finally:
        shutil.rmtree(tmpdir, ignore_errors=True)


def test_strip_extension_non_ts():
    """Strip extension only for TS-related extensions."""
    from vibelexity.circular_deps.resolvers.typescript import TypeScriptPathResolver

    resolver = TypeScriptPathResolver()
    assert resolver._strip_extension("file.txt") == "file.txt"
    assert resolver._strip_extension("file.ts") == "file"
    assert resolver._strip_extension("file.tsx") == "file"


def test_has_extension_empty():
    """has_extension returns False for empty paths."""
    from vibelexity.circular_deps.resolvers.typescript import TypeScriptPathResolver

    resolver = TypeScriptPathResolver()
    assert not resolver._has_extension("")
    assert not resolver._has_extension("module")


def test_module_modules_import_skipped():
    """node_modules imports return None."""
    resolver = TypeScriptPathResolver()
    result = resolver.resolve("lodash/package", "test.ts", "/project")
    assert result is None


def test_detect_from_tsconfig_no_config_found():
    """When no tsconfig found, returns node16."""
    tmpdir = Path(tempfile.mkdtemp())
    try:
        a_ts = tmpdir / "a.ts"
        a_ts.write_text("")

        resolver = TypeScriptPathResolver(filepath=str(a_ts))
        assert resolver.module_resolution == "node16"
    finally:
        shutil.rmtree(tmpdir, ignore_errors=True)


def test_detect_from_tsconfig_invalid_json():
    """Invalid JSON in tsconfig falls back to node16."""
    tmpdir = Path(tempfile.mkdtemp())
    try:
        tsconfig = tmpdir / "tsconfig.json"
        a_ts = tmpdir / "a.ts"

        tsconfig.write_text("{ invalid: json")
        a_ts.write_text("")

        resolver = TypeScriptPathResolver(filepath=str(a_ts))
        assert resolver.module_resolution == "node16"
    finally:
        shutil.rmtree(tmpdir, ignore_errors=True)


def test_resolver_bundler_ext_js_only_jsx_exists():
    """bundler .js request when only .jsx exists."""
    tmpdir = Path(tempfile.mkdtemp())
    try:
        moa_jsx = tmpdir / "moa.jsx"
        a_ts = tmpdir / "a.ts"
        moa_jsx.write_text("")
        a_ts.write_text("")

        resolver = TypeScriptPathResolver(module_resolution="bundler")
        result = resolver.resolve("./moa.js", str(a_ts), str(tmpdir))
        # Should try to find .ts or .tsx instead
        assert result is None or result.endswith("moa.jsx")
    finally:
        shutil.rmtree(tmpdir, ignore_errors=True)


def test_resolver_bundler_without_extension_jsx():
    """bundler mode finds .jsx."""
    tmpdir = Path(tempfile.mkdtemp())
    try:
        moa_jsx = tmpdir / "moa.jsx"
        a_ts = tmpdir / "a.ts"
        moa_jsx.write_text("")
        a_ts.write_text("")

        resolver = TypeScriptPathResolver(module_resolution="bundler")
        result = resolver.resolve("./moa", str(a_ts), str(tmpdir))
        assert result is not None
        assert result.endswith("moa.jsx")
    finally:
        shutil.rmtree(tmpdir, ignore_errors=True)


def test_resolver_bundler_without_extension_js():
    """bundler mode finds .js."""
    tmpdir = Path(tempfile.mkdtemp())
    try:
        moa_js = tmpdir / "moa.js"
        a_ts = tmpdir / "a.ts"
        moa_js.write_text("")
        a_ts.write_text("")

        resolver = TypeScriptPathResolver(module_resolution="bundler")
        result = resolver.resolve("./moa", str(a_ts), str(tmpdir))
        assert result is not None
        assert result.endswith("moa.js")
    finally:
        shutil.rmtree(tmpdir, ignore_errors=True)


def test_resolver_bundler_without_extension_tsx():
    """bundler mode finds .tsx."""
    tmpdir = Path(tempfile.mkdtemp())
    try:
        moa_tsx = tmpdir / "moa.tsx"
        a_ts = tmpdir / "a.ts"
        moa_tsx.write_text("")
        a_ts.write_text("")

        resolver = TypeScriptPathResolver(module_resolution="bundler")
        result = resolver.resolve("./moa", str(a_ts), str(tmpdir))
        assert result is not None
        assert result.endswith("moa.tsx")
    finally:
        shutil.rmtree(tmpdir, ignore_errors=True)


def test_resolve_directory_index_not_found():
    """Directory without index.ts returns None for path with /."""
    tmpdir = Path(tempfile.mkdtemp())
    try:
        pkg_dir = tmpdir / "pkg"
        pkg_dir.mkdir()
        a_ts = tmpdir / "a.ts"
        a_ts.write_text("")

        resolver = TypeScriptPathResolver(module_resolution="node")

        # Directory with no index - should not return None in node mode because it checks dirname.*
        # But if dirname.* also doesn't exist, then return None
        result = resolver.resolve("./pkg", str(a_ts), str(tmpdir))
        # The result could be None or some valid path depending on implementation
        assert result is None or "pkg" in result
    finally:
        shutil.rmtree(tmpdir, ignore_errors=True)
