"""Tests for the inspect module (print_inspect, inspect_json, helpers).

The actual ``inspect()`` function requires a live uopy connection, so we test
the formatting functions and internal helpers directly.
"""
from __future__ import annotations

import io
import json

from universe_reads.inspect_file import (
    _coerce_id,
    _coerce_value,
    inspect_json,
    print_inspect,
    AM,
    VM,
    SVM,
)


# ---------------------------------------------------------------------------
# _coerce_id
# ---------------------------------------------------------------------------

def test_coerce_id_str():
    assert _coerce_id("KEY-001") == "KEY-001"


def test_coerce_id_bytes():
    assert _coerce_id(b"KEY-002") == "KEY-002"


def test_coerce_id_dynarray_list_attr():
    """Objects with .list = ["KEY"] should coerce to "KEY"."""
    class FakeDyn:
        list = ["KEY-003"]
    assert _coerce_id(FakeDyn()) == "KEY-003"


def test_coerce_id_dynarray_string_list():
    class FakeDyn:
        list = "KEY-004"
    assert _coerce_id(FakeDyn()) == "KEY-004"


def test_coerce_id_fallback():
    assert _coerce_id(42) == "42"


# ---------------------------------------------------------------------------
# _coerce_value
# ---------------------------------------------------------------------------

def test_coerce_value_str():
    assert _coerce_value("hello") == "hello"


def test_coerce_value_bytes():
    assert _coerce_value(b"hello") == "hello"


def test_coerce_value_dynarray():
    class FakeDyn:
        list = ["a", "b"]
    assert _coerce_value(FakeDyn()) == ["a", "b"]


def test_coerce_value_other():
    assert _coerce_value(123) == "123"


# ---------------------------------------------------------------------------
# print_inspect
# ---------------------------------------------------------------------------

def _make_inspect_info() -> dict:
    """Build a realistic inspect result dict."""
    raw = AM.join(["STU-0001", "Thandi Mokoena", "CS101", "21254", "P", "08:00", "Notes here"])
    return {
        "file": "ATTENDANCE",
        "record_count": 300_000,
        "sample_keys": ["ATT-CS101-20260310-0001", "ATT-CS201-20260311-0002"],
        "field_counts": [7, 7],
        "sample_records": [
            {"key": "ATT-CS101-20260310-0001", "fields": 7, "raw": raw},
            {"key": "ATT-CS201-20260311-0002", "fields": 7, "raw": raw},
        ],
        "elapsed_seconds": 1.234,
    }


def test_print_inspect_output():
    info = _make_inspect_info()
    buf = io.StringIO()
    print_inspect(info, file=buf)
    output = buf.getvalue()

    assert "ATTENDANCE" in output
    assert "300,000" in output
    assert "7.0" in output  # avg fields
    assert "1.234" in output
    assert "ATT-CS101-20260310-0001" in output
    # Delimiters should be replaced in preview.
    assert AM not in output
    assert " | " in output  # AM replacement


def test_print_inspect_with_error():
    info = {
        "file": "BROKEN",
        "record_count": 0,
        "sample_keys": ["KEY-X"],
        "field_counts": [],
        "sample_records": [{"key": "KEY-X", "error": "File not found"}],
        "elapsed_seconds": 0.001,
    }
    buf = io.StringIO()
    print_inspect(info, file=buf)
    output = buf.getvalue()
    assert "ERROR" in output
    assert "File not found" in output


def test_print_inspect_empty():
    info = {
        "file": "EMPTY",
        "record_count": 0,
        "sample_keys": [],
        "field_counts": [],
        "sample_records": [],
        "elapsed_seconds": 0.0,
    }
    buf = io.StringIO()
    print_inspect(info, file=buf)
    output = buf.getvalue()
    assert "EMPTY" in output
    assert "0" in output


# ---------------------------------------------------------------------------
# inspect_json
# ---------------------------------------------------------------------------

def test_inspect_json_output():
    info = _make_inspect_info()
    buf = io.StringIO()
    inspect_json(info, file=buf)
    output = buf.getvalue()

    parsed = json.loads(output)
    assert parsed["file"] == "ATTENDANCE"
    assert parsed["record_count"] == 300_000
    assert len(parsed["sample_records"]) == 2


# ---------------------------------------------------------------------------
# Entry point
# ---------------------------------------------------------------------------

def main() -> None:
    test_coerce_id_str()
    test_coerce_id_bytes()
    test_coerce_id_dynarray_list_attr()
    test_coerce_id_dynarray_string_list()
    test_coerce_id_fallback()
    print("PASS: _coerce_id")

    test_coerce_value_str()
    test_coerce_value_bytes()
    test_coerce_value_dynarray()
    test_coerce_value_other()
    print("PASS: _coerce_value")

    test_print_inspect_output()
    print("PASS: print_inspect")

    test_print_inspect_with_error()
    print("PASS: print_inspect with error")

    test_print_inspect_empty()
    print("PASS: print_inspect empty")

    test_inspect_json_output()
    print("PASS: inspect_json")

    print("\nAll inspect tests passed.")


if __name__ == "__main__":
    main()
