"""Audit log — write, query, and export customer audit events.

Firestore path: ``customers/{customer_id}/audit_log/{auto_id}``.
TTL via ``expires_at`` aligned to tier ``retention_months``.
"""

from __future__ import annotations

import csv
import io
import logging
from datetime import UTC, datetime
from typing import Any

from backend.dashboard.context import AuditLogEvent, AuditLogPage

_log = logging.getLogger(__name__)

AUDIT_EVENT_TYPES: list[str] = [
    "dashboard.login",
    "dashboard.scan_pushed",
    "dashboard.report_exported",
    "dashboard.report_viewed",
    "dashboard.settings_updated",
    "dashboard.drift_config_changed",
    "dashboard.alert_webhook_updated",
    "dashboard.member_invited",
    "dashboard.member_removed",
    "dashboard.member_role_changed",
    "dashboard.api_key_regenerated",
    "dashboard.data_exported",
    "dashboard.project_archived",
    "dashboard.share_link_created",
    "dashboard.share_link_revoked",
    "scim.user_provisioned",
    "scim.user_deprovisioned",
    "scim.user_role_changed",
]

_CSV_HEADERS = [
    "timestamp",
    "event",
    "actor_email",
    "actor_role",
    "ip_hash",
    "metadata",
]


def _compute_expires_at(retention_months: int | None) -> datetime | None:
    """Return expiry timestamp or None for unlimited retention."""
    if retention_months is None:
        return None
    now = datetime.now(UTC)
    # Approximate months as 30 days each
    from datetime import timedelta

    return now + timedelta(days=retention_months * 30)


async def write_audit_event(
    customer_id: str,
    event: str,
    actor_email: str,
    actor_role: str,
    ip_hash: str,
    metadata: dict[str, str | int | bool | None] | None = None,
    retention_months: int | None = None,
) -> str:
    """Write to customers/{customer_id}/audit_log/{auto_id}.

    Sets expires_at based on tier retention_months.
    Returns the auto-generated document ID.
    """
    from backend.shared.firestore import get_db

    now = datetime.now(UTC)
    doc_data: dict[str, Any] = {
        "event": event,
        "actor_email": actor_email,
        "actor_role": actor_role,
        "timestamp": now.isoformat(),
        "ip_hash": ip_hash,
        "metadata": metadata or {},
    }

    expires_at = _compute_expires_at(retention_months)
    if expires_at is not None:
        doc_data["expires_at"] = expires_at

    db = get_db()
    doc_ref = await (
        db.collection("customers")
        .document(customer_id)
        .collection("audit_log")
        .add(doc_data)
    )
    # Firestore add returns (timestamp, doc_ref) tuple
    doc_id: str = doc_ref[1].id
    return doc_id


async def query_audit_log(
    customer_id: str,
    event_filter: str | None = None,
    actor_filter: str | None = None,
    start_date: str | None = None,
    end_date: str | None = None,
    page_size: int = 50,
    cursor: str | None = None,
) -> AuditLogPage:
    """Firestore cursor-based pagination.

    Returns AuditLogPage with events + next_cursor.
    """
    from backend.shared.firestore import get_db

    db = get_db()
    query: Any = (
        db.collection("customers")
        .document(customer_id)
        .collection("audit_log")
        .order_by("timestamp", direction="DESCENDING")
    )

    if event_filter:
        query = query.where("event", "==", event_filter)
    if actor_filter:
        query = query.where("actor_email", "==", actor_filter)
    if start_date:
        query = query.where("timestamp", ">=", start_date)
    if end_date:
        query = query.where("timestamp", "<=", end_date)

    if cursor:
        # Cursor is a document ID — fetch the doc to use as start_after
        cursor_doc = await (
            db.collection("customers")
            .document(customer_id)
            .collection("audit_log")
            .document(cursor)
            .get()
        )
        if cursor_doc.exists:
            query = query.start_after(cursor_doc)

    # Fetch one extra to detect next page
    query = query.limit(page_size + 1)
    docs = [doc async for doc in query.stream()]

    next_cursor: str | None = None
    if len(docs) > page_size:
        docs = docs[:page_size]
        next_cursor = docs[-1].id

    events: list[AuditLogEvent] = []
    for doc in docs:
        data: dict[str, Any] = doc.to_dict()  # type: ignore[assignment]
        events.append(
            AuditLogEvent(
                event_id=doc.id,
                event=data.get("event", ""),
                actor_email=data.get("actor_email", ""),
                actor_role=data.get("actor_role", ""),
                timestamp=data.get("timestamp", ""),
                ip_hash=data.get("ip_hash", ""),
                metadata=data.get("metadata", {}),
            )
        )

    return AuditLogPage(events=events, next_cursor=next_cursor, total_hint=None)


async def export_audit_csv(
    customer_id: str,
    event_filter: str | None = None,
    actor_filter: str | None = None,
    start_date: str | None = None,
    end_date: str | None = None,
    max_rows: int = 10000,
) -> str:
    """Generate CSV string from audit log. Max 10,000 rows."""
    from backend.shared.firestore import get_db

    db = get_db()
    query: Any = (
        db.collection("customers")
        .document(customer_id)
        .collection("audit_log")
        .order_by("timestamp", direction="DESCENDING")
    )

    if event_filter:
        query = query.where("event", "==", event_filter)
    if actor_filter:
        query = query.where("actor_email", "==", actor_filter)
    if start_date:
        query = query.where("timestamp", ">=", start_date)
    if end_date:
        query = query.where("timestamp", "<=", end_date)

    query = query.limit(max_rows)
    docs = [doc async for doc in query.stream()]

    output = io.StringIO()
    writer = csv.writer(output)
    writer.writerow(_CSV_HEADERS)

    for doc in docs:
        data: dict[str, Any] = doc.to_dict()  # type: ignore[assignment]
        writer.writerow(
            [
                data.get("timestamp", ""),
                data.get("event", ""),
                data.get("actor_email", ""),
                data.get("actor_role", ""),
                data.get("ip_hash", ""),
                str(data.get("metadata", {})),
            ]
        )

    return output.getvalue()
