"""Microsoft Defender event aggregation for OpenClaw detection."""

import json
import logging
from typing import Any

from astrix_openclaw_scanner.utils.aggregation import coalesce, group_and_sort
from astrix_openclaw_scanner.utils.filters import (
    extract_binary_from_commandline,
    extract_binary_name,
    is_filtered_binary,
)
from astrix_openclaw_scanner.utils.timestamps import parse_iso_timestamp

logger = logging.getLogger(__name__)

OS_MAPPING = {
    "Windows": "Windows",
    "Windows10": "Windows",
    "Windows11": "Windows",
    "macOS": "macOS",
    "Linux": "Linux",
}


def _is_windows(event: dict[str, Any]) -> bool:
    """Derive OS hint from the event's ``osType`` field."""
    return (event.get("osType") or "").lower().startswith("windows")


def _is_noise_event(event: dict[str, Any]) -> bool:
    """Determine whether a Defender event is noise.

    Extracts the binary name from Defender-specific fields and checks it
    against the deny-list. Returns ``False`` (keep the event) on any error so
    that real detections are never silently dropped.
    """
    try:
        is_win = _is_windows(event)
        binary = coalesce(
            extract_binary_name(event.get("FileName"), is_windows=is_win),
            extract_binary_name(event.get("FolderPath"), is_windows=is_win),
            extract_binary_name(
                extract_binary_from_commandline(event.get("ProcessCommandLine")),
                is_windows=is_win,
            ),
        )
        return is_filtered_binary(binary)
    except Exception:
        logger.debug("Filtering failed for entry, keeping it", exc_info=True)
        return False


def _parse_and_filter(
    events: list[str],
    *,
    skip_filter: bool = False,
) -> list[dict[str, Any]]:
    """Parse raw JSON events and optionally remove noise.

    Args:
        events: JSON-serialized Defender events.
        skip_filter: When ``True``, skip noise filtering.

    Returns:
        Parsed event dicts with a ``_commandline`` key added.
    """
    parsed = []
    for e in events:
        event = json.loads(e)
        event["_commandline"] = coalesce(
            event.get("ProcessCommandLine"),
            event.get("FolderPath"),
            event.get("FileName"),
        )
        parsed.append(event)

    if skip_filter:
        return parsed

    before = len(parsed)
    filtered = [e for e in parsed if not _is_noise_event(e)]
    removed = before - len(filtered)
    if removed:
        logger.info(f"Filtered out {removed} noise entries")
    return filtered


def aggregate_events(
    events: list[str],
    *,
    skip_filter: bool = False,
) -> list[dict[str, Any]]:
    """Aggregate Defender raw events: filter first, then group.

    Args:
        events: List of JSON-serialized raw events
        skip_filter: When ``True``, disable post-collection noise filtering.

    Returns:
        List of normalized endpoint dicts
    """
    logger.info(f"Aggregating {len(events)} Defender raw events")

    filtered = _parse_and_filter(events, skip_filter=skip_filter)

    if not filtered:
        logger.warning("No events remaining after filtering")
        return []

    # Step 3: Normalize to common format
    normalized = []
    for event in filtered:
        normalized.append({
            "endpoint": event.get("DeviceName") or "Unknown",
            "username": event.get("AccountName") or "Unknown",
            "os": event.get("osType") or "Unknown",
            "commandline": event["_commandline"],
            "ts": parse_iso_timestamp(event.get("Timestamp")),
        })

    # Step 4: Group, merge, sort
    result = group_and_sort(normalized, OS_MAPPING)
    logger.info(f"Aggregated to {len(result)} unique endpoints")
    return result
