import pytest
import json
from flowtimer.Schedule import Schedule
from flowtimer.Phase import Phase
from flowtimer.RecurringPhaseSequence import RecurringPhaseSequence


class TestSchedule:

    @pytest.fixture
    def setup_schedule(self):
        #  Set up some real Phase and RecurringPhaseSequence objects for testing
        phase1 = Phase("Phase 1", 300)
        phase2 = Phase("Phase 2", 600)
        sequence = RecurringPhaseSequence("Sequence 1", [phase1, phase2], 2)
        blocks = [phase1, sequence]
        schedule = Schedule("Test Schedule", blocks)
        return schedule, phase1, phase2, sequence

    def test_initialization(self, setup_schedule):
        schedule, phase1, _, _ = setup_schedule
        assert schedule.title == "Test Schedule"
        assert schedule.blocks == [phase1, schedule.blocks[1]]
        assert schedule.current_block == phase1
        assert schedule.state() == "initial"

    def test_default_json_string(self):
        default_json = Schedule.default_json_string()
        assert isinstance(default_json, str)

    def test_from_json(self):
        json_string = Schedule.default_json_string()
        schedule = Schedule.from_json(json_string)
        assert schedule.title == "Default"
        assert len(schedule.blocks) == 2
        assert isinstance(schedule.blocks[0], Phase)
        assert isinstance(schedule.blocks[1], RecurringPhaseSequence)

    def test_to_json(self, setup_schedule):
        schedule, _, _, _ = setup_schedule
        json_string = schedule.to_json()
        assert isinstance(json_string, str)
        data = json.loads(json_string)
        assert data['title'] == "Test Schedule"
        assert len(data['blocks']) == 2

    def test_start(self, setup_schedule):
        schedule, phase1, _, _ = setup_schedule
        schedule.start()
        assert schedule.state() == "running"
        # assert phase1.ticks_left < phase1.initial_ticks  # Assuming the phase reduces ticks when started

    def test_running(self, setup_schedule):
        schedule, _, _, _ = setup_schedule
        schedule.start()
        assert schedule.is_running() is True
        schedule.pause()
        assert schedule.is_running() is False

    def test_paused(self, setup_schedule):
        schedule, _, _, _ = setup_schedule
        schedule.pause()
        assert schedule.is_paused() is True

    def test_abort(self, setup_schedule):
        schedule, _, _, _ = setup_schedule
        schedule.abort()
        assert schedule.state() == "aborted"

    def test_completed(self, setup_schedule):
        schedule, phase1, _, sequence = setup_schedule
        schedule.tick(phase1.initial_ticks)  # Complete phase1
        schedule.tick(sequence.initial_ticks)  # Complete sequence
        schedule.tick(phase1.initial_ticks)  # Complete phase1
        schedule.tick(sequence.initial_ticks)  # Complete sequence
        assert schedule.is_completed() is True
        assert schedule.state() == "completed"

    def test_advance_to_next_block(self, setup_schedule):
        schedule, _, _, sequence = setup_schedule
        schedule.advance_to_next_block()
        assert schedule.current_block == schedule.blocks[1]

    def test_skip(self, setup_schedule):
        schedule, _, _, sequence = setup_schedule
        schedule.skip()
        assert schedule.current_block == schedule.blocks[1]  # Should skip to Phase 2 within the sequence

    def test_total_ticks_left(self, setup_schedule):
        schedule, phase1, phase2, sequence = setup_schedule
        expected_total_ticks = phase1.ticks_left + sequence.ticks_left
        assert schedule.total_ticks_left() == expected_total_ticks

    def test_upcoming_blocks(self, setup_schedule):
        schedule, _, _, sequence = setup_schedule
        assert schedule.upcoming_blocks() == [sequence]

    def test_current_block_is_final(self, setup_schedule):
        schedule, _, _, sequence = setup_schedule
        assert schedule.current_block_is_final() is False
        schedule.advance_to_next_block()
        assert schedule.current_block_is_final() is True

    def test_tick(self, setup_schedule):
        schedule, phase1, _, _ = setup_schedule
        initial_ticks = phase1.ticks_left
        schedule.tick(100)
        assert phase1.ticks_left == initial_ticks - 100

        schedule.tick(phase1.ticks_left)  # Finish phase1
        assert schedule.current_block == schedule.blocks[1]  # Should advance to the next block
