"""Tests for utils/aggregation.py -- coalesce, group_and_sort."""

from datetime import datetime, timezone

from astrix_openclaw_scanner.utils.aggregation import coalesce, group_and_sort

# ── coalesce ─────────────────────────────────────────────────────────────────

class TestCoalesce:
    def test_first_non_empty_wins(self):
        assert coalesce("a", "b", "c") == "a"

    def test_skips_none(self):
        assert coalesce(None, "b") == "b"

    def test_skips_empty_string(self):
        assert coalesce("", "b") == "b"

    def test_skips_none_and_empty(self):
        assert coalesce(None, "", "c") == "c"

    def test_all_empty_returns_empty(self):
        assert coalesce(None, "", None) == ""

    def test_no_args_returns_empty(self):
        assert coalesce() == ""


# ── group_and_sort ───────────────────────────────────────────────────────────

OS_MAPPING = {"Win": "Windows", "Mac": "macOS", "Lin": "Linux"}

_TS_EARLY = datetime(2026, 2, 4, 14, 0, tzinfo=timezone.utc)
_TS_LATE = datetime(2026, 2, 5, 10, 0, tzinfo=timezone.utc)


def _entry(
    endpoint="host-1",
    username="user-1",
    os="Win",
    commandline="cmd",
    ts=_TS_EARLY,
):
    """Helper to build a normalized entry dict."""
    return {
        "endpoint": endpoint,
        "username": username,
        "os": os,
        "commandline": commandline,
        "ts": ts,
    }


class TestGroupAndSort:
    def test_empty_input(self):
        assert group_and_sort([], OS_MAPPING) == []

    def test_single_entry(self):
        result = group_and_sort([_entry()], OS_MAPPING)
        assert len(result) == 1
        assert result[0]["endpoint"] == "host-1"
        assert result[0]["username"] == "user-1"
        assert result[0]["os"] == "Windows"  # "Win" mapped
        assert result[0]["commandline"] == "cmd"

    def test_grouping_same_endpoint_username(self):
        entries = [
            _entry(ts=_TS_EARLY, commandline="old-cmd"),
            _entry(ts=_TS_LATE, commandline="new-cmd"),
        ]
        result = group_and_sort(entries, OS_MAPPING)
        assert len(result) == 1

    def test_merge_keeps_latest_timestamp(self):
        entries = [
            _entry(ts=_TS_EARLY, commandline="old-cmd"),
            _entry(ts=_TS_LATE, commandline="new-cmd"),
        ]
        result = group_and_sort(entries, OS_MAPPING)
        assert result[0]["commandline"] == "new-cmd"
        assert "Feb 05, 2026" in result[0]["last_seen"]

    def test_merge_prefers_known_over_unknown(self):
        entries = [
            _entry(os="Unknown"),
            _entry(os="Win", ts=_TS_EARLY),
        ]
        result = group_and_sort(entries, OS_MAPPING)
        assert result[0]["os"] == "Windows"

    def test_distinct_groups(self):
        entries = [
            _entry(endpoint="host-1", username="user-1"),
            _entry(endpoint="host-2", username="user-2"),
        ]
        result = group_and_sort(entries, OS_MAPPING)
        assert len(result) == 2

    def test_sort_descending_by_timestamp(self):
        entries = [
            _entry(endpoint="host-old", ts=_TS_EARLY),
            _entry(endpoint="host-new", username="user-2", ts=_TS_LATE),
        ]
        result = group_and_sort(entries, OS_MAPPING)
        assert result[0]["endpoint"] == "host-new"
        assert result[1]["endpoint"] == "host-old"

    def test_os_mapping_applied(self):
        for raw, expected in [("Win", "Windows"), ("Mac", "macOS"), ("Lin", "Linux")]:
            result = group_and_sort([_entry(os=raw)], OS_MAPPING)
            assert result[0]["os"] == expected

    def test_unmapped_os_left_as_is(self):
        result = group_and_sort([_entry(os="FreeBSD")], OS_MAPPING)
        assert result[0]["os"] == "FreeBSD"

    def test_timestamp_formatting(self):
        ts = datetime(2026, 2, 4, 14, 20, tzinfo=timezone.utc)
        result = group_and_sort([_entry(ts=ts)], OS_MAPPING)
        assert result[0]["last_seen"] == "Feb 04, 2026 14:20"

    def test_internal_ts_key_removed(self):
        result = group_and_sort([_entry()], OS_MAPPING)
        assert "_ts" not in result[0]

    def test_none_timestamp(self):
        result = group_and_sort([_entry(ts=None)], OS_MAPPING)
        assert result[0]["last_seen"] == "Unknown"
