from __future__ import annotations

import itertools
import time
from typing import Any, Dict, Optional

from pulselog.levels import Level

# ---------------------------------------------------------------------------
# Global sequence counter — atomic on CPython due to the GIL.
# Provides a monotonic ordering tie-breaker when timestamps collide.
# ---------------------------------------------------------------------------
_seq_counter = itertools.count(1)


class LogRecord:
    """Immutable snapshot of a single log event.

    Attributes:
        level:     Severity level.
        msg:       Log message string.
        timestamp: Unix timestamp (seconds) when the record was created.
        extra:     Arbitrary key/value metadata supplied by the caller,
                   or None when the caller passed no kwargs (avoids a dict
                   allocation on every plain info/debug call).
        tag:       Active tag label at the time of logging, or None.
        seq:       Monotonic sequence number — unique per process lifetime.
                   Guarantees ordering when two records share a timestamp.

    Why __slots__ instead of @dataclass:
        @dataclass(slots=True) requires Python 3.10+.  A plain class with
        __slots__ works on 3.8+ and is faster:  no per-instance __dict__
        means ~3× less heap allocation per record and dramatically lower GC
        pressure at high throughput (the root cause of the 80µs p99.9 spike).
    """

    __slots__ = ("level", "msg", "timestamp", "extra", "tag", "seq")

    def __init__(
        self,
        level: Level,
        msg: str,
        timestamp: Optional[float] = None,
        extra: Optional[Dict[str, Any]] = None,
        tag: Optional[str] = None,
        seq: Optional[int] = None,
    ) -> None:
        """
        Args:
            level:     Severity level enum value.
            msg:       Log message string.  Caller is responsible for str()
                       conversion — LogRecord does not cast to avoid the
                       redundant type-check at 177k+ calls/sec.
            timestamp: Unix epoch seconds.  Defaults to time.time() if omitted.
            extra:     Key/value metadata dict, or None when no kwargs were
                       passed.  Keeping None (rather than {}) avoids an empty-
                       dict allocation on every no-kwarg log call.
            tag:       Active tag for the calling thread, or None.
            seq:       Sequence number override.  Auto-assigned from the global
                       counter when omitted.
        """
        self.level     = level
        self.msg       = msg
        self.timestamp = time.time() if timestamp is None else timestamp
        self.extra     = extra      # may be None — callers must handle this
        self.tag       = tag
        self.seq       = next(_seq_counter) if seq is None else seq

    # ------------------------------------------------------------------
    # Serialisation
    # ------------------------------------------------------------------

    def to_dict(self) -> Dict[str, Any]:
        """Serialise to a JSON-serialisable dict.

        ``extra`` is normalised to {} in the output so downstream consumers
        (WebSocket broadcast, checkpoint store, tests) never have to
        special-case None.

        Returns:
            Dict with string keys and JSON-safe values.
        """
        return {
            "level":     self.level.name,
            "msg":       self.msg,
            "timestamp": self.timestamp,
            "extra":     self.extra if self.extra is not None else {},
            "tag":       self.tag,
            "seq":       self.seq,
        }

    # ------------------------------------------------------------------
    # Debug
    # ------------------------------------------------------------------

    def __repr__(self) -> str:
        extra_repr = f", extra={self.extra!r}" if self.extra else ""
        tag_repr   = f", tag={self.tag!r}"     if self.tag   else ""
        return (
            f"LogRecord(level={self.level.name}, msg={self.msg!r}, "
            f"seq={self.seq}{tag_repr}{extra_repr})"
        )

    def __eq__(self, other: object) -> bool:
        """Equality by sequence number — unique per process lifetime."""
        if not isinstance(other, LogRecord):
            return NotImplemented
        return self.seq == other.seq

    def __hash__(self) -> int:
        return hash(self.seq)