"""Service for creating and querying audit log entries."""
import asyncio
import csv
import io
import json
import uuid
from datetime import datetime
from typing import Optional

from sqlalchemy.ext.asyncio import AsyncSession
from sqlmodel import select

from models.sql.audit_log import AuditLogModel
from services.database import async_session_maker


def log(
    user_id: Optional[uuid.UUID],
    action: str,
    resource_type: str,
    resource_id: Optional[uuid.UUID] = None,
    client_id: Optional[uuid.UUID] = None,
    details: Optional[dict] = None,
    ip_address: Optional[str] = None,
) -> None:
    """Fire-and-forget audit log entry creation."""
    asyncio.create_task(
        _write_log(user_id, action, resource_type, resource_id, client_id, details, ip_address)
    )


async def _write_log(
    user_id: Optional[uuid.UUID],
    action: str,
    resource_type: str,
    resource_id: Optional[uuid.UUID],
    client_id: Optional[uuid.UUID],
    details: Optional[dict],
    ip_address: Optional[str],
) -> None:
    try:
        async with async_session_maker() as session:
            entry = AuditLogModel(
                user_id=user_id,
                action=action,
                resource_type=resource_type,
                resource_id=resource_id,
                client_id=client_id,
                details=details,
                ip_address=ip_address,
            )
            session.add(entry)
            await session.commit()
    except Exception as e:
        # Audit logging should never break the request
        print(f"Audit log error: {e}")


async def query(
    session: AsyncSession,
    user_id: Optional[uuid.UUID] = None,
    action: Optional[str] = None,
    resource_type: Optional[str] = None,
    client_id: Optional[uuid.UUID] = None,
    date_from: Optional[datetime] = None,
    date_to: Optional[datetime] = None,
    offset: int = 0,
    limit: int = 50,
) -> list:
    """Query audit logs with optional filters."""
    q = select(AuditLogModel)

    if user_id:
        q = q.where(AuditLogModel.user_id == user_id)
    if action:
        q = q.where(AuditLogModel.action == action)
    if resource_type:
        q = q.where(AuditLogModel.resource_type == resource_type)
    if client_id:
        q = q.where(AuditLogModel.client_id == client_id)
    if date_from:
        q = q.where(AuditLogModel.created_at >= date_from)
    if date_to:
        q = q.where(AuditLogModel.created_at <= date_to)

    q = q.order_by(AuditLogModel.created_at.desc()).offset(offset).limit(limit)
    result = await session.execute(q)
    return list(result.scalars().all())


def export_csv(logs: list) -> str:
    """Export audit logs as CSV string."""
    output = io.StringIO()
    writer = csv.writer(output)
    writer.writerow(["id", "user_id", "action", "resource_type", "resource_id", "client_id", "details", "ip_address", "created_at"])
    for log_entry in logs:
        writer.writerow([
            str(log_entry.id),
            str(log_entry.user_id) if log_entry.user_id else "",
            log_entry.action,
            log_entry.resource_type,
            str(log_entry.resource_id) if log_entry.resource_id else "",
            str(log_entry.client_id) if log_entry.client_id else "",
            json.dumps(log_entry.details) if log_entry.details else "",
            log_entry.ip_address or "",
            log_entry.created_at.isoformat() if log_entry.created_at else "",
        ])
    return output.getvalue()


def export_json(logs: list) -> str:
    """Export audit logs as JSON string."""
    entries = []
    for log_entry in logs:
        entries.append({
            "id": str(log_entry.id),
            "user_id": str(log_entry.user_id) if log_entry.user_id else None,
            "action": log_entry.action,
            "resource_type": log_entry.resource_type,
            "resource_id": str(log_entry.resource_id) if log_entry.resource_id else None,
            "client_id": str(log_entry.client_id) if log_entry.client_id else None,
            "details": log_entry.details,
            "ip_address": log_entry.ip_address,
            "created_at": log_entry.created_at.isoformat() if log_entry.created_at else None,
        })
    return json.dumps(entries, indent=2)
