"""E2E: Backup and restore flow.

Tests creating entities, backing up, deleting, then restoring from backup.
"""

import io
import json



class TestBackupRestoreFlow:
    """A user backs up their configuration and restores it."""

    def _create_device(self, client, name="Backup Device") -> str:
        resp = client.post("/api/v1/devices", json={
            "name": name,
            "url": "mock://backup",
            "device_type": "mock",
            "led_count": 30,
        })
        assert resp.status_code == 201
        return resp.json()["id"]

    def _create_css(self, client, name="Backup CSS") -> str:
        resp = client.post("/api/v1/color-strip-sources", json={
            "name": name,
            "source_type": "static",
            "color": [255, 0, 0],
            "led_count": 30,
        })
        assert resp.status_code == 201
        return resp.json()["id"]

    def test_backup_and_restore_roundtrip(self, client):
        # 1. Create some entities
        device_id = self._create_device(client, "Device for Backup")
        css_id = self._create_css(client, "CSS for Backup")

        # Verify entities exist
        resp = client.get("/api/v1/devices")
        assert resp.json()["count"] == 1
        resp = client.get("/api/v1/color-strip-sources")
        assert resp.json()["count"] == 1

        # 2. Create a backup (GET returns a JSON file)
        resp = client.get("/api/v1/system/backup")
        assert resp.status_code == 200
        backup_data = resp.json()
        assert backup_data["meta"]["format"] == "ledgrab-backup"
        assert "stores" in backup_data
        assert "devices" in backup_data["stores"]
        assert "color_strip_sources" in backup_data["stores"]

        # Verify device is in the backup.
        # Store files have structure: {"version": "...", "devices": {id: {...}}}
        devices_store = backup_data["stores"]["devices"]
        assert "devices" in devices_store
        assert len(devices_store["devices"]) == 1

        # 3. Delete all created entities
        resp = client.delete(f"/api/v1/color-strip-sources/{css_id}")
        assert resp.status_code == 204
        resp = client.delete(f"/api/v1/devices/{device_id}")
        assert resp.status_code == 204

        # Verify they're gone
        resp = client.get("/api/v1/devices")
        assert resp.json()["count"] == 0
        resp = client.get("/api/v1/color-strip-sources")
        assert resp.json()["count"] == 0

        # 4. Restore from backup (POST with the backup JSON as a file upload)
        backup_bytes = json.dumps(backup_data).encode("utf-8")
        resp = client.post(
            "/api/v1/system/restore",
            files={"file": ("backup.json", io.BytesIO(backup_bytes), "application/json")},
        )
        assert resp.status_code == 200, f"Restore failed: {resp.text}"
        restore_result = resp.json()
        assert restore_result["status"] == "restored"
        assert restore_result["stores_written"] > 0

        # 5. After restore, stores are written to disk but the in-memory
        # stores haven't been re-loaded (normally a server restart does that).
        # Verify the backup file was written correctly by reading it back.
        # The restore endpoint writes JSON files; we check the response confirms success.
        assert restore_result["restart_scheduled"] is True

    def test_backup_contains_all_store_keys(self, client):
        """Backup response includes entries for all known store types."""
        resp = client.get("/api/v1/system/backup")
        assert resp.status_code == 200
        stores = resp.json()["stores"]
        # At minimum, these critical stores should be present
        expected_keys = {
            "devices", "output_targets", "color_strip_sources",
            "capture_templates", "value_sources",
        }
        assert expected_keys.issubset(set(stores.keys()))

    def test_restore_rejects_invalid_format(self, client):
        """Uploading a non-backup JSON file should fail validation."""
        bad_data = json.dumps({"not": "a backup"}).encode("utf-8")
        resp = client.post(
            "/api/v1/system/restore",
            files={"file": ("bad.json", io.BytesIO(bad_data), "application/json")},
        )
        assert resp.status_code == 400

    def test_restore_rejects_empty_stores(self, client):
        """A backup with no recognized stores should fail."""
        bad_backup = {
            "meta": {"format": "ledgrab-backup", "format_version": 1},
            "stores": {"unknown_store": {}},
        }
        resp = client.post(
            "/api/v1/system/restore",
            files={"file": ("bad.json", io.BytesIO(json.dumps(bad_backup).encode()), "application/json")},
        )
        assert resp.status_code == 400
