"""Tests for Data Transformation Density (TypeScript)."""

from vibelexity.analyzers.typescript import analyze_source


def _dtd(code: str) -> int:
    """Return DTD of the first function in code."""
    result = analyze_source(code)
    assert result.functions, f"No functions found in:\n{code}"
    dtd_val = result.functions[0].dtd
    assert dtd_val is not None
    return dtd_val


def _dtds(code: str) -> dict[str, int]:
    """Return {name: dtd} for all functions in code."""
    result = analyze_source(code)
    return {f.name: f.dtd for f in result.functions if f.dtd is not None}


# --- Empty ---


def test_empty_function():
    assert _dtd("function f() {}") == 0


# --- Flat structures ---


def test_flat_object():
    code = """
function f() {
    return {a: 1, b: 2, c: 3};
}
"""
    # object at depth 1, 3 pairs x 1 = 3
    assert _dtd(code) == 3


def test_flat_array():
    code = """
function f() {
    return [1, 2, 3];
}
"""
    # array at depth 1, 3 elements x 1 = 3
    assert _dtd(code) == 3


# --- Nested ---


def test_nested_object_in_array():
    code = """
function f() {
    return [{a: 1}, {b: 2}];
}
"""
    # array at depth 1: 2 elements x 1 = 2
    # each object at depth 2: 1 pair x 2 = 2 each -> 4
    # total = 2 + 4 = 6
    assert _dtd(code) == 6


def test_deeply_nested():
    code = """
function f() {
    return {outer: {inner: [1, 2]}};
}
"""
    # outer object depth 1: 1 pair x 1 = 1
    # inner object depth 2: 1 pair x 2 = 2
    # array depth 3: 2 elements x 3 = 6
    # total = 1 + 2 + 6 = 9
    assert _dtd(code) == 9


# --- Map call ---


def test_map_call():
    code = """
function f(items: number[]) {
    return items.map(x => ({value: x}));
}
"""
    # .map() call at depth 1 (container)
    # Inside: object at depth 2, 1 pair x 2 = 2
    # total = 2
    assert _dtd(code) == 2


# --- Shorthand property ---


def test_shorthand_property():
    code = """
function f(x: number, y: number) {
    return {x, y};
}
"""
    # object at depth 1, 2 shorthand properties x 1 = 2
    assert _dtd(code) == 2


# --- Arrow expression body ---


def test_arrow_expression_body():
    code = """
const f = (x: number) => ({value: x});
"""
    # object at depth 1, 1 pair x 1 = 1
    assert _dtd(code) == 1


# --- Nested named arrow skipped ---


def test_nested_named_arrow():
    code = """
function outer() {
    const x = {a: 1};
    const inner = (y: number) => {
        return {b: 2, c: 3};
    };
}
"""
    dtds = _dtds(code)
    assert dtds["outer"] == 1  # only {a: 1}
    assert dtds["inner"] == 2  # {b: 2, c: 3} -> 2 pairs x 1
