from __future__ import annotations

from dataclasses import dataclass, field


@dataclass(frozen=True)
class RelationshipField:
    left: str
    right: str
    name: str = ""


@dataclass(frozen=True)
class StreamProfile:
    stream_id: str
    name: str
    entity_field: str
    timestamp_field: str
    entity_fields: tuple[str, ...] = ()
    categorical_fields: tuple[str, ...] = ()
    numeric_fields: tuple[str, ...] = ()
    detectors: dict[str, dict[str, object]] = field(default_factory=dict)
    field_weights: dict[str, object] = field(default_factory=dict)
    relationship_fields: tuple[RelationshipField, ...] = ()


def _detectors(value: object) -> dict[str, dict[str, object]]:
    if not isinstance(value, dict):
        return {}
    output: dict[str, dict[str, object]] = {}
    for name, settings in value.items():
        if str(name) not in {"auth_failure", "dns_query", "deny_action"} or not isinstance(settings, dict):
            continue
        try:
            minimum = max(1, int(settings.get("minimum", 1)))
            z_threshold = max(1.0, float(settings.get("z_threshold", 3.0)))
        except (TypeError, ValueError):
            continue
        output[str(name)] = {"enabled": bool(settings.get("enabled", True)), "minimum": minimum, "z_threshold": z_threshold}
    return output


def _field_weights(value: object) -> dict[str, object]:
    if not isinstance(value, dict):
        return {}
    output: dict[str, object] = {}
    for key, setting in value.items():
        if isinstance(setting, dict):
            nested: dict[str, float] = {}
            for nested_key, nested_value in setting.items():
                try:
                    nested[str(nested_key).lower()] = max(0.0, min(5.0, float(nested_value)))
                except (TypeError, ValueError):
                    continue
            if nested:
                output[str(key).lower()] = nested
            continue
        try:
            output[str(key).lower()] = max(0.0, min(5.0, float(setting)))
        except (TypeError, ValueError):
            continue
    return output


def _relationship_fields(value: object) -> tuple[RelationshipField, ...]:
    output: list[RelationshipField] = []
    seen: set[tuple[str, str]] = set()
    for item in value if isinstance(value, list) else []:
        if not isinstance(item, dict):
            continue
        left = str(item.get("left") or item.get("entity") or item.get("from") or "").strip()
        right = str(item.get("right") or item.get("value") or item.get("to") or "").strip()
        if not left or not right or left == right:
            continue
        key = (left.lower(), right.lower())
        if key in seen:
            continue
        seen.add(key)
        name = str(item.get("name") or f"{left}->{right}").strip()
        output.append(RelationshipField(left=left, right=right, name=name))
    return tuple(output)


def parse_profiles(value: object) -> dict[str, StreamProfile]:
    profiles: dict[str, StreamProfile] = {}
    for item in value if isinstance(value, list) else []:
        if not isinstance(item, dict):
            continue
        stream_id = str(item.get("stream_id", "")).strip()
        entity_fields = tuple(dict.fromkeys(str(field).strip() for field in item.get("entity_fields", []) if str(field).strip())) if isinstance(item.get("entity_fields", []), list) else ()
        entity = str(item.get("entity_field", "")).strip() or (entity_fields[0] if entity_fields else "")
        if not entity_fields and entity:
            entity_fields = (entity,)
        timestamp = str(item.get("timestamp_field", "timestamp")).strip()
        if not stream_id or not entity:
            continue
        detectors = _detectors(item.get("detectors", {}))
        field_weights = _field_weights(item.get("field_weights", {}))
        relationship_fields = _relationship_fields(item.get("relationship_fields", []))
        profiles[stream_id] = StreamProfile(
            stream_id, str(item.get("name", "")).strip() or stream_id, entity, timestamp, entity_fields,
            tuple(str(field) for field in item.get("categorical_fields", []) if field),
            tuple(str(field) for field in item.get("numeric_fields", []) if field),
            detectors,
            field_weights,
            relationship_fields,
        )
    return profiles
