from __future__ import annotations

import json
import re
import time
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

from .models import EventRecord


class EventEmitter:
    """Writes atomic JSON event files to the events directory."""

    def __init__(
        self, events_dir: Path, node_name: str, location_tag: str = ""
    ) -> None:
        # Defense in depth (config.py already fails fast on this): the event id
        # embeds node_name into the filename, so "unknown"/empty here is exactly
        # what produces evt-unknown-* poison files. Refuse at the emit boundary
        # too, so no future call path can quietly reintroduce it (see d5139c9).
        normalized = (node_name or "").strip()
        if normalized.lower() in ("", "unknown"):
            raise ValueError(
                f"EventEmitter refuses node_name={node_name!r} — would write "
                "evt-unknown-* files that poison the observer checkpoint "
                "(see d5139c9). node_name must be a real node identity."
            )
        self._events_dir = events_dir
        self._node_name = normalized
        self._location_tag = location_tag
        self._seq = 0
        events_dir.mkdir(parents=True, exist_ok=True)

    def _make_id(self, event_type: str, service: str) -> str:
        # Sequence suffix guarantees uniqueness even when multiple events of the
        # same type are emitted within the same millisecond.
        self._seq += 1
        ts = int(time.time())
        svc_slug = re.sub(r"[^a-z0-9]", "-", (service or "ha").lower())[:32].strip("-")
        return f"evt-{self._node_name}-{ts}-{event_type}-{svc_slug}-{self._seq}"

    def emit(
        self,
        event_type: str,
        severity: str,
        service: str,
        message: str,
        payload: dict[str, Any] | None = None,
    ) -> str:
        event_id = self._make_id(event_type, service)
        merged: dict[str, Any] = {}
        if self._location_tag:
            merged["location_tag"] = self._location_tag
        merged.update(payload or {})
        record = EventRecord(
            id=event_id,
            timestamp=int(time.time()),
            date=datetime.now(timezone.utc).isoformat(),
            type=event_type,
            severity=severity,
            node=self._node_name,
            service=service,
            message=message,
            payload=merged,
        )
        path = self._events_dir / f"{event_id}.json"
        tmp = path.with_suffix(".tmp")
        tmp.write_text(json.dumps(record.model_dump(), indent=2))
        tmp.rename(path)
        return event_id
