"""Unit tests for table serialization modes."""

from __future__ import annotations

from extractor.schema import TableData, TableStructured
from extractor.table_serializers import serialize, to_hybrid, to_nl_columns, to_nl_rows


def _make_table(
    headers=None,
    rows=None,
    markdown="| A | B |\n|---|---|\n| 1 | 2 |",
) -> TableData:
    structured = TableStructured(
        headers=headers if headers is not None else ["Product", "Revenue"],
        rows=rows if rows is not None else [["Widget A", "12M"], ["Widget B", "8M"]],
    )
    return TableData(markdown=markdown, structured=structured)


# ---------------------------------------------------------------------------
# to_nl_rows
# ---------------------------------------------------------------------------


def test_to_nl_rows_basic():
    table = _make_table()
    result = to_nl_rows(table)
    assert "Row 1" in result
    assert "Product=Widget A" in result
    assert "Revenue=12M" in result
    assert "Row 2" in result
    assert "Widget B" in result


def test_to_nl_rows_no_headers():
    table = _make_table(headers=[], rows=[["a", "b"], ["c", "d"]])
    result = to_nl_rows(table)
    assert "Row 1" in result
    assert "a" in result
    assert "Row 2" in result
    assert "c" in result


def test_to_nl_rows_single_row():
    table = _make_table(rows=[["Alpha", "100"]])
    result = to_nl_rows(table)
    assert "Row 1" in result
    assert "Alpha" in result


def test_to_nl_rows_empty_rows():
    table = _make_table(rows=[])
    result = to_nl_rows(table)
    assert result == ""


def test_to_nl_rows_empty_cell_skipped():
    """Empty string cells are excluded from the NL representation."""
    table = _make_table(headers=["A", "B", "C"], rows=[["x", "", "z"]])
    result = to_nl_rows(table)
    assert "A=x" in result
    assert "C=z" in result
    # Empty cell should not appear as B=
    assert "B=" not in result


def test_to_nl_rows_no_structured_falls_back_to_markdown():
    table = TableData(
        markdown="| X |\n|---|\n| 1 |",
        structured=TableStructured(headers=[], rows=[]),
    )
    # With no headers and no rows, returns empty string
    result = to_nl_rows(table)
    assert result == ""


# ---------------------------------------------------------------------------
# to_nl_columns
# ---------------------------------------------------------------------------


def test_to_nl_columns_basic():
    table = _make_table()
    result = to_nl_columns(table)
    assert "Product:" in result
    assert "Widget A" in result
    assert "Widget B" in result
    assert "Revenue:" in result
    assert "12M" in result
    assert "8M" in result


def test_to_nl_columns_no_rows_falls_back():
    """With no rows, falls back to markdown."""
    table = _make_table(rows=[], markdown="FALLBACK_MARKDOWN")
    result = to_nl_columns(table)
    assert result == "FALLBACK_MARKDOWN"


def test_to_nl_columns_missing_header_uses_column_number():
    """When headers list is shorter than columns, Column N is used."""
    table = _make_table(headers=["Name"], rows=[["Alice", "30"], ["Bob", "25"]])
    result = to_nl_columns(table)
    assert "Name:" in result
    assert "Column 2:" in result


def test_to_nl_columns_single_column():
    table = _make_table(headers=["Item"], rows=[["Apple"], ["Banana"]])
    result = to_nl_columns(table)
    assert "Item:" in result
    assert "Apple" in result
    assert "Banana" in result


# ---------------------------------------------------------------------------
# to_hybrid
# ---------------------------------------------------------------------------


def test_to_hybrid_contains_both():
    table = _make_table()
    result = to_hybrid(table)
    # Should contain NL rows AND markdown table delimiter
    assert "Row 1" in result
    assert "|" in result


def test_to_hybrid_separator():
    """NL rows and markdown are separated by a blank line."""
    table = _make_table()
    result = to_hybrid(table)
    assert "\n\n" in result


def test_to_hybrid_nl_part_before_markdown():
    table = _make_table()
    result = to_hybrid(table)
    nl_pos = result.index("Row 1")
    md_pos = result.index("|")
    assert nl_pos < md_pos


# ---------------------------------------------------------------------------
# serialize dispatcher
# ---------------------------------------------------------------------------


def test_serialize_markdown_default():
    table = _make_table(markdown="| X |\n|---|\n| 1 |")
    result = serialize(table, "markdown")
    assert result == "| X |\n|---|\n| 1 |"


def test_serialize_nl_rows():
    table = _make_table()
    result = serialize(table, "nl-rows")
    assert "Row 1" in result


def test_serialize_nl_columns():
    table = _make_table()
    result = serialize(table, "nl-columns")
    assert "Product:" in result


def test_serialize_hybrid():
    table = _make_table()
    result = serialize(table, "hybrid")
    assert "Row 1" in result
    assert "|" in result


def test_serialize_unknown_mode_falls_back_to_markdown():
    table = _make_table(markdown="MD_CONTENT")
    result = serialize(table, "unknown_mode")
    assert result == "MD_CONTENT"


def test_serialize_empty_mode_falls_back_to_markdown():
    table = _make_table(markdown="MD_CONTENT")
    result = serialize(table, "")
    assert result == "MD_CONTENT"
