"""Phase-1 bullet 3 (#91277): the dashboard/Desktop READ the update receipt.

The update receipt (written by every `hermes update` run since #91283,
`latest.json` pointer) is the durable outcome record. These tests pin:

- GET /api/hermes/update/receipt returns the full receipt + summary; 404
  when none exists.
- GET /api/actions/hermes-update/status attaches the receipt summary, and
  uses a finished receipt as the outcome when both in-memory registries AND
  the log marker are gone (dashboard restarted + log rotated).
"""

import json
from pathlib import Path

import pytest

import hermes_cli.web_server as web_server
import hermes_cli.web_server_gateway as _web_server_gateway


@pytest.fixture()
def client():
    try:
        from starlette.testclient import TestClient
    except ImportError:
        pytest.skip("fastapi/starlette not installed")
    from hermes_cli.web_server import _SESSION_HEADER_NAME, _SESSION_TOKEN, app

    c = TestClient(app)
    c.headers[_SESSION_HEADER_NAME] = _SESSION_TOKEN
    return c


def _write_receipt(tmp_path: Path, monkeypatch, *, outcome="success") -> dict:
    receipt = {
        "schema": 1,
        "started_at": "2026-08-23T07:00:00+00:00",
        "finished_at": "2026-08-23T07:03:20+00:00",
        "argv": ["hermes", "update"],
        "pid": 12345,
        "outcome": outcome,
        "pre_update": {"sha": "a" * 40, "version": "0.20.4"},
        "post_update": {"sha": "b" * 40, "version": "0.20.5"},
        "steps": [{"name": "pre_update_backup", "ok": True, "detail": "", "at": ""}],
        "skips": [],
        "gateway_restart": {},
        "fleet": [
            {"profile": "default", "pid": 1, "code_sha": "b" * 40, "state": "current"}
        ],
    }
    receipt_dir = tmp_path / "logs" / "update_receipts"
    receipt_dir.mkdir(parents=True)
    (receipt_dir / "latest.json").write_text(json.dumps(receipt), encoding="utf-8")
    import hermes_cli.update_receipt as ur

    monkeypatch.setattr(ur, "_receipt_dir", lambda: receipt_dir)
    return receipt


class TestUpdateReceiptEndpoint:

    def test_receipt_endpoint_returns_full_receipt_and_summary(self, client, tmp_path, monkeypatch):
        receipt = _write_receipt(tmp_path, monkeypatch)

        resp = client.get("/api/hermes/update/receipt")

        assert resp.status_code == 200
        data = resp.json()
        assert data["receipt"]["outcome"] == "success"
        assert data["receipt"]["steps"] == receipt["steps"]
        summary = data["summary"]
        assert summary["outcome"] == "success"
        assert summary["pre_sha"] == "a" * 40
        assert summary["post_sha"] == "b" * 40
        assert summary["post_version"] == "0.20.5"
        assert summary["fleet_states"] == ["current"]

    def test_receipt_endpoint_404_when_no_receipt(self, client, tmp_path, monkeypatch):
        import hermes_cli.update_receipt as ur

        monkeypatch.setattr(ur, "_receipt_dir", lambda: tmp_path / "none")

        resp = client.get("/api/hermes/update/receipt")

        assert resp.status_code == 404


class TestUpdateStatusReadsReceipt:

    def _clear_registries(self, monkeypatch, tmp_path):
        monkeypatch.setattr(_web_server_gateway, "_ACTION_LOG_DIR", tmp_path / "actions")
        (tmp_path / "actions").mkdir(exist_ok=True)
        monkeypatch.setattr(_web_server_gateway, "_ACTION_PROCS", {})
        monkeypatch.setattr(_web_server_gateway, "_ACTION_RESULTS", {})
        monkeypatch.setattr(_web_server_gateway, "_ACTION_COMMANDS", {})
        monkeypatch.setattr(_web_server_gateway, "_ACTION_IDS", {})

    def test_status_attaches_receipt_summary(self, client, tmp_path, monkeypatch):
        _write_receipt(tmp_path, monkeypatch)
        self._clear_registries(monkeypatch, tmp_path)

        resp = client.get("/api/actions/hermes-update/status")

        assert resp.status_code == 200
        data = resp.json()
        assert data["receipt"]["outcome"] == "success"

    def test_finished_receipt_is_the_outcome_when_marker_and_memory_gone(self, client, tmp_path, monkeypatch):
        """Dashboard restarted (registries lost) AND update.log has no
        completion marker (rotated): the receipt alone reports success."""
        _write_receipt(tmp_path, monkeypatch, outcome="success")
        self._clear_registries(monkeypatch, tmp_path)

        resp = client.get("/api/actions/hermes-update/status")

        data = resp.json()
        assert data["running"] is False
        assert data["exit_code"] == 0

    def test_partial_receipt_maps_to_exit_1(self, client, tmp_path, monkeypatch):
        _write_receipt(tmp_path, monkeypatch, outcome="partial")
        self._clear_registries(monkeypatch, tmp_path)

        resp = client.get("/api/actions/hermes-update/status")

        assert resp.json()["exit_code"] == 1

    def test_running_receipt_proves_nothing(self, client, tmp_path, monkeypatch):
        """An unfinished receipt (crashed run / mid-update) must not report
        an outcome — clients keep polling."""
        receipt = _write_receipt(tmp_path, monkeypatch, outcome="running")
        self._clear_registries(monkeypatch, tmp_path)

        resp = client.get("/api/actions/hermes-update/status")

        assert resp.json()["exit_code"] is None

    def test_non_update_actions_untouched(self, client, tmp_path, monkeypatch):
        _write_receipt(tmp_path, monkeypatch)
        self._clear_registries(monkeypatch, tmp_path)

        resp = client.get("/api/actions/gateway-restart/status")

        data = resp.json()
        assert "receipt" not in data
