package mozilla.components.feature.contextmenu

import android.content.res.Resources
import androidx.test.ext.junit.runners.AndroidJUnit4
import mozilla.components.feature.search.SearchAdapter
import mozilla.components.support.test.mock
import mozilla.components.support.test.whenever
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.anyString
import org.mockito.Mockito.times
import org.mockito.Mockito.verify

@RunWith(AndroidJUnit4::class)
class DefaultSelectionActionDelegateTest {

    val selectedRegularText = "mozilla"
    val selectedEmailText = "test@mozilla.org"
    val selectedPhoneText = "555-5555"
    var lambdaValue: String? = null
    val shareClicked: (String) -> Unit = { lambdaValue = it }
    val emailClicked: (String) -> Unit = { lambdaValue = it }
    val phoneClicked: (String) -> Unit = { lambdaValue = it }

    @Before
    fun setup() {
        lambdaValue = null
    }

    @Test
    fun `are non-private regular actions available`() {
        val searchAdapter = mock<SearchAdapter> {
            whenever(isPrivateSession()).thenReturn(false)
        }
        val delegate = DefaultSelectionActionDelegate(
            searchAdapter,
            getTestResources(),
            shareClicked,
            emailClicked,
            phoneClicked
        )

        assertTrue(delegate.isActionAvailable(SEARCH, selectedRegularText))
        assertTrue(delegate.isActionAvailable(SHARE, selectedRegularText))
        assertFalse(delegate.isActionAvailable(SEARCH_PRIVATELY, selectedRegularText))
        assertFalse(delegate.isActionAvailable(EMAIL, selectedRegularText))
        assertFalse(delegate.isActionAvailable(CALL, selectedRegularText))
    }

    @Test
    fun `are non-private non-share actions available`() {
        val searchAdapter = mock<SearchAdapter> {
            whenever(isPrivateSession()).thenReturn(false)
        }
        val delegate = DefaultSelectionActionDelegate(
            searchAdapter,
            getTestResources()
        )

        assertTrue(delegate.isActionAvailable(SEARCH, selectedRegularText))
        assertFalse(delegate.isActionAvailable(SHARE, selectedRegularText))
        assertFalse(delegate.isActionAvailable(SEARCH_PRIVATELY, selectedRegularText))
    }

    @Test
    fun `is email available when passed in and email text selected`() {
        val searchAdapter = mock<SearchAdapter> {
            whenever(isPrivateSession()).thenReturn(false)
        }
        val delegate = DefaultSelectionActionDelegate(
            searchAdapter,
            getTestResources(),
            emailTextClicked = emailClicked
        )

        assertTrue(delegate.isActionAvailable(EMAIL, selectedEmailText))
        assertTrue(delegate.isActionAvailable(EMAIL, " $selectedEmailText "))
        assertFalse(delegate.isActionAvailable(EMAIL, selectedRegularText))
        assertFalse(delegate.isActionAvailable(EMAIL, selectedPhoneText))
        assertFalse(delegate.isActionAvailable(EMAIL, " $selectedPhoneText "))
    }

    @Test
    fun `is call available when passed in and call text selected`() {
        val searchAdapter = mock<SearchAdapter> {
            whenever(isPrivateSession()).thenReturn(false)
        }
        val delegate = DefaultSelectionActionDelegate(
            searchAdapter,
            getTestResources(),
            callTextClicked = phoneClicked
        )

        assertTrue(delegate.isActionAvailable(CALL, selectedPhoneText))
        assertFalse(delegate.isActionAvailable(CALL, selectedRegularText))
        assertFalse(delegate.isActionAvailable(CALL, selectedEmailText))
    }

    @Test
    fun `are private actions available`() {
        val searchAdapter = mock<SearchAdapter> {
            whenever(isPrivateSession()).thenReturn(true)
        }
        val delegate = DefaultSelectionActionDelegate(
            searchAdapter,
            getTestResources(),
            shareClicked
        )

        assertTrue(delegate.isActionAvailable(SEARCH_PRIVATELY, selectedRegularText))
        assertTrue(delegate.isActionAvailable(SHARE, selectedRegularText))
        assertFalse(delegate.isActionAvailable(SEARCH, selectedRegularText))
    }

