"""Persist and query operational system events (warnings / errors)."""

from __future__ import annotations

import logging
from datetime import datetime, timedelta, timezone

from sqlalchemy import select, update
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.system_event import SystemEvent

logger = logging.getLogger(__name__)

DEFAULT_LOOKBACK_DAYS = 7
DEFAULT_DEDUP_HOURS = 24
SEVERITIES = frozenset({"warning", "error"})


async def log_event(
    db: AsyncSession,
    *,
    severity: str,
    source: str,
    code: str,
    message: str,
    symbol: str | None = None,
    dedup_key: str | None = None,
    dedup_hours: int = DEFAULT_DEDUP_HOURS,
) -> SystemEvent | None:
    """Insert a system event, optionally de-duplicating recent identical keys.

    Returns the new row, or None when a recent dedup_key already exists.
    Commits the session.
    """
    severity = (severity or "").strip().lower()
    if severity not in SEVERITIES:
        severity = "warning"
    source = (source or "system")[:64]
    code = (code or "unknown")[:64]
    message = (message or "").strip() or code
    symbol = symbol.strip().upper()[:20] if symbol else None
    dedup_key = dedup_key[:200] if dedup_key else None

    if dedup_key:
        cutoff = datetime.now(timezone.utc) - timedelta(hours=max(1, dedup_hours))
        existing = await db.execute(
            select(SystemEvent.id)
            .where(
                SystemEvent.dedup_key == dedup_key,
                SystemEvent.created_at >= cutoff,
            )
            .limit(1)
        )
        if existing.scalar_one_or_none() is not None:
            return None

    row = SystemEvent(
        severity=severity,
        source=source,
        code=code,
        message=message[:4000],
        symbol=symbol,
        dedup_key=dedup_key,
        created_at=datetime.now(timezone.utc),
    )
    db.add(row)
    await db.commit()
    await db.refresh(row)
    return row


async def log_event_standalone(
    *,
    severity: str,
    source: str,
    code: str,
    message: str,
    symbol: str | None = None,
    dedup_key: str | None = None,
) -> None:
    """Open a short-lived session and log an event (for scheduler / fire-and-forget)."""
    try:
        from app.database import async_session_factory

        async with async_session_factory() as db:
            await log_event(
                db,
                severity=severity,
                source=source,
                code=code,
                message=message,
                symbol=symbol,
                dedup_key=dedup_key,
            )
    except Exception:
        logger.exception("Failed to persist system event %s/%s", source, code)


async def list_events(
    db: AsyncSession,
    *,
    days: int = DEFAULT_LOOKBACK_DAYS,
    severity: str | None = None,
    unacknowledged_only: bool = False,
    limit: int = 200,
) -> list[SystemEvent]:
    days = max(1, min(int(days), 30))
    cutoff = datetime.now(timezone.utc) - timedelta(days=days)
    stmt = (
        select(SystemEvent)
        .where(SystemEvent.created_at >= cutoff)
        .order_by(SystemEvent.created_at.desc())
        .limit(max(1, min(limit, 500)))
    )
    if severity in SEVERITIES:
        stmt = stmt.where(SystemEvent.severity == severity)
    if unacknowledged_only:
        stmt = stmt.where(SystemEvent.acknowledged_at.is_(None))
    result = await db.execute(stmt)
    return list(result.scalars().all())


async def summary(
    db: AsyncSession,
    *,
    days: int = DEFAULT_LOOKBACK_DAYS,
) -> dict:
    """Counts for badge + admin header."""
    days = max(1, min(int(days), 30))
    cutoff = datetime.now(timezone.utc) - timedelta(days=days)
    rows = (
        await db.execute(
            select(SystemEvent.severity, SystemEvent.acknowledged_at).where(
                SystemEvent.created_at >= cutoff
            )
        )
    ).all()
    total = len(rows)
    unacked = 0
    errors = 0
    warnings = 0
    for severity, acknowledged_at in rows:
        if acknowledged_at is not None:
            continue
        unacked += 1
        if severity == "error":
            errors += 1
        elif severity == "warning":
            warnings += 1
    return {
        "days": days,
        "total": total,
        "unacknowledged": unacked,
        "unacknowledged_errors": errors,
        "unacknowledged_warnings": warnings,
    }


async def acknowledge_all(
    db: AsyncSession,
    *,
    days: int = DEFAULT_LOOKBACK_DAYS,
) -> int:
    """Mark unacknowledged events in the lookback window as dismissed. Returns count."""
    days = max(1, min(int(days), 30))
    cutoff = datetime.now(timezone.utc) - timedelta(days=days)
    now = datetime.now(timezone.utc)
    result = await db.execute(
        update(SystemEvent)
        .where(
            SystemEvent.created_at >= cutoff,
            SystemEvent.acknowledged_at.is_(None),
        )
        .values(acknowledged_at=now)
    )
    await db.commit()
    return int(result.rowcount or 0)


def event_to_dict(row: SystemEvent) -> dict:
    return {
        "id": row.id,
        "severity": row.severity,
        "source": row.source,
        "code": row.code,
        "message": row.message,
        "symbol": row.symbol,
        "created_at": row.created_at.isoformat() if row.created_at else None,
        "acknowledged_at": row.acknowledged_at.isoformat() if row.acknowledged_at else None,
    }
