/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package mozilla.components.browser.state.action

import mozilla.components.browser.state.selector.findTab
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.MediaSessionState
import mozilla.components.browser.state.state.createTab
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.concept.engine.mediasession.MediaSession
import mozilla.components.support.test.ext.joinBlocking
import mozilla.components.support.test.mock
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Test

class MediaSessionActionTest {
    @Test
    fun `ActivatedMediaSessionAction updates media session state`() {
        val store = BrowserStore(
            BrowserState(
                tabs = listOf(
                    createTab("https://www.mozilla.org", id = "test-tab")
                )
            )
        )

        val mediaSessionController: MediaSession.Controller = mock()

        store.dispatch(
            MediaSessionAction.ActivatedMediaSessionAction(
                "test-tab",
                mediaSessionController
            )
        ).joinBlocking()

        val mediaSessionState: MediaSessionState? = store.state.findTab("test-tab")?.mediaSessionState
        assertNotNull(mediaSessionState)
        assertEquals(mediaSessionController, mediaSessionState?.controller)
    }

    @Test
    fun `DeactivatedMediaSessionAction updates media session state`() {
        val store = BrowserStore(
            BrowserState(
                tabs = listOf(
                    createTab("https://www.mozilla.org", id = "test-tab")
                )
            )
        )

        val mediaSessionController: MediaSession.Controller = mock()

        store.dispatch(
            MediaSessionAction.ActivatedMediaSessionAction(
                "test-tab",
                mediaSessionController
            )
        ).joinBlocking()

        store.dispatch(
            MediaSessionAction.DeactivatedMediaSessionAction(
                "test-tab"
            )
        ).joinBlocking()

        val mediaSessionState: MediaSessionState? = store.state.findTab("test-tab")?.mediaSessionState
        assertNull(mediaSessionState)
    }

    @Test
    fun `UpdateMediaMetadataAction updates media session state`() {
        val store = BrowserStore(
            BrowserState(
                tabs = listOf(
                    createTab("https://www.mozilla.org", id = "test-tab")
                )
            )
        )

        val mediaSessionController: MediaSession.Controller = mock()
        val metadata: MediaSession.Metadata = mock()

        store.dispatch(
            MediaSessionAction.ActivatedMediaSessionAction(
                "test-tab",
                mediaSessionController
            )
        ).joinBlocking()

        store.dispatch(
            MediaSessionAction.UpdateMediaMetadataAction(
                "test-tab",
                metadata
            )
        ).joinBlocking()

        val mediaSessionState: MediaSessionState? = store.state.findTab("test-tab")?.mediaSessionState
        assertNotNull(mediaSessionState)
        assertEquals(mediaSessionController, mediaSessionState?.controller)
        assertEquals(metadata, mediaSessionState?.metadata)
    }

    @Test
    fun `UpdateMediaPlaybackStateAction updates media session state`() {
        val store = BrowserStore(
            BrowserState(
                tabs = listOf(
                    createTab("https://www.mozilla.org", id = "test-tab")
                )
            )
        )

        val mediaSessionController: MediaSession.Controller = mock()
        val playbackState: MediaSession.PlaybackState = mock()

        store.dispatch(
            MediaSessionAction.ActivatedMediaSessionAction(
                "test-tab",
                mediaSessionController
            )
        ).joinBlocking()

        store.dispatch(
            MediaSessionAction.UpdateMediaPlaybackStateAction(
                "test-tab",
                playbackState
            )
        ).joinBlocking()

        val mediaSessionState: MediaSessionState? = store.state.findTab("test-tab")?.mediaSessionState
        assertNotNull(mediaSessionState)
        assertEquals(mediaSessionController, mediaSessionState?.controller)
        assertEquals(playbackState, mediaSessionState?.playbackState)
    }

