import json
import logging
from datetime import datetime

from soagila.db.sqlite_init import get_db
from soagila.db.event_bus import publish_event
from soagila.core.dns.models import AccessEvent, AnomalyEventModel
from soagila.core.dns.behavioral_scorer import calculate_bas, get_action_for_score, update_profile

logger = logging.getLogger("soagila.dns.anomaly")

CHANNEL = "soagila:metrics"

# Alert threshold — BAS scores above this are logged as anomaly events
ALERT_THRESHOLD = 30.0


async def process_event(event: AccessEvent) -> AnomalyEventModel | None:
    """
    Process an access event through the DNS pipeline:
    1. Update behavioral profile
    2. Calculate BAS score
    3. If above threshold, create anomaly event
    4. Determine graduated response action
    """
    # Update profile baseline
    await update_profile(event.user_id, event)

    # Calculate BAS
    bas_score = await calculate_bas(event.user_id, event)

    # Below threshold = normal, no anomaly
    if bas_score < ALERT_THRESHOLD:
        return None

    # Determine action
    action = get_action_for_score(bas_score)

    # Classify anomaly type
    anomaly_type = _classify_anomaly(event, bas_score)

    # Create anomaly event
    anomaly = AnomalyEventModel(
        user_id=event.user_id,
        session_id=event.session_id,
        bas_score=bas_score,
        anomaly_type=anomaly_type,
        details={
            "event_type": event.event_type,
            "endpoint": event.endpoint,
            "topic": event.topic,
            "hour": event.timestamp.hour,
        },
        action_taken=action,
    )

    # Persist to SQLite
    await _persist_anomaly(anomaly)

    # Publish alert event
    alert = json.dumps({
        "type": "anomaly_detected",
        "user_id": event.user_id,
        "bas_score": bas_score,
        "anomaly_type": anomaly_type,
        "action": action,
        "timestamp": datetime.utcnow().isoformat(),
    })
    await publish_event(CHANNEL, alert)

    logger.warning(
        f"Anomaly detected: user={event.user_id}, BAS={bas_score:.1f}, "
        f"type={anomaly_type}, action={action}"
    )

    return anomaly


def _classify_anomaly(event: AccessEvent, bas_score: float) -> str:
    """Classify the type of anomaly based on event characteristics."""
    hour = event.timestamp.hour

    # Late night / early morning access
    if hour < 6 or hour > 22:
        return "unusual_hour"

    # High BAS likely from frequency
    if bas_score >= 70:
        return "unusual_frequency"

    # Default
    return "unusual_access_pattern"


async def _persist_anomaly(anomaly: AnomalyEventModel):
    """Store anomaly event in SQLite."""
    db = await get_db()
    await db.execute(
        """
            INSERT INTO anomaly_events (
                id, user_id, session_id, bas_score, anomaly_type,
                details, action_taken, resolved, created_at
            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
        """,
        (
            anomaly.id,
            anomaly.user_id,
            anomaly.session_id,
            anomaly.bas_score,
            anomaly.anomaly_type,
            json.dumps(anomaly.details),
            anomaly.action_taken,
            anomaly.resolved,
            anomaly.created_at.isoformat(),
        ),
    )
    await db.commit()


async def get_anomalies(
    user_id: str | None = None,
    severity: str | None = None,
    limit: int = 50,
) -> list[AnomalyEventModel]:
    """Query anomaly events with optional filters."""
    db = await get_db()
    conditions = []
    params: list = []

    if user_id:
        conditions.append("user_id = ?")
        params.append(user_id)
    if severity == "high":
        conditions.append("bas_score >= 70")
    elif severity == "medium":
        conditions.append("bas_score >= 50 AND bas_score < 70")
    elif severity == "low":
        conditions.append("bas_score >= 30 AND bas_score < 50")

    where = "WHERE " + " AND ".join(conditions) if conditions else ""
    params.append(limit)
    result = await db.execute(
        f"""
            SELECT id, user_id, session_id, bas_score, anomaly_type,
                   details, action_taken, resolved, created_at
            FROM anomaly_events
            {where}
            ORDER BY created_at DESC LIMIT ?
        """,
        tuple(params),
    )
    rows = await result.fetchall()

    return [
        AnomalyEventModel(
            id=str(row[0]),
            user_id=str(row[1]),
            session_id=str(row[2]) if row[2] else None,
            bas_score=row[3],
            anomaly_type=row[4],
            details=json.loads(row[5]) if row[5] else {},
            action_taken=row[6],
            resolved=bool(row[7]),
            created_at=row[8],
        )
        for row in rows
    ]
