"""Registro de eventos persistente (append-only)."""
from __future__ import annotations

import threading
from collections import deque
from pathlib import Path
from typing import Callable, Deque, List, Optional

from .models import EventLevel, LogEvent, app_root, ensure_dirs, now_str


class EventLog:
    def __init__(self, maxlen: int = 5000):
        self._events: Deque[LogEvent] = deque(maxlen=maxlen)
        self._lock = threading.Lock()
        self._listeners: List[Callable[[LogEvent], None]] = []
        ensure_dirs()
        self._file = app_root() / "logs" / f"events_{now_str().replace(':', '-').replace(' ', '_')}.log"
        # archivo sesión
        self._session = open(self._file, "a", encoding="utf-8")

    @property
    def path(self) -> Path:
        return self._file

    def subscribe(self, cb: Callable[[LogEvent], None]) -> None:
        self._listeners.append(cb)

    def add(self, level: str, ip: str, event: str, details: str = "") -> LogEvent:
        ev = LogEvent(ts=now_str(), level=level, ip=ip or "-", event=event, details=details)
        with self._lock:
            self._events.append(ev)
            try:
                self._session.write(ev.as_line() + "\n")
                self._session.flush()
            except Exception:
                pass
        for cb in list(self._listeners):
            try:
                cb(ev)
            except Exception:
                pass
        return ev

    def info(self, ip: str, event: str, details: str = "") -> LogEvent:
        return self.add(EventLevel.INFO.value, ip, event, details)

    def warning(self, ip: str, event: str, details: str = "") -> LogEvent:
        return self.add(EventLevel.WARNING.value, ip, event, details)

    def error(self, ip: str, event: str, details: str = "") -> LogEvent:
        return self.add(EventLevel.ERROR.value, ip, event, details)

    def critical(self, ip: str, event: str, details: str = "") -> LogEvent:
        return self.add(EventLevel.CRITICAL.value, ip, event, details)

    def all(self, levels: Optional[List[str]] = None) -> List[LogEvent]:
        with self._lock:
            if not levels:
                return list(self._events)
            wanted = set(levels)
            return [e for e in self._events if e.level in wanted]

    def close(self) -> None:
        try:
            self._session.close()
        except Exception:
            pass
