from __future__ import annotations

import json
import uuid
from dataclasses import asdict, dataclass, field
from datetime import datetime, timezone
from typing import Any, Dict, List, Optional


def utc_now_iso() -> str:
    return datetime.now(timezone.utc).isoformat(timespec="milliseconds")


@dataclass
class NormalizedEvent:
    """Single normalized event schema used by all collectors."""

    event_id: str = field(default_factory=lambda: str(uuid.uuid4()))
    timestamp: str = field(default_factory=utc_now_iso)
    event_type: str = "unknown"
    source: str = "unknown"
    severity: str = "info"
    confidence: int = 0
    hostname: Optional[str] = None
    user: Optional[str] = None
    pid: Optional[int] = None
    parent_pid: Optional[int] = None
    parent_process_name: Optional[str] = None
    process_name: Optional[str] = None
    path: Optional[str] = None
    command_line: Optional[str] = None
    sha256: Optional[str] = None
    signer: Optional[str] = None
    signed: Optional[bool] = None
    signature_status: Optional[str] = None
    local_ip: Optional[str] = None
    local_port: Optional[int] = None
    remote_ip: Optional[str] = None
    remote_port: Optional[int] = None
    domain: Optional[str] = None
    rule_id: Optional[str] = None
    risk_score: int = 0
    tags: List[str] = field(default_factory=list)
    metadata: Dict[str, Any] = field(default_factory=dict)

    def to_dict(self) -> Dict[str, Any]:
        payload = asdict(self)
        payload["tags"] = list(self.tags or [])
        payload["metadata"] = dict(self.metadata or {})
        return payload

    def to_json(self, *, indent: Optional[int] = None) -> str:
        return json.dumps(self.to_dict(), indent=indent, sort_keys=True)

    @classmethod
    def from_dict(cls, payload: Dict[str, Any]) -> "NormalizedEvent":
        sanitized = dict(payload or {})
        sanitized["tags"] = list(sanitized.get("tags") or [])
        sanitized["metadata"] = dict(sanitized.get("metadata") or {})
        if "event_id" not in sanitized or not sanitized["event_id"]:
            sanitized["event_id"] = str(uuid.uuid4())
        if "timestamp" not in sanitized or not sanitized["timestamp"]:
            sanitized["timestamp"] = utc_now_iso()
        return cls(**sanitized)

    @classmethod
    def from_json(cls, serialized: str) -> "NormalizedEvent":
        return cls.from_dict(json.loads(serialized))

    def with_metadata(self, **kwargs: Any) -> "NormalizedEvent":
        updated = dict(self.metadata or {})
        updated.update(kwargs)
        self.metadata = updated
        return self
