import numpy as np
import pytest

from CCD_analysis.utils.dual_clock import DualClockCollector, field_steps_from_interval


class FakeFlowField:
    def __init__(self):
        self.absolute_step = 0
        self.ddf = np.zeros(9, dtype=np.float32)
        self.begin_calls = 0
        self.end_calls = 0
        self._completed = 0
        self._total = 0

    def begin_control_interval(self, total_steps, action_target):
        self.begin_calls += 1
        self._total = total_steps
        self._completed = 0

    def run_control_segment(self, steps):
        self.absolute_step += steps
        self._completed += steps

    def end_control_interval(self):
        assert self._completed == self._total
        self.end_calls += 1
        return np.array([self.absolute_step], dtype=np.float32)

    def get_ddf(self):
        self.ddf[:] = self.absolute_step


def test_field_steps_from_nondivisible_interval():
    assert field_steps_from_interval(16, 3) == (3, 6, 9, 12, 15)


def test_irregular_field_reads_do_not_add_control_boundaries(monkeypatch):
    def fake_velocity(ff, u0):
        ff.get_ddf()
        return ff.ddf[:3].copy(), ff.ddf[3:6].copy()

    monkeypatch.setattr("CCD_analysis.utils.dual_clock.get_velocity_field", fake_velocity)
    ff = FakeFlowField()
    collector = DualClockCollector(
        ff,
        control_interval=8,
        control_count=2,
        u0=0.01,
        field_steps=[3, 6, 9, 12, 15, 16],
    )
    collector.run_interval(0, np.zeros(1, dtype=np.float32))
    collector.run_interval(1, np.zeros(1, dtype=np.float32))
    assert collector.saved_steps == [3, 6, 9, 12, 15, 16]
    assert ff.begin_calls == 2
    assert ff.end_calls == 2
    assert ff.absolute_step == 16


def test_collector_rejects_invalid_field_timeline():
    with pytest.raises(ValueError):
        DualClockCollector(
            FakeFlowField(), control_interval=8, control_count=2,
            u0=0.01, field_steps=[3, 3],
        )
