"""Campaign logs and audit log endpoints: send attempts, events, errors with filters,
CSV export, per-recipient history. Combined logs view for the Logs page."""
import csv
import io
from datetime import datetime, timezone

from fastapi import APIRouter, Depends, HTTPException, Query
from fastapi.responses import StreamingResponse
from sqlalchemy import func, or_, select
from sqlalchemy.ext.asyncio import AsyncSession

from app.api.deps import get_current_user, get_db
from app.models import AuditLog, Campaign, CampaignRecipient, EmailEvent, SendAttempt, User

router = APIRouter(prefix="/logs", tags=["logs"])


def _ser_attempt(a: SendAttempt) -> dict:
    return {
        "id": a.id,
        "campaign_id": a.campaign_id,
        "campaign_recipient_id": a.campaign_recipient_id,
        "account_id": a.gmail_account_id,
        "attempt_number": a.attempt_number,
        "outcome": a.outcome,
        "error_class": a.error_class,
        "error_message": (a.error_message or "")[:300],
        "message_id": a.message_id,
        "provider_response": (a.provider_response or "")[:300],
        "created_at": a.created_at.isoformat() if a.created_at else None,
    }


@router.get("/list")
@router.get("/events")
async def list_events(campaign_id: int = Query(0),
                      event_type: str = Query(""),
                      page: int = Query(1, ge=1),
                      page_size: int = Query(50, ge=1, le=200),
                      db: AsyncSession = Depends(get_db),
                      user: User = Depends(get_current_user)):
    q = (select(EmailEvent, CampaignRecipient)
         .join(CampaignRecipient, EmailEvent.campaign_recipient_id == CampaignRecipient.id)
         .join(Campaign, Campaign.id == EmailEvent.campaign_id)
         .where(Campaign.user_id == user.id))
    if campaign_id:
        q = q.where(EmailEvent.campaign_id == campaign_id)
    if event_type:
        q = q.where(EmailEvent.event_type == event_type)
    total = (await db.execute(
        select(func.count()).select_from(q.subquery()))).scalar() or 0
    rows = (await db.execute(
        q.order_by(EmailEvent.id.desc()).offset((page - 1) * page_size).limit(page_size)
    )).all()
    out = []
    for e, cr in rows:
        out.append({
            "id": e.id,
            "campaign_id": e.campaign_id,
            "email": cr.email,
            "event_type": e.event_type,
            "detail": (e.detail or "")[:300],
            "message_id": e.message_id,
            "created_at": e.created_at.isoformat() if e.created_at else None,
        })
    return {"events": out, "total": total, "page": page, "page_size": page_size}


@router.get("/audit")
async def list_audit(action: str = Query(""),
                     page: int = Query(1, ge=1),
                     page_size: int = Query(50, ge=1, le=200),
                     db: AsyncSession = Depends(get_db),
                     user: User = Depends(get_current_user)):
    """Audit log (own actions only). Secrets are redacted at write time."""
    q = select(AuditLog).where(AuditLog.user_id == user.id)
    if action:
        q = q.where(AuditLog.action == action)
    total = (await db.execute(
        select(func.count()).select_from(q.subquery()))).scalar() or 0
    rows = (await db.execute(
        q.order_by(AuditLog.id.desc()).offset((page - 1) * page_size).limit(page_size)
    )).scalars().all()
    out = []
    for r in rows:
        out.append({
            "id": r.id,
            "action": r.action,
            "entity": r.entity,
            "entity_id": r.entity_id,
            "detail": r.detail if isinstance(r.detail, dict) else {},
            "ip_address": r.ip_address,
            "created_at": r.created_at.isoformat() if r.created_at else None,
        })
    return {"audit": out, "total": total, "page": page, "page_size": page_size}


@router.get("/dlq")
async def list_dlq(page: int = Query(1, ge=1), page_size: int = Query(50, ge=1, le=200),
                   user: User = Depends(get_current_user)):
    """Dead-letter queue (Redis list dlq:send): jobs that exhausted retries."""
    import redis as redis_lib
    from app.core.config import settings
    r = redis_lib.Redis.from_url(settings.REDIS_URL, decode_responses=True)
    try:
        items = r.lrange("dlq:send", 0, -1)
    except Exception as e:  # Redis down — don't 500 the whole logs page
        return {"dlq": [], "total": 0, "error": f"Redis unavailable: {e}"}
    import json as _json
    out = []
    for raw in items:
        try:
            out.append(_json.loads(raw))
        except Exception:
            out.append({"raw": raw[:200]})
    total = len(out)
    start = (page - 1) * page_size
    return {"dlq": out[start:start + page_size], "total": total,
            "page": page, "page_size": page_size}


@router.get("/export")
async def export_logs(campaign_id: int = Query(0),
                      outcome: str = Query(""),
                      db: AsyncSession = Depends(get_db),
                      user: User = Depends(get_current_user)):
    """CSV export of send attempts for a campaign (all pages)."""
    q = (select(SendAttempt, CampaignRecipient)
         .join(CampaignRecipient, SendAttempt.campaign_recipient_id == CampaignRecipient.id)
         .join(Campaign, Campaign.id == SendAttempt.campaign_id)
         .where(Campaign.user_id == user.id))
    if campaign_id:
        q = q.where(SendAttempt.campaign_id == campaign_id)
    if outcome:
        q = q.where(SendAttempt.outcome == outcome)
    rows = (await db.execute(q.order_by(SendAttempt.id.asc()))).all()

    def generate():
        buf = io.StringIO()
        w = csv.writer(buf)
        w.writerow(["attempt_id", "campaign_id", "email", "attempt_number", "outcome",
                    "error_class", "error_message", "message_id", "provider_response", "timestamp"])
        yield buf.getvalue()
        buf.seek(0); buf.truncate(0)
        for a, cr in rows:
            w.writerow([a.id, a.campaign_id, cr.email, a.attempt_number, a.outcome,
                        a.error_class, (a.error_message or "")[:500], a.message_id,
                        (a.provider_response or "")[:500],
                        a.created_at.isoformat() if a.created_at else ""])
            yield buf.getvalue()
            buf.seek(0); buf.truncate(0)

    return StreamingResponse(generate(), media_type="text/csv",
                             headers={"Content-Disposition":
                                      f"attachment; filename=send_logs{('_' + str(campaign_id)) if campaign_id else 's'}.csv"})


@router.get("/recipient/{cr_id}/history")
async def recipient_history(cr_id: int, db: AsyncSession = Depends(get_db),
                            user: User = Depends(get_current_user)):
    """Full attempt history for one campaign recipient (modal in Logs page)."""
    cr = await db.get(CampaignRecipient, cr_id)
    if not cr:
        raise HTTPException(status_code=404, detail="Recipient not found")
    camp = await db.get(Campaign, cr.campaign_id)
    if not camp or camp.user_id != user.id:
