from typing import Any

from fastapi import APIRouter, Depends, HTTPException

from ..auth import require_api_key
from ..models.backups import (
    BackupAlertResponse,
    BackupReportRequest,
    BackupRunResponse,
)
from ..observability import record_backup_run
from ..services.backup_alert_engine import generate_alerts_for_run
from ..services.settings_store import SettingsStore, get_settings_store

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


def _resolve_backup_service_id(store: SettingsStore, explicit: str | None = None) -> str:
    """Return the service_id for backup attribution.

    First-wins: if no explicit service_id is given, pick the first enabled
    ``backups`` service instance (spec R6.1). Returns an empty string when
    none is configured (backward-compatible with pre-service reports).
    """
    if explicit:
        return explicit
    candidates = store.list_services("backups")
    for svc in candidates:
        if svc.get("enabled"):
            return svc["id"]
    return ""


def _get_or_create_job(store: SettingsStore, report: BackupReportRequest, service_id: str = "") -> dict[str, Any]:
    job = store.get_backup_job_by_name(report.name)
    if not job:
        job = store.upsert_backup_job(
            {
                "name": report.name,
                "source": report.source,
                "target": report.target,
                "schedule_interval_seconds": report.schedule_interval_seconds,
                "service_id": service_id,
            }
        )
    elif report.schedule_interval_seconds:
        store.upsert_backup_job(
            {
                "id": job["id"],
                "name": report.name,
                "source": report.source,
                "target": report.target,
                "schedule_interval_seconds": report.schedule_interval_seconds,
                "service_id": service_id,
            }
        )
        job = store.get_backup_job(job["id"])
    return job


@router.post("/report")
def post_backup_report(
    report: BackupReportRequest,
    service_id: str | None = None,
    store: SettingsStore = Depends(get_settings_store),
    _auth: str = Depends(require_api_key),
) -> BackupRunResponse:
    resolved_service_id = _resolve_backup_service_id(store, service_id)
    job = _get_or_create_job(store, report, resolved_service_id)

    # Check for duplicate (same job + started_at within 1s)
    existing_runs = store.list_backup_runs(job_id=job["id"], limit=5)
    started_at_ts = int(report.started_at.timestamp())
    for existing in existing_runs:
        if abs(existing["started_at"] - started_at_ts) <= 1:
            return BackupRunResponse(**existing)

    run_data = {
        "job_id": job["id"],
        "started_at": started_at_ts,
        "ended_at": int(report.ended_at.timestamp()) if report.ended_at else None,
        "status": report.status,
        "bytes_transferred": report.bytes_transferred,
        "duration_ms": report.duration_ms,
        "error_message": report.error_message,
        "details": report.details,
    }
    run = store.create_backup_run(run_data)
    record_backup_run(job_name=report.name, status=report.status, success=report.status == "success")

    # Generate alerts
    previous_runs = store.list_backup_runs(job_id=job["id"], status="success", limit=20)
    alerts = generate_alerts_for_run(run, previous_runs, job)
    for alert in alerts:
        store.create_backup_alert(alert)

    # Resolve old alerts of the same type if this run is successful
    if report.status == "success":
        store.resolve_backup_alerts_for_job(job["id"], "failed_status")
        store.resolve_backup_alerts_for_job(job["id"], "anomaly_size")
        store.resolve_backup_alerts_for_job(job["id"], "anomaly_duration")

    # Map details -> details_json for response model
    run["details_json"] = run.pop("details", None)
    return BackupRunResponse(**run)


@router.post("/report/start")
def post_backup_start(
    report: BackupReportRequest,
    service_id: str | None = None,
    store: SettingsStore = Depends(get_settings_store),
    _auth: str = Depends(require_api_key),
) -> BackupRunResponse:
    resolved_service_id = _resolve_backup_service_id(store, service_id)
    job = _get_or_create_job(store, report, resolved_service_id)

    run_data = {
        "job_id": job["id"],
        "started_at": int(report.started_at.timestamp()),
        "status": "in_progress",
    }
    run = store.create_backup_run(run_data)
    record_backup_run(job_name=report.name, status="in_progress")
    # Map details -> details_json for response model
    run["details_json"] = run.pop("details", None)
    return BackupRunResponse(**run)


@router.get("/jobs")
def get_backup_jobs(
    store: SettingsStore = Depends(get_settings_store),
) -> list[dict[str, Any]]:
    jobs = store.list_backup_jobs()
    return jobs


@router.get("/jobs/{job_id}")
def get_backup_job(
    job_id: str,
    store: SettingsStore = Depends(get_settings_store),
) -> dict[str, Any]:
    job = store.get_backup_job(job_id)
    if not job:
        raise HTTPException(status_code=404, detail="Backup job not found")
    runs = store.list_backup_runs(job_id=job_id, limit=20)
    return {
        "job": job,
        "runs": runs,
    }


@router.get("/runs")
def get_backup_runs(
    job_id: str | None = None,
    status: str | None = None,
    limit: int = 50,
    store: SettingsStore = Depends(get_settings_store),
) -> list[BackupRunResponse]:
    runs = store.list_backup_runs(job_id=job_id, status=status, limit=limit)
    return [BackupRunResponse(**run) for run in runs]


@router.get("/runs/{run_id}")
def get_backup_run(
    run_id: str,
    store: SettingsStore = Depends(get_settings_store),
) -> BackupRunResponse:
    run = store.get_backup_run(run_id)
    if not run:
        raise HTTPException(status_code=404, detail="Backup run not found")
    return BackupRunResponse(**run)


@router.get("/alerts")
def get_backup_alerts(
    job_id: str | None = None,
    acknowledged: bool | None = None,
    severity: str | None = None,
    store: SettingsStore = Depends(get_settings_store),
) -> list[BackupAlertResponse]:
    alerts = store.list_backup_alerts(job_id=job_id, acknowledged=acknowledged, severity=severity)
    return [BackupAlertResponse(**alert) for alert in alerts]


@router.post("/alerts/{alert_id}/acknowledge")
def acknowledge_backup_alert(
    alert_id: str,
    store: SettingsStore = Depends(get_settings_store),
) -> BackupAlertResponse:
    alert = store.acknowledge_backup_alert(alert_id)
    if not alert:
        raise HTTPException(status_code=404, detail="Alert not found")
    return BackupAlertResponse(**alert)
