"""Append-only audit log for Ilum CLI operations."""

from __future__ import annotations

import json
import os
from dataclasses import asdict, dataclass, field
from pathlib import Path


@dataclass
class AuditEntry:
    """A single audit log entry."""

    timestamp: str
    operation: str  # "install" | "upgrade" | "enable" | "disable" | "uninstall"
    release: str
    namespace: str
    context: str
    user: str
    chart_version: str
    modules: list[str] = field(default_factory=list)
    set_flags: list[str] = field(default_factory=list)
    success: bool = True
    duration_seconds: float = 0.0
    error_code: str = ""


def _get_user() -> str:
    """Best-effort user identification."""
    try:
        return os.getlogin()
    except OSError:
        return os.environ.get("USER", os.environ.get("USERNAME", "unknown"))


class AuditLog:
    """Append-only JSONL audit log.

    Stores entries at ``{state_dir}/audit.jsonl``.
    """

    def __init__(self, state_dir: Path) -> None:
        self._path = state_dir / "audit.jsonl"

    @property
    def path(self) -> Path:
        return self._path

    def append(self, entry: AuditEntry) -> None:
        """Append an entry to the audit log."""
        self._path.parent.mkdir(parents=True, exist_ok=True)
        line = json.dumps(asdict(entry), default=str)
        with self._path.open("a") as f:
            f.write(line + "\n")

    def query(
        self,
        *,
        release: str | None = None,
        last_n: int | None = None,
        operation: str | None = None,
    ) -> list[AuditEntry]:
        """Read and filter audit log entries.

        Parameters
        ----------
        release:
            Filter by release name.
        last_n:
            Return only the last *n* entries (after filtering).
        operation:
            Filter by operation type.
        """
        if not self._path.exists():
            return []

        entries: list[AuditEntry] = []
        with self._path.open() as f:
            for line in f:
                line = line.strip()
                if not line:
                    continue
                try:
                    data = json.loads(line)
                    entry = AuditEntry(
                        **{k: v for k, v in data.items() if k in AuditEntry.__dataclass_fields__}
                    )
                    # Apply filters
                    if release and entry.release != release:
                        continue
                    if operation and entry.operation != operation:
                        continue
                    entries.append(entry)
                except (json.JSONDecodeError, TypeError):
                    continue  # Skip malformed lines

        if last_n is not None:
            entries = entries[-last_n:]

        return entries
