"""Sealed-controller adapter ordering for the r71b file transaction."""

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path
from typing import final

from scripts.nutricoach_v150_r71b_cron_watch import CronJobsEvent
from scripts.nutricoach_v150_r71b_maintenance_confirmation import (
    ArmedCronWatcher,
    MaintenanceConfirmation,
)
from scripts.nutricoach_v150_r71b_maintenance_transaction import (
    KNOWN_R70_CRON_ERROR,
    KNOWN_R70_CRON_ERROR_EVIDENCE_SHA256,
    KNOWN_R70_CRON_JOB_ID,
    KNOWN_R70_CRON_JOB_NAME,
    KNOWN_R70_CRON_SCHEDULE,
    KNOWN_R70_RUNTIME_CANDIDATE_DIGEST,
    R71bMaintenanceController,
    R71bMaintenanceProof,
)
from scripts.nutricoach_v150_r71b_sealed_host import R71bSealedMaintenanceHost
from scripts.nutricoach_v150_phase_journal import PhaseJournal


@final
@dataclass
class FakeFiles:
    calls: list[str]

    def arm(self) -> None:
        self.calls.append("hold")

    def confirm(
        self,
        watcher: ArmedCronWatcher,
        evidence: MaintenanceConfirmation,
    ) -> None:
        del watcher
        assert evidence.provider_calls == 0
        assert evidence.network_calls == 0
        self.calls.append("confirm")

    def rollback(self) -> None:
        self.calls.append("pause")

    def recover_confirmed(self, evidence: MaintenanceConfirmation) -> None:
        assert evidence.provider_calls == 0
        assert evidence.network_calls == 0
        self.calls.append("recover-confirm")

    def finalize(self) -> None:
        self.calls.append("retain-credential")


@final
@dataclass
class FakeWatcher:
    calls: list[str]

    def arm(self) -> None:
        self.calls.append("watch")

    def close(self) -> None:
        self.calls.append("watch-close")

    def wait(self, *, timeout_seconds: int) -> CronJobsEvent:
        assert timeout_seconds == 120
        return CronJobsEvent("jobs.json", True)


@final
@dataclass
class FakeHost:
    calls: list[str]
    provider_events: list[str]
    network_events: list[str]

    def start(self) -> None:
        self.calls.append("start")


def test_adapter_persists_maintenance_phases_and_closes_watch_after_commit(
    tmp_path: Path,
) -> None:
    # Given
    calls: list[str] = []
    adapter = R71bSealedMaintenanceHost(
        FakeHost(calls, [], []),
        FakeFiles(calls),
        FakeWatcher(calls),
        MaintenanceConfirmation(tmp_path / "ledger", "a" * 64, tmp_path / "out", 99, 99),
        PhaseJournal(tmp_path / "phase.json"),
    )
    controller = R71bMaintenanceController(
        adapter,
        R71bMaintenanceProof(
            candidate_digest="b" * 64,
            cron_job_id=KNOWN_R70_CRON_JOB_ID,
            cron_job_name=KNOWN_R70_CRON_JOB_NAME,
            cron_schedule=KNOWN_R70_CRON_SCHEDULE,
            cron_error=KNOWN_R70_CRON_ERROR,
            cron_error_evidence_sha256=KNOWN_R70_CRON_ERROR_EVIDENCE_SHA256,
            cron_ledger_sha256="c" * 64,
            expected_cron_ledger_sha256="c" * 64,
            runtime_candidate_digest=KNOWN_R70_RUNTIME_CANDIDATE_DIGEST,
            expected_candidate_digest="b" * 64,
            final_package_digest="d" * 64,
        ),
    )

    # When
    controller.execute("AUTHORIZE NUTRICOACH V1.5 R71B MAINTENANCE UPGRADE " + "d" * 64)

    # Then
    assert calls == [
        "hold", "watch", "start", "confirm", "retain-credential", "watch-close",
    ]
    assert adapter.phase() == "MAINTENANCE_CONFIRMED"
