"""Tests for microsoft_defender/aggregator.py -- end-to-end aggregation."""

from astrix_openclaw_scanner.microsoft_defender.aggregator import aggregate_events
from tests.conftest import (
    DEFENDER_CHROME_NOISE_EVENT,
    DEFENDER_LINUX_EVENT,
    DEFENDER_OPENCLAW_EVENT,
    DEFENDER_SPARSE_EVENT,
    to_json_list,
)


class TestDefenderNormalization:
    """Verify Defender fields map to the common output format."""

    def test_field_mapping(self):
        result = aggregate_events(to_json_list(DEFENDER_OPENCLAW_EVENT))
        assert len(result) == 1
        row = result[0]
        assert row["endpoint"] == "LT-PC01"
        assert row["username"] == "jdoe"
        assert row["os"] == "Windows"  # "Windows10" mapped
        assert "Feb 04, 2026" in row["last_seen"]
        # Commandline coalesced: ProcessCommandLine is first non-empty
        assert "openclaw-agent.exe" in row["commandline"]

    def test_linux_os_mapping(self):
        result = aggregate_events(to_json_list(DEFENDER_LINUX_EVENT))
        assert result[0]["os"] == "Linux"

    def test_iso_timestamp_parsed(self):
        result = aggregate_events(to_json_list(DEFENDER_OPENCLAW_EVENT))
        assert "14:20" in result[0]["last_seen"]


class TestDefenderFiltering:
    """Verify noise removal and the skip_filter flag."""

    def test_noise_removed(self):
        events = to_json_list(DEFENDER_OPENCLAW_EVENT, DEFENDER_CHROME_NOISE_EVENT)
        result = aggregate_events(events)
        # Chrome noise removed -- only one endpoint/user pair with openclaw data
        assert len(result) == 1
        assert "chrome" not in result[0]["commandline"].lower()

    def test_skip_filter_keeps_noise(self):
        events = to_json_list(DEFENDER_OPENCLAW_EVENT, DEFENDER_CHROME_NOISE_EVENT)
        result = aggregate_events(events, skip_filter=True)
        # Both events are same (endpoint, username). Chrome has later timestamp
        # so its commandline wins the merge.
        assert len(result) == 1
        assert "chrome" in result[0]["commandline"].lower()

    def test_safe_fallback_on_bad_data(self):
        """An event that causes extraction errors should NOT be filtered out."""
        bad_event = {
            **DEFENDER_OPENCLAW_EVENT,
            "FileName": 12345,
            "FolderPath": 12345,
            "ProcessCommandLine": 12345,
        }
        result = aggregate_events(to_json_list(bad_event))
        assert len(result) == 1


class TestDefenderMissingFields:
    """Verify graceful handling of missing/absent fields."""

    def test_sparse_event_defaults_to_unknown(self):
        result = aggregate_events(to_json_list(DEFENDER_SPARSE_EVENT))
        assert len(result) == 1
        row = result[0]
        assert row["endpoint"] == "Unknown"  # DeviceName absent from event
        assert row["username"] == "Unknown"  # AccountName absent from event
        assert row["os"] == "Unknown"  # osType absent from event


class TestDefenderGrouping:
    """Verify deduplication across multiple events."""

    def test_same_endpoint_username_grouped(self):
        early = {**DEFENDER_OPENCLAW_EVENT, "Timestamp": "2026-02-04T14:20:00.000Z"}
        late = {
            **DEFENDER_OPENCLAW_EVENT,
            "Timestamp": "2026-02-05T10:00:00.000Z",
            "ProcessCommandLine": "agent --v2",
        }
        result = aggregate_events(to_json_list(early, late))
        assert len(result) == 1
        assert result[0]["commandline"] == "agent --v2"

    def test_two_users_same_endpoint(self):
        """Two users on the same machine should produce two separate rows."""
        user_a = {**DEFENDER_OPENCLAW_EVENT, "AccountName": "alice"}
        user_b = {**DEFENDER_OPENCLAW_EVENT, "AccountName": "bob", "Timestamp": "2026-02-05T10:00:00.000Z"}
        result = aggregate_events(to_json_list(user_a, user_b))
        assert len(result) == 2
        usernames = {r["username"] for r in result}
        assert usernames == {"alice", "bob"}
        # Both rows share the same endpoint
        assert all(r["endpoint"] == "LT-PC01" for r in result)

    def test_different_endpoints_not_grouped(self):
        events = to_json_list(DEFENDER_OPENCLAW_EVENT, DEFENDER_LINUX_EVENT)
        result = aggregate_events(events)
        assert len(result) == 2
