package com.anytypeio.anytype.presentation.sets.main

import app.cash.turbine.test
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.core_models.restrictions.DataViewRestriction
import com.anytypeio.anytype.core_models.restrictions.DataViewRestrictions
import com.anytypeio.anytype.domain.primitives.FieldParserImpl
import com.anytypeio.anytype.presentation.collections.MockSet
import com.anytypeio.anytype.presentation.sets.DataViewViewState
import com.anytypeio.anytype.presentation.sets.ObjectSetViewModel
import kotlin.test.assertEquals
import kotlin.test.assertIs
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.mockito.MockitoAnnotations

@OptIn(ExperimentalCoroutinesApi::class)
class ObjectSetRestrictionsTest : ObjectSetViewModelTestSetup() {

    private lateinit var viewModel: ObjectSetViewModel
    private lateinit var mockObjectSet: MockSet

    @Before
    fun setup() {
        MockitoAnnotations.openMocks(this)
        proceedWithDefaultBeforeTestStubbing()
        viewModel = givenViewModel()
        mockObjectSet = MockSet(context = root, space = defaultSpace)
    }

    @After
    fun after() {
        rule.advanceTime()
    }

    @Test
    fun `should show error toast when clicked on viewer button`() = runTest {
        // SETUP
        stubSpaceManager(mockObjectSet.spaceId)
        stubOpenObject(
            doc = listOf(mockObjectSet.header, mockObjectSet.title, mockObjectSet.dataView),
            details = mockObjectSet.details,
            dataViewRestrictions = listOf(
                DataViewRestrictions(
                    block = mockObjectSet.dataView.id,
                    restrictions = listOf(DataViewRestriction.VIEWS)
                )
            )
        )
        stubSubscriptionResults(
            subscription = mockObjectSet.subscriptionId,
            spaceId = mockObjectSet.spaceId,
            storeOfRelations = storeOfRelations,
            keys = mockObjectSet.dvKeys,
            sources = listOf(mockObjectSet.setOf),
            objects = listOf(mockObjectSet.obj1, mockObjectSet.obj2),
            dvFilters = mockObjectSet.filters
        )

        // TESTING
        proceedWithStartingViewModel()

        // ASSERT DATA VIEW STATE
        viewModel.currentViewer.test {
            val first = awaitItem()
            assertIs<DataViewViewState.Init>(first)

            val second = awaitItem()
            assertIs<DataViewViewState.Set.Default>(second)

            // ASSERT ERROR TOAST
            viewModel.toasts.test {
                viewModel.onExpandViewerMenuClicked()
                assertEquals(ObjectSetViewModel.NOT_ALLOWED, awaitItem())
                cancelAndIgnoreRemainingEvents()
            }
        }
    }

    @Test
    fun `should show error toast when clicked on add object button`() = runTest {
        // SETUP
        stubSpaceManager(mockObjectSet.spaceId)
        stubOpenObject(
            doc = listOf(mockObjectSet.header, mockObjectSet.title, mockObjectSet.dataView),
            details = mockObjectSet.details,
            dataViewRestrictions = listOf(
                DataViewRestrictions(
                    block = mockObjectSet.dataView.id,
                    restrictions = listOf(DataViewRestriction.CREATE_OBJECT)
                )
            )
        )
        stubSubscriptionResults(
            subscription = mockObjectSet.subscriptionId,
            spaceId = mockObjectSet.spaceId,
            storeOfRelations = storeOfRelations,
            keys = mockObjectSet.dvKeys,
            sources = listOf(mockObjectSet.setOf),
            objects = listOf(mockObjectSet.obj1, mockObjectSet.obj2),
            dvFilters = mockObjectSet.filters
        )

        // TESTING
        proceedWithStartingViewModel()

        // ASSERT DATA VIEW STATE
        viewModel.currentViewer.test {
            val first = awaitItem()
            assertIs<DataViewViewState.Init>(first)

            val second = awaitItem()
            assertIs<DataViewViewState.Set.Default>(second)

            // ASSERT ERROR TOAST
            viewModel.toasts.test {
                viewModel.proceedWithDataViewObjectCreate()
                assertEquals(ObjectSetViewModel.NOT_ALLOWED, awaitItem())
                cancelAndIgnoreRemainingEvents()
            }
        }
    }