    @Test
    fun `UpdateMediaFeatureAction updates media session state`() {
        val store = BrowserStore(
            BrowserState(
                tabs = listOf(
                    createTab("https://www.mozilla.org", id = "test-tab")
                )
            )
        )

        val mediaSessionController: MediaSession.Controller = mock()
        val features: MediaSession.Feature = mock()

        store.dispatch(
            MediaSessionAction.ActivatedMediaSessionAction(
                "test-tab",
                mediaSessionController
            )
        ).joinBlocking()

        store.dispatch(
            MediaSessionAction.UpdateMediaFeatureAction(
                "test-tab",
                features
            )
        ).joinBlocking()

        val mediaSessionState: MediaSessionState? = store.state.findTab("test-tab")?.mediaSessionState
        assertNotNull(mediaSessionState)
        assertEquals(mediaSessionController, mediaSessionState?.controller)
        assertEquals(features, mediaSessionState?.features)
    }

    @Test
    fun `UpdateMediaPositionStateAction updates media session state`() {
        val store = BrowserStore(
            BrowserState(
                tabs = listOf(
                    createTab("https://www.mozilla.org", id = "test-tab")
                )
            )
        )

        val mediaSessionController: MediaSession.Controller = mock()
        val positionState: MediaSession.PositionState = mock()

        store.dispatch(
            MediaSessionAction.ActivatedMediaSessionAction(
                "test-tab",
                mediaSessionController
            )
        ).joinBlocking()

        store.dispatch(
            MediaSessionAction.UpdateMediaPositionStateAction(
                "test-tab",
                positionState
            )
        ).joinBlocking()

        val mediaSessionState: MediaSessionState? = store.state.findTab("test-tab")?.mediaSessionState
        assertNotNull(mediaSessionState)
        assertEquals(mediaSessionController, mediaSessionState?.controller)
        assertEquals(positionState, mediaSessionState?.positionState)
    }

    @Test
    fun `UpdateMediaMutedAction updates media session state`() {
        val store = BrowserStore(
            BrowserState(
                tabs = listOf(
                    createTab("https://www.mozilla.org", id = "test-tab")
                )
            )
        )

        val mediaSessionController: MediaSession.Controller = mock()

        store.dispatch(
            MediaSessionAction.ActivatedMediaSessionAction(
                "test-tab",
                mediaSessionController
            )
        ).joinBlocking()

        store.dispatch(
            MediaSessionAction.UpdateMediaMutedAction(
                "test-tab",
                true
            )
        ).joinBlocking()

        val mediaSessionState: MediaSessionState? = store.state.findTab("test-tab")?.mediaSessionState
        assertNotNull(mediaSessionState)
        assertEquals(mediaSessionController, mediaSessionState?.controller)
        assertEquals(true, mediaSessionState?.muted)
    }

    @Test
    fun `UpdateMediaFullscreenAction updates media session state`() {
        val store = BrowserStore(
            BrowserState(
                tabs = listOf(
                    createTab("https://www.mozilla.org", id = "test-tab")
                )
            )
        )

        val mediaSessionController: MediaSession.Controller = mock()
        val elementMetadata: MediaSession.ElementMetadata = mock()

        store.dispatch(
            MediaSessionAction.ActivatedMediaSessionAction(
                "test-tab",
                mediaSessionController
            )
        ).joinBlocking()

        store.dispatch(
            MediaSessionAction.UpdateMediaFullscreenAction(
                "test-tab",
                true,
                elementMetadata
            )
        ).joinBlocking()

        val mediaSessionState: MediaSessionState? = store.state.findTab("test-tab")?.mediaSessionState
        assertNotNull(mediaSessionState)
        assertEquals(mediaSessionController, mediaSessionState?.controller)
        assertEquals(true, mediaSessionState?.fullscreen)
        assertEquals(elementMetadata, mediaSessionState?.elementMetadata)
    }

    @Test
    fun `updates are ignore if media session is not activated`() {
        val store = BrowserStore(
            BrowserState(
                tabs = listOf(
                    createTab("https://www.mozilla.org", id = "test-tab")
                )
            )
        )

        val elementMetadata: MediaSession.ElementMetadata = mock()

        store.dispatch(
            MediaSessionAction.UpdateMediaFullscreenAction(
                "test-tab",
                true,
                elementMetadata
            )
        ).joinBlocking()

        val mediaSessionState: MediaSessionState? = store.state.findTab("test-tab")?.mediaSessionState
        assertNull(mediaSessionState)

        store.dispatch(
            MediaSessionAction.UpdateMediaMutedAction(
                "test-tab",
                true
            )
        ).joinBlocking()
        assertNull(mediaSessionState)
    }
}
