/* 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.syncintegration

import android.os.SystemClock.sleep
import android.widget.EditText

import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText

import org.junit.Rule
import org.junit.Test
import org.mozilla.fenix.helpers.HomeActivityTestRule

import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiSelector
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import okhttp3.mockwebserver.MockWebServer
import org.hamcrest.Matchers.allOf
import org.junit.After
import org.junit.Before
import org.mozilla.fenix.R
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
import org.mozilla.fenix.helpers.TestAssetHelper
import org.mozilla.fenix.helpers.ext.toUri
import org.mozilla.fenix.helpers.ext.waitNotNull
import org.mozilla.fenix.ui.robots.homeScreen
import org.mozilla.fenix.ui.robots.accountSettings
import org.mozilla.fenix.ui.robots.settingsSubMenuLoginsAndPassword
import org.mozilla.fenix.ui.robots.navigationToolbar
import org.mozilla.fenix.ui.robots.browserScreen

@Suppress("RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
class SyncIntegrationTest {
    val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
    private lateinit var mockWebServer: MockWebServer

    @get:Rule
    val activityTestRule = HomeActivityTestRule()

    @Before
    fun setUp() {
        mockWebServer = MockWebServer().apply {
            setDispatcher(AndroidAssetDispatcher())
            start()
        }
    }

    @After
    fun tearDown() {
        mockWebServer.shutdown()
    }

    // History item Desktop -> Fenix
    @Test
    fun checkHistoryFromDesktopTest() {
        signInFxSync()
        tapReturnToPreviousApp()
        // Let's wait until homescreen is shown to go to three dot menu
        mDevice.waitNotNull(Until.findObjects(By.text("Open tabs")), TestAssetHelper.waitingTime)
        homeScreen {
        }.openThreeDotMenu {
        }.openHistory { }
        historyAfterSyncIsShown()
    }

    // Bookmark item Desktop -> Fenix
    @Test
    fun checkBookmarkFromDesktopTest() {
        signInFxSync()
        tapReturnToPreviousApp()
        homeScreen {
        }.openThreeDotMenu {
        }.openBookmarks { }
        bookmarkAfterSyncIsShown()
    }

    @Test
    fun checkAccountSettings() {
        signInFxSync()
        mDevice.waitNotNull(Until.findObjects(By.text("Account")), TestAssetHelper.waitingTime)

        goToAccountSettings()
        // This function to be added to the robot once the status of checkboxes can be checked
        // currently is not possible to select each one (History/Bookmark) and verify its status
        // verifyCheckBoxesSelected()
        // Then select/unselect each one and verify again that its status is correct
        // See issue #6544
        accountSettings {
            verifyBookmarksCheckbox()
            verifyHistoryCheckbox()
            verifySignOutButton()
            verifyDeviceName()
        }.disconnectAccount {
            mDevice.waitNotNull(Until.findObjects(By.text("Settings")), TestAssetHelper.waitingTime)
            verifySettingsView()
        }
    }

    // Login item Desktop -> Fenix
    @Test
    fun checkLoginsFromDesktopTest() {
        homeScreen {
        }.openThreeDotMenu {
        }.openSettings {
        }.openLoginsAndPasswordSubMenu {
        }.openSyncLogins {
            // Tap to sign in from Logins menu
            tapOnUseEmailToSignIn()
            typeEmail()
            tapOnContinueButton()
            typePassword()
            tapOnSignIn()
        }
        // Automatically goes back to Logins and passwords view
        settingsSubMenuLoginsAndPassword {
            verifyDefaultView()
            // Sync logings option is set to Off, no synced logins yet
            verifyDefaultViewBeforeSyncComplete()
        }.openSavedLogins {
            // Discard the secure your device message
            tapSetupLater()
            // Check the logins synced
            verifySavedLoginsAfterSync()
        }.goBack {
            // After checking the synced logins
            // on Logins and Passwords menu the Sync logins option is set to On
            verifyDefaultViewAfterSync()
        }
    }

    // Bookmark item Fenix -> Desktop
    @Test
    fun checkBookmarkFromDeviceTest() {
        val defaultWebPage = "example.com".toUri()!!
        navigationToolbar {
        }.enterURLAndEnterToBrowser(defaultWebPage) {
        }.openThreeDotMenu {
            verifyAddBookmarkButton()
            clickAddBookmarkButton()
        }
        browserScreen {
        }.openThreeDotMenu {
        }.openSettings {
        }.openTurnOnSyncMenu {
            useEmailInsteadButton()
            typeEmail()
            tapOnContinueButton()
            typePassword()
            sleep(TestAssetHelper.waitingTimeShort)
            tapOnSignIn()
        }
    }

    // History item Fenix -> Desktop
    @Test
    fun checkHistoryFromDeviceTest() {
        val defaultWebPage = "example.com".toUri()!!
        navigationToolbar {
        }.enterURLAndEnterToBrowser(defaultWebPage) {
        }.openThreeDotMenu {
        }.openSettings {
        }.openTurnOnSyncMenu {
            useEmailInsteadButton()
            typeEmail()
            tapOnContinueButton()
            typePassword()
            sleep(TestAssetHelper.waitingTimeShort)
            tapOnSignIn()
        }
    }

    // Useful functions for the tests
    fun typeEmail() {
        val emailInput = mDevice.findObject(UiSelector()
                .instance(0)
                .className(EditText::class.java))
        emailInput.waitForExists(TestAssetHelper.waitingTime)

        val emailAddress = javaClass.classLoader.getResource("email.txt").readText()
        emailInput.setText(emailAddress)
    }

    fun tapOnContinueButton() {
        val continueButton = mDevice.findObject(By.res("submit-btn"))
        continueButton.clickAndWait(Until.newWindow(), TestAssetHelper.waitingTime)
    }

    fun typePassword() {
        val passwordInput = mDevice.findObject(UiSelector()
                .instance(0)
                .className(EditText::class.java))

        val passwordValue = javaClass.classLoader.getResource("password.txt").readText()
        passwordInput.setText(passwordValue)
    }

    fun tapOnSignIn() {
        mDevice.waitNotNull(Until.findObjects(By.text("Sign in")))
        // Let's tap on enter, sometimes depending on the device the sign in button is
        // hidden by the keyboard
        mDevice.pressEnter()
    }

    fun historyAfterSyncIsShown() {
        mDevice.waitNotNull(Until.findObjects(By.text("http://www.example.com/")), TestAssetHelper.waitingTime)
    }

    fun bookmarkAfterSyncIsShown() {
        val bookmarkEntry = mDevice.findObject(By.text("Example Domain"))
        bookmarkEntry.isEnabled()
    }

    fun tapReturnToPreviousApp() {
        mDevice.waitNotNull(Until.findObjects(By.text("Save")), TestAssetHelper.waitingTime)
        mDevice.waitNotNull(Until.findObjects(By.text("Settings")), TestAssetHelper.waitingTime)

        // Wait until the Settings shows the account synced
        mDevice.waitNotNull(Until.findObjects(By.text("Account")), TestAssetHelper.waitingTime)
        // Go to Homescreen
        mDevice.pressBack()
    }

    fun signInFxSync() {
        homeScreen {
        }.openThreeDotMenu {
            verifySettingsButton()
        }.openSettings {}
        settingsAccount()
        useEmailInsteadButton()

        typeEmail()
        tapOnContinueButton()
        typePassword()
        sleep(TestAssetHelper.waitingTimeShort)
        tapOnSignIn()
    }

    fun goToAccountSettings() {
        enterAccountSettings()
        mDevice.waitNotNull(Until.findObjects(By.text("Device name")), TestAssetHelper.waitingTime)
    }
}

fun settingsAccount() = onView(allOf(withText("Turn on Sync"))).perform(click())
fun useEmailInsteadButton() = onView(withId(R.id.signInEmailButton)).perform(click())
fun enterAccountSettings() = onView(withId(R.id.email)).perform(click())
