"""
Reports API Routes

Provides endpoints for report generation and bulk export.
"""

import csv
import io
import json
import logging
from datetime import datetime, timezone
from typing import Any, Dict, List, Optional

from fastapi import APIRouter, Depends, HTTPException
from fastapi.responses import Response as RawResponse
from sqlmodel import select

from app.auth import get_current_user
from app.database import SessionDep
from app.models import ScanRecord, User
from app.services.dashboard_metrics import get_metrics_provider

logger = logging.getLogger(__name__)

router = APIRouter(tags=["reports"])


@router.get("/reports/summary")
async def get_report_summary(
    session: SessionDep,
    user: User = Depends(get_current_user),
) -> Dict[str, Any]:
    """
    Get aggregated report summary data — same metrics as dashboard
    plus a flat list of all scans for the export table.
    """
    try:
        provider = get_metrics_provider(session)
        metrics = await provider.get_metrics(user.id)

        # Latest scan per (target, scan_type) — no duplicates for report selection
        # Subquery gets the max timestamp per group, outer query fetches those rows.
        from sqlalchemy import func
        from sqlmodel import col

        subq = (
            select(
                ScanRecord.target,
                ScanRecord.scan_type,
                func.max(ScanRecord.timestamp).label("max_ts"),
            )
            .where(ScanRecord.user_id == user.id)
            .group_by(ScanRecord.target, ScanRecord.scan_type)
            .subquery()
        )

        scans = session.exec(
            select(
                ScanRecord.id,
                ScanRecord.target,
                ScanRecord.scan_type,
                ScanRecord.timestamp,
            )
            .where(ScanRecord.user_id == user.id)
            .join(
                subq,
                (ScanRecord.target == subq.c.target)
                & (ScanRecord.scan_type == subq.c.scan_type)
                & (ScanRecord.timestamp == subq.c.max_ts),
            )
            .order_by(ScanRecord.timestamp.desc())
        ).all()

        scan_list = [
            {
                "id": s.id,
                "target": s.target,
                "scan_type": s.scan_type.value if hasattr(s.scan_type, "value") else str(s.scan_type),
                "timestamp": s.timestamp.isoformat(),
            }
            for s in scans
        ]

        return {
            "success": True,
            "data": {
                "metrics": metrics,
                "scans": scan_list,
                "generated_at": datetime.now(timezone.utc).isoformat(),
            },
        }
    except Exception as e:
        logger.error(f"Failed to get report summary: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=f"Failed to get report summary: {str(e)}")


@router.get("/reports/export")
async def export_all_scans(
    format: str = "json",
    scan_type: Optional[str] = None,
    session: SessionDep = None,
    user: User = Depends(get_current_user),
):
    """
    Export all scan records for the current user.

    Query Parameters:
        format: 'json' or 'csv'
        scan_type: optional filter (code, quantum, port, binary)
    """
    stmt = (
        select(ScanRecord)
        .where(ScanRecord.user_id == user.id)
        .order_by(ScanRecord.timestamp.desc())
        .limit(500)
    )
    if scan_type:
        stmt = stmt.where(ScanRecord.scan_type == scan_type)

    scans = session.exec(stmt).all()

    timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")

    if format == "json":
        rows = []
        for s in scans:
            sd = s.scan_data or {}
            rows.append({
                "id": s.id,
                "target": s.target,
                "scan_type": s.scan_type.value if hasattr(s.scan_type, "value") else str(s.scan_type),
                "timestamp": s.timestamp.isoformat(),
                "findings_count": _extract_findings_count(sd, s.scan_type),
                "risk_score": _extract_risk_score(sd),
            })
        content = json.dumps({"exported_at": timestamp, "total": len(rows), "scans": rows}, indent=2)
        return RawResponse(
            content=content.encode(),
            media_type="application/json",
            headers={"Content-Disposition": f'attachment; filename="rivet_export_{timestamp}.json"'},
        )

    elif format == "csv":
        output = io.StringIO()
        writer = csv.writer(output)
        writer.writerow(["id", "target", "scan_type", "timestamp", "findings_count", "risk_score"])
        for s in scans:
            sd = s.scan_data or {}
            scan_type_str = s.scan_type.value if hasattr(s.scan_type, "value") else str(s.scan_type)
            writer.writerow([
                s.id,
                s.target,
                scan_type_str,
                s.timestamp.isoformat(),
                _extract_findings_count(sd, s.scan_type),
                _extract_risk_score(sd) or "",
            ])
        content = output.getvalue()
        return RawResponse(
            content=content.encode(),
            media_type="text/csv",
            headers={"Content-Disposition": f'attachment; filename="rivet_export_{timestamp}.csv"'},
        )

    else:
        raise HTTPException(status_code=400, detail=f"Unsupported format: {format}. Use 'json' or 'csv'.")


# --- helpers ---

def _extract_findings_count(scan_data: dict, scan_type) -> int:
    if not isinstance(scan_data, dict):
        return 0
    if "findings_count" in scan_data:
        return scan_data["findings_count"]
    if "enriched_findings" in scan_data:
        return len(scan_data["enriched_findings"])
    data = scan_data.get("data", {})
    if isinstance(data, dict):
        yara = data.get("yara_analysis", {})
        if isinstance(yara, dict) and yara.get("match_details"):
            return len(yara["match_details"])
        scan_type_str = scan_type.value if hasattr(scan_type, "value") else str(scan_type)
        if scan_type_str == "quantum":
            cert = data.get("certificate_analysis", {})
            if isinstance(cert, dict) and cert.get("shor_vulnerable"):
                return 1
        if scan_type_str == "port":
            net = data.get("data", {})
            if isinstance(net, dict):
                return len(net.get("openPorts", []))
    return 0


def _extract_risk_score(scan_data: dict) -> Optional[float]:
    if not isinstance(scan_data, dict):
        return None
    data = scan_data.get("data", {})
    if not isinstance(data, dict):
        return None
    # Code analysis: top-level quantum_risk_score
    if "quantum_risk_score" in data:
        return data["quantum_risk_score"]
    # Certificate/quantum scan: nested under certificate_analysis
    cert = data.get("certificate_analysis", {})
    if isinstance(cert, dict) and "quantum_risk_score" in cert:
        return cert["quantum_risk_score"]
    # Legacy path
    ra = data.get("quantum_risk_assessment", {})
    if isinstance(ra, dict):
        return ra.get("quantum_risk_score")
    return None
