"""Tests for heat FSM transitions."""

from __future__ import annotations

import unittest
from datetime import datetime, timezone

from control.config import ControlConfig, MaintainConfig, BackflushConfig
from control.heat_fsm import tick_heat
from control.state import ControlState, FsmContext


def _reading(tub: float, hot: float | None = 120.0) -> dict:
    return {
        "ts": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
        "setup_state": "ready",
        "tub_avg_f": tub,
        "hot_in_f": hot,
    }


class TestHeatFsm(unittest.TestCase):
    def setUp(self) -> None:
        self.cfg = ControlConfig(
            maintain=MaintainConfig(
                band_f=5.0,
                startup_delay_s=0.0,
                hot_in_rise_f=10.0,
                startup_attempts=3,
                retry_off_s=0.0,
            ),
            backflush=BackflushConfig(duration_s=0.0, revert_to_heat_mode="sleep"),
        )

    def test_maintain_startup_success_then_heating(self) -> None:
        state = ControlState(heat_mode="maintain", target_f=102.0)
        ctx = FsmContext()
        reading = _reading(90.0, hot=105.0)
        tick_heat(state, ctx, self.cfg, reading)
        self.assertEqual(state.heat_phase, "startup_1")
        self.assertEqual(state.heat_pump, "on")
        tick_heat(state, ctx, self.cfg, reading)
        self.assertEqual(state.heat_phase, "heating")

    def test_three_failures_error_startup(self) -> None:
        state = ControlState(heat_mode="maintain", target_f=102.0)
        ctx = FsmContext()
        reading = _reading(90.0, hot=90.0)
        for _ in range(24):
            tick_heat(state, ctx, self.cfg, reading)
            if state.heat_phase == "error_startup":
                break
        self.assertEqual(state.heat_phase, "error_startup")
        self.assertEqual(state.heat_pump, "off")
        self.assertIsNotNone(state.error)

    def test_backflush_reverts_to_sleep(self) -> None:
        state = ControlState(heat_mode="backflush")
        ctx = FsmContext()
        tick_heat(state, ctx, self.cfg, _reading(90.0))
        self.assertEqual(state.heat_phase, "backflush")
        tick_heat(state, ctx, self.cfg, _reading(90.0))
        self.assertEqual(state.heat_mode, "sleep")
        self.assertEqual(state.heat_pump, "off")


if __name__ == "__main__":
    unittest.main()
