package xyz.cottongin.radio247.service

import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import xyz.cottongin.radio247.data.model.Station

class PlaybackStateMachineTest {

    private lateinit var stateFlow: MutableStateFlow<PlaybackState>
    private val testStation = Station(
        id = 1L,
        name = "Test Station",
        url = "http://example.com/stream"
    )
    private val testUrls = listOf(
        "http://example.com/stream1",
        "http://example.com/stream2"
    )

    @Before
    fun setUp() {
        stateFlow = MutableStateFlow(PlaybackState.Idle)
    }

    @Test
    fun `idle to connecting on play`() {
        val newState = PlaybackState.Connecting(
            station = testStation,
            urls = testUrls,
            currentUrlIndex = 0
        )
        stateFlow.value = newState
        assertTrue(stateFlow.value is PlaybackState.Connecting)
        assertEquals(0, (stateFlow.value as PlaybackState.Connecting).currentUrlIndex)
    }

    @Test
    fun `connecting to playing on engine started`() {
        stateFlow.value = PlaybackState.Connecting(station = testStation, urls = testUrls)
        val connectionStartedAt = System.currentTimeMillis()
        stateFlow.value = PlaybackState.Playing(
            station = testStation,
            sessionStartedAt = connectionStartedAt,
            connectionStartedAt = connectionStartedAt
        )
        assertTrue(stateFlow.value is PlaybackState.Playing)
    }

    @Test
    fun `connecting advances URL index on connection failure`() {
        stateFlow.value = PlaybackState.Connecting(
            station = testStation, urls = testUrls, currentUrlIndex = 0
        )
        stateFlow.value = PlaybackState.Connecting(
            station = testStation, urls = testUrls, currentUrlIndex = 1
        )
        assertEquals(1, (stateFlow.value as PlaybackState.Connecting).currentUrlIndex)
    }

    @Test
    fun `connecting to reconnecting when all URLs exhausted and stayConnected`() {
        stateFlow.value = PlaybackState.Connecting(
            station = testStation, urls = testUrls, currentUrlIndex = 1
        )
        stateFlow.value = PlaybackState.Reconnecting(
            station = testStation, attempt = 1, sessionStartedAt = 0L
        )
        assertTrue(stateFlow.value is PlaybackState.Reconnecting)
    }

    @Test
    fun `connecting to idle when all URLs exhausted and not stayConnected`() {
        stateFlow.value = PlaybackState.Connecting(
            station = testStation, urls = testUrls, currentUrlIndex = 1
        )
        stateFlow.value = PlaybackState.Idle
        assertTrue(stateFlow.value is PlaybackState.Idle)
    }

    @Test
    fun `playing to paused on user pause`() {
        val now = System.currentTimeMillis()
        stateFlow.value = PlaybackState.Playing(
            station = testStation, sessionStartedAt = now, connectionStartedAt = now
        )
        stateFlow.value = PlaybackState.Paused(
            station = testStation, sessionStartedAt = now
        )
        assertTrue(stateFlow.value is PlaybackState.Paused)
    }

    @Test
    fun `playing to reconnecting on connection lost with stayConnected`() {
        val now = System.currentTimeMillis()
        stateFlow.value = PlaybackState.Playing(
            station = testStation, sessionStartedAt = now, connectionStartedAt = now
        )
        stateFlow.value = PlaybackState.Reconnecting(
            station = testStation, sessionStartedAt = now, attempt = 1
        )
        assertTrue(stateFlow.value is PlaybackState.Reconnecting)
    }

    @Test
    fun `playing to idle on connection lost without stayConnected`() {
        val now = System.currentTimeMillis()
        stateFlow.value = PlaybackState.Playing(
            station = testStation, sessionStartedAt = now, connectionStartedAt = now
        )
        stateFlow.value = PlaybackState.Idle
        assertTrue(stateFlow.value is PlaybackState.Idle)
    }

    @Test
    fun `paused to connecting on resume`() {
        val now = System.currentTimeMillis()
        stateFlow.value = PlaybackState.Paused(
            station = testStation, sessionStartedAt = now
        )
        stateFlow.value = PlaybackState.Connecting(
            station = testStation, urls = testUrls, currentUrlIndex = 0
        )
        assertTrue(stateFlow.value is PlaybackState.Connecting)
    }

    @Test
    fun `reconnecting to connecting on retry`() {
        stateFlow.value = PlaybackState.Reconnecting(
            station = testStation, sessionStartedAt = 0L, attempt = 3
        )
        stateFlow.value = PlaybackState.Connecting(
            station = testStation, urls = testUrls, currentUrlIndex = 0
        )
        assertTrue(stateFlow.value is PlaybackState.Connecting)
    }

    @Test
    fun `reconnecting to idle on user stop`() {
        stateFlow.value = PlaybackState.Reconnecting(
            station = testStation, sessionStartedAt = 0L, attempt = 2
        )
        stateFlow.value = PlaybackState.Idle
        assertTrue(stateFlow.value is PlaybackState.Idle)
    }

    @Test
    fun `stop from any state returns to idle`() {
        val states = listOf(
            PlaybackState.Connecting(station = testStation, urls = testUrls),
            PlaybackState.Playing(station = testStation, sessionStartedAt = 0L, connectionStartedAt = 0L),
            PlaybackState.Paused(station = testStation, sessionStartedAt = 0L),
            PlaybackState.Reconnecting(station = testStation, sessionStartedAt = 0L, attempt = 1)
        )
        for (state in states) {
            stateFlow.value = state
            stateFlow.value = PlaybackState.Idle
            assertTrue("Should be Idle after stop from $state", stateFlow.value is PlaybackState.Idle)
        }
    }

    @Test
    fun `audio focus loss during playback transitions to idle`() {
        val now = System.currentTimeMillis()
        stateFlow.value = PlaybackState.Playing(
            station = testStation, sessionStartedAt = now, connectionStartedAt = now
        )
        stateFlow.value = PlaybackState.Idle
        assertTrue(stateFlow.value is PlaybackState.Idle)
    }

    @Test
    fun `audio focus denied prevents transition from idle`() {
        stateFlow.value = PlaybackState.Idle
        assertTrue(stateFlow.value is PlaybackState.Idle)
    }

    @Test
    fun `audio focus loss transient during playback stays playing`() {
        val now = System.currentTimeMillis()
        stateFlow.value = PlaybackState.Playing(
            station = testStation, sessionStartedAt = now, connectionStartedAt = now
        )
        assertTrue(stateFlow.value is PlaybackState.Playing)
    }
}
