/* 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 org.mozilla.fenix.ui.robots

import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.filter
import androidx.compose.ui.test.hasAnyChild
import androidx.compose.ui.test.hasTestTag
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.ComposeTestRule
import androidx.compose.ui.test.longClick
import androidx.compose.ui.test.onAllNodesWithTag
import androidx.compose.ui.test.onFirst
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollTo
import androidx.compose.ui.test.performTouchInput
import androidx.test.uiautomator.UiSelector
import org.mozilla.fenix.helpers.HomeActivityComposeTestRule
import org.mozilla.fenix.helpers.MatcherHelper.itemContainingText
import org.mozilla.fenix.helpers.MatcherHelper.itemWithResId
import org.mozilla.fenix.helpers.MatcherHelper.itemWithResIdContainingText
import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime
import org.mozilla.fenix.helpers.TestAssetHelper.waitingTimeShort
import org.mozilla.fenix.helpers.TestHelper.mDevice
import org.mozilla.fenix.helpers.TestHelper.packageName
import org.mozilla.fenix.home.topsites.TopSitesTestTag

/**
 * Implementation of Robot Pattern for the Compose Top Sites.
 */
class ComposeTopSitesRobot(private val composeTestRule: HomeActivityComposeTestRule) {

    @OptIn(ExperimentalTestApi::class)
    fun verifyExistingTopSitesList() {
        composeTestRule.waitUntilExactlyOneExists(hasTestTag(TopSitesTestTag.topSites), timeoutMillis = waitingTime)
    }

    @OptIn(ExperimentalTestApi::class)
    fun verifyExistingTopSiteItem(vararg titles: String) {
        titles.forEach { title ->
            mDevice.findObject(UiSelector().textContains(title)).waitForExists(waitingTimeShort)
            composeTestRule.topSiteItem(title).assertExists()
        }
    }

    fun verifyNotExistingTopSiteItem(vararg titles: String) {
        titles.forEach { title ->
            itemContainingText(title).waitForExists(waitingTime)
            composeTestRule.topSiteItem(title).assertDoesNotExist()
        }
    }

    fun verifyTopSiteContextMenuItems() {
        verifyTopSiteContextMenuOpenInPrivateTabButton()
        verifyTopSiteContextMenuRemoveButton()
        verifyTopSiteContextMenuRenameButton()
    }

    fun verifyTopSiteContextMenuOpenInPrivateTabButton() {
        composeTestRule.contextMenuItemOpenInPrivateTab().assertExists()
    }

    fun verifyTopSiteContextMenuRenameButton() {
        composeTestRule.contextMenuItemRename().assertExists()
    }

    fun verifyTopSiteContextMenuRemoveButton() {
        composeTestRule.contextMenuItemRemove().assertExists()
    }

    class Transition(private val composeTestRule: HomeActivityComposeTestRule) {

        fun openTopSiteTabWithTitle(
            title: String,
            interact: BrowserRobot.() -> Unit,
        ): BrowserRobot.Transition {
            composeTestRule.topSiteItem(title).performScrollTo().performClick()

            BrowserRobot().interact()
            return BrowserRobot.Transition()
        }

        fun openTopSiteInPrivate(
            interact: BrowserRobot.() -> Unit,
        ): BrowserRobot.Transition {
            composeTestRule.contextMenuItemOpenInPrivateTab().performClick()
            composeTestRule.waitForIdle()

            BrowserRobot().interact()
            return BrowserRobot.Transition()
        }

        fun openContextMenuOnTopSitesWithTitle(
            title: String,
            interact: ComposeTopSitesRobot.() -> Unit,
        ): Transition {
            composeTestRule.topSiteItem(title).performScrollTo().performTouchInput {
                longClick()
            }

            ComposeTopSitesRobot(composeTestRule).interact()
            return Transition(composeTestRule)
        }

        fun renameTopSite(
            title: String,
            interact: ComposeTopSitesRobot.() -> Unit,
        ): Transition {
            composeTestRule.contextMenuItemRename().performClick()
            itemWithResId("$packageName:id/top_site_title")
                .also {
                    it.waitForExists(waitingTimeShort)
                    it.setText(title)
                }
            itemWithResIdContainingText("android:id/button1", "OK").click()

            ComposeTopSitesRobot(composeTestRule).interact()
            return Transition(composeTestRule)
        }

        @OptIn(ExperimentalTestApi::class)
        fun removeTopSite(
            interact: ComposeTopSitesRobot.() -> Unit,
        ): Transition {
            composeTestRule.contextMenuItemRemove().performClick()
            composeTestRule.waitUntilDoesNotExist(hasTestTag(TopSitesTestTag.remove), waitingTime)

            ComposeTopSitesRobot(composeTestRule).interact()
            return Transition(composeTestRule)
        }

        @OptIn(ExperimentalTestApi::class)
        fun deleteTopSiteFromHistory(
            interact: BrowserRobot.() -> Unit,
        ): BrowserRobot.Transition {
            composeTestRule.contextMenuItemRemove().performClick()
            composeTestRule.waitUntilDoesNotExist(hasTestTag(TopSitesTestTag.remove), waitingTime)

            BrowserRobot().interact()
            return BrowserRobot.Transition()
        }
    }
}

/**
 * Obtains the top site with the provided [title].
 */
private fun ComposeTestRule.topSiteItem(title: String) =
    onAllNodesWithTag(TopSitesTestTag.topSiteItemRoot).filter(hasAnyChild(hasText(title))).onFirst()

/**
 * Obtains the option to open in private tab the top site
 */
private fun ComposeTestRule.contextMenuItemOpenInPrivateTab() =
    onAllNodesWithTag(TopSitesTestTag.openInPrivateTab).onFirst()

/**
 * Obtains the option to rename the top site
 */
private fun ComposeTestRule.contextMenuItemRename() = onAllNodesWithTag(TopSitesTestTag.rename).onFirst()

/**
 * Obtains the option to remove the top site
 */
private fun ComposeTestRule.contextMenuItemRemove() = onAllNodesWithTag(TopSitesTestTag.remove).onFirst()
