from datetime import UTC, datetime, timedelta

from worker_watcher.alerts import build_alert_summary

NOW = datetime(2026, 8, 1, 20, 0, tzinfo=UTC)


def health(**overrides):
    result = {
        "watcher_status": "healthy", "jobs_total": 0, "jobs_healthy": 0,
        "collectors_failed": 0, "database_status": "ok",
        "last_cycle_finished_at": "2026-08-01T19:59:30Z",
        "last_successful_cycle_at": "2026-08-01T19:59:30Z",
    }
    result.update(overrides)
    return result


def row(instance_id, job_key, health_status="healthy", observation_status="fresh", severity="info", reason="source is healthy", **extra):
    result = {"instance_id": instance_id, "job_key": job_key, "name": job_key, "health_status": health_status,
              "observation_status": observation_status, "severity": severity, "status_reason": reason,
              "last_failure_at": None}
    result.update(extra)
    return result


def event(instance_id, health_status, observation_status, occurred_at):
    return {"instance_id": instance_id, "new_health_status": health_status,
            "new_observation_status": observation_status, "occurred_at": occurred_at}


def test_no_alerts_is_healthy() -> None:
    result = build_alert_summary(health(), [], [], NOW, 60)
    assert result["overall_status"] == "healthy"
    assert result["active_alerts"] == 0
    assert result["oldest_problem"] is None


def test_failed_and_unknown_unavailable_are_counted_once_and_oldest_is_selected() -> None:
    jobs = [
        row(1, "project.youtube.graphiti", "failed", severity="critical", reason="run failed with exit code 15"),
        row(2, "project.telefon.agent", "unknown", "unavailable", severity="warning", reason="collector could not read the configured source"),
    ]
    result = build_alert_summary(health(watcher_status="degraded", jobs_total=2), jobs,
                                 [event(1, "failed", "fresh", "2026-08-01T17:00:00Z"),
                                  event(2, "unknown", "unavailable", "2026-08-01T18:00:00Z")], NOW, 60)
    assert result["overall_status"] == "critical"
    assert result["active_alerts"] == 2
    assert result["failed"] == 1
    assert result["unknown"] == 1
    assert result["unavailable"] == 1
    assert result["oldest_problem"]["job_key"] == "project.youtube.graphiti"
    assert result["oldest_problem"]["age_seconds"] == 10800


def test_degraded_is_warning_and_paused_healthy_is_not_alert() -> None:
    jobs = [row(1, "project.demo.worker", "degraded", severity="warning"),
            row(2, "project.paused.worker", "healthy", reason="job is explicitly paused")]
    result = build_alert_summary(health(watcher_status="degraded", jobs_total=2), jobs, [], NOW, 60)
    assert result["overall_status"] == "warning"
    assert result["active_alerts"] == 1


def test_overdue_stale_job_is_one_alert() -> None:
    jobs = [row(1, "automation.overdue.worker", "stale", severity="warning", reason="expected run is overdue")]
    result = build_alert_summary(health(watcher_status="degraded", jobs_total=1), jobs, [], NOW, 60)
    assert result["stale"] == 1
    assert result["overdue"] == 1
    assert result["active_alerts"] == 1
    assert result["overall_status"] == "warning"


def test_stale_watcher_data_is_unavailable_and_unknown_problem_time_stays_null() -> None:
    jobs = [row(1, "project.unknown.worker", "unknown", severity="warning")]
    old = (NOW - timedelta(seconds=181)).isoformat().replace("+00:00", "Z")
    result = build_alert_summary(health(watcher_status="degraded", jobs_total=1, last_cycle_finished_at=old), jobs, [], NOW, 60)
    assert result["overall_status"] == "unavailable"
    assert result["data_is_current"] is False
    assert result["oldest_problem"]["problem_since"] is None
    assert result["oldest_problem"]["age_seconds"] is None