    @Test
    fun `should show error toast when clicked on add filter button`() = runTest {
        // SETUP
        stubSpaceManager(mockObjectSet.spaceId)
        stubOpenObject(
            doc = listOf(mockObjectSet.header, mockObjectSet.title, mockObjectSet.dataView),
            details = mockObjectSet.details,
            dataViewRestrictions = listOf(
                DataViewRestrictions(
                    block = mockObjectSet.dataView.id,
                    restrictions = listOf(DataViewRestriction.VIEWS)
                )
            )
        )
        stubSubscriptionResults(
            subscription = mockObjectSet.subscriptionId,
            spaceId = mockObjectSet.spaceId,
            storeOfRelations = storeOfRelations,
            keys = mockObjectSet.dvKeys,
            sources = listOf(mockObjectSet.setOf),
            objects = listOf(mockObjectSet.obj1, mockObjectSet.obj2),
            dvFilters = mockObjectSet.filters
        )

        // TESTING
        proceedWithStartingViewModel()

        // ASSERT DATA VIEW STATE
        viewModel.currentViewer.test {
            val first = awaitItem()
            assertIs<DataViewViewState.Init>(first)

            val second = awaitItem()
            assertIs<DataViewViewState.Set.Default>(second)

            // ASSERT ERROR TOAST
            viewModel.toasts.test {
                viewModel.onViewerFiltersClicked()
                assertEquals(ObjectSetViewModel.NOT_ALLOWED, awaitItem())
                cancelAndIgnoreRemainingEvents()
            }
        }
    }

    @Test
    fun `should show error toast when clicked on add sorts button`() = runTest {
        // SETUP
        stubSpaceManager(mockObjectSet.spaceId)
        stubInterceptEvents()
        stubInterceptThreadStatus()
        stubOpenObject(
            doc = listOf(mockObjectSet.header, mockObjectSet.title, mockObjectSet.dataView),
            details = mockObjectSet.details,
            dataViewRestrictions = listOf(
                DataViewRestrictions(
                    block = mockObjectSet.dataView.id,
                    restrictions = listOf(DataViewRestriction.VIEWS)
                )
            )
        )
        stubSubscriptionResults(
            subscription = mockObjectSet.subscriptionId,
            spaceId = mockObjectSet.spaceId,
            storeOfRelations = storeOfRelations,
            keys = mockObjectSet.dvKeys,
            sources = listOf(mockObjectSet.setOf),
            objects = listOf(mockObjectSet.obj1, mockObjectSet.obj2),
            dvFilters = mockObjectSet.filters
        )

        // TESTING
        proceedWithStartingViewModel()

        // ASSERT DATA VIEW STATE
        viewModel.currentViewer.test {
            val first = awaitItem()
            assertIs<DataViewViewState.Init>(first)

            val second = awaitItem()
            assertIs<DataViewViewState.Set.Default>(second)

            // ASSERT ERROR TOAST
            viewModel.toasts.test {
                viewModel.onViewerSortsClicked()
                assertEquals(ObjectSetViewModel.NOT_ALLOWED, awaitItem())
                cancelAndIgnoreRemainingEvents()
            }
        }
    }

    @Test
    fun `should show error toast when clicked on relations button`() = runTest {
        // SETUP
        stubSpaceManager(mockObjectSet.spaceId)
        stubInterceptEvents()
        stubInterceptThreadStatus()
        stubOpenObject(
            doc = listOf(mockObjectSet.header, mockObjectSet.title, mockObjectSet.dataView),
            details = mockObjectSet.details,
            dataViewRestrictions = listOf(
                DataViewRestrictions(
                    block = mockObjectSet.dataView.id,
                    restrictions = listOf(DataViewRestriction.RELATION)
                )
            )
        )
        stubSubscriptionResults(
            subscription = mockObjectSet.subscriptionId,
            spaceId = mockObjectSet.spaceId,
            storeOfRelations = storeOfRelations,
            keys = mockObjectSet.dvKeys,
            sources = listOf(mockObjectSet.setOf),
            objects = listOf(mockObjectSet.obj1, mockObjectSet.obj2),
            dvFilters = mockObjectSet.filters
        )

        // TESTING
        proceedWithStartingViewModel()

        // ASSERT DATA VIEW STATE
        viewModel.currentViewer.test {
            val first = awaitItem()
            assertIs<DataViewViewState.Init>(first)

            val second = awaitItem()
            assertIs<DataViewViewState.Set.Default>(second)

            // ASSERT ERROR TOAST
            viewModel.toasts.test {
                viewModel.onViewerSettingsClicked(viewer = mockObjectSet.viewer.id)
                assertEquals(ObjectSetViewModel.NOT_ALLOWED, awaitItem())
                cancelAndIgnoreRemainingEvents()
            }
        }
    }

    private fun proceedWithStartingViewModel() {
        viewModel.onStart()
    }
}