    @Test
    fun `when share ID is passed to perform action it should invoke the lambda`() {
        val adapter = mock<SearchAdapter>()
        val delegate =
            DefaultSelectionActionDelegate(adapter, getTestResources(), shareClicked)

        delegate.performAction(SHARE, "some selected text")

        assertEquals(lambdaValue, "some selected text")
    }

    @Test
    fun `when email ID is passed to perform action it should invoke the lambda`() {
        val adapter = mock<SearchAdapter>()
        val delegate =
            DefaultSelectionActionDelegate(adapter, getTestResources(), emailTextClicked = emailClicked)

        delegate.performAction(EMAIL, selectedEmailText)

        assertEquals(lambdaValue, selectedEmailText)

        delegate.performAction(EMAIL, " $selectedEmailText ")

        assertEquals(lambdaValue, selectedEmailText)
    }

    @Test
    fun `when call ID is passed to perform action it should invoke the lambda`() {
        val adapter = mock<SearchAdapter>()
        val delegate =
            DefaultSelectionActionDelegate(adapter, getTestResources(), callTextClicked = phoneClicked)

        delegate.performAction(CALL, selectedPhoneText)

        assertEquals(lambdaValue, selectedPhoneText)

        delegate.performAction(CALL, " $selectedPhoneText ")

        assertEquals(lambdaValue, selectedPhoneText)
    }

    @Test
    fun `when unknown ID is passed to performAction it should not perform a search`() {
        val adapter = mock<SearchAdapter>()
        val delegate =
            DefaultSelectionActionDelegate(adapter, getTestResources(), shareClicked)

        delegate.performAction("unrecognized string", "some selected text")

        verify(adapter, times(0)).sendSearch(anyBoolean(), anyString())
    }

    @Test
    fun `when unknown ID is passed to performAction it not consume the action`() {
        val adapter = mock<SearchAdapter>()
        val delegate =
            DefaultSelectionActionDelegate(adapter, getTestResources(), shareClicked)

        val result = delegate.performAction("unrecognized string", "some selected text")

        assertFalse(result)
    }

    @Test
    fun `when search ID is passed to performAction it should perform a search`() {
        val adapter = mock<SearchAdapter>()
        val delegate =
            DefaultSelectionActionDelegate(adapter, getTestResources(), shareClicked)

        delegate.performAction(SEARCH, "some selected text")

        verify(adapter, times(1)).sendSearch(false, "some selected text")
    }

    @Test
    fun `when search ID is passed to performAction it should consume the action`() {
        val adapter = mock<SearchAdapter>()
        val delegate =
            DefaultSelectionActionDelegate(adapter, getTestResources(), shareClicked)

        val result = delegate.performAction(SEARCH, "some selected text")

        assertTrue(result)
    }

    @Test
    fun `when private search ID is passed to performAction it should perform a private normal search`() {
        val adapter = mock<SearchAdapter>()
        val delegate =
            DefaultSelectionActionDelegate(adapter, getTestResources(), shareClicked)

        delegate.performAction(SEARCH_PRIVATELY, "some selected text")

        verify(adapter, times(1)).sendSearch(true, "some selected text")
    }

    @Test
    fun `when private search ID is passed to performAction it should consume the action`() {
        val adapter = mock<SearchAdapter>()
        val delegate =
            DefaultSelectionActionDelegate(adapter, getTestResources(), shareClicked)

        val result = delegate.performAction(SEARCH_PRIVATELY, "some selected text")

        assertTrue(result)
    }
}

fun getTestResources() = mock<Resources> {
    whenever(getString(R.string.mozac_selection_context_menu_search_2)).thenReturn("Search")
    whenever(getString(R.string.mozac_selection_context_menu_search_privately_2))
        .thenReturn("search privately")
    whenever(getString(R.string.mozac_selection_context_menu_share)).thenReturn("share")
    whenever(getString(R.string.mozac_selection_context_menu_email)).thenReturn("email")
    whenever(getString(R.string.mozac_selection_context_menu_call)).thenReturn("call")
}
