/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.qs.tiles

import android.os.Handler
import android.service.quicksettings.Tile
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tiles.dialog.InternetDialogFactory
import com.android.systemui.statusbar.connectivity.AccessPointController
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.ethernet.domain.EthernetInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel
import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel.Wifi
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.InternetTileViewModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
import org.mockito.MockitoAnnotations

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWithLooper(setAsMainLooper = true)
class InternetTileNewImplTest : SysuiTestCase() {
    lateinit var underTest: InternetTileNewImpl

    private val testDispatcher = StandardTestDispatcher()
    private val testScope = TestScope(testDispatcher)

    private var airplaneModeRepository = FakeAirplaneModeRepository()
    private var connectivityRepository = FakeConnectivityRepository()
    private var ethernetInteractor = EthernetInteractor(connectivityRepository)
    private var mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
    private var wifiRepository = FakeWifiRepository()
    private var wifiInteractor =
        WifiInteractorImpl(connectivityRepository, wifiRepository, testScope.backgroundScope)
    private lateinit var viewModel: InternetTileViewModel

    private lateinit var looper: TestableLooper

    @Mock private lateinit var host: QSHost
    @Mock private lateinit var eventLogger: QsEventLogger
    @Mock private lateinit var metricsLogger: MetricsLogger
    @Mock private lateinit var sbStateController: StatusBarStateController
    @Mock private lateinit var activityStarter: ActivityStarter
    @Mock private lateinit var logger: QSLogger
    @Mock private lateinit var dialogFactory: InternetDialogFactory
    @Mock private lateinit var accessPointController: AccessPointController

    @Before
    fun setUp() {
        MockitoAnnotations.initMocks(this)
        looper = TestableLooper.get(this)

        // Allow the tile to load resources
        whenever(host.context).thenReturn(context)
        whenever(host.userContext).thenReturn(context)

        viewModel =
            InternetTileViewModel(
                airplaneModeRepository,
                connectivityRepository,
                ethernetInteractor,
                mobileIconsInteractor,
                wifiInteractor,
                context,
                testScope.backgroundScope,
            )

        underTest =
            InternetTileNewImpl(
                host,
                eventLogger,
                looper.looper,
                Handler(looper.looper),
                FalsingManagerFake(),
                metricsLogger,
                sbStateController,
                activityStarter,
                logger,
                viewModel,
                dialogFactory,
                accessPointController
            )

        underTest.initialize()
        underTest.setListening(Object(), true)

        looper.processAllMessages()
    }

    @Test
    fun noDefaultConnection_noNetworkAvailable() =
        testScope.runTest {
            connectivityRepository.defaultConnections.value = DefaultConnectionModel()
            wifiRepository.wifiScanResults.value = listOf()

            runCurrent()
            looper.processAllMessages()

            assertThat(underTest.state.secondaryLabel.toString())
                .isEqualTo(context.getString(R.string.quick_settings_networks_unavailable))
            assertThat(underTest.state.state).isEqualTo(Tile.STATE_INACTIVE)
        }

    @Test
    fun noDefaultConnection_networksAvailable() =
        testScope.runTest {
            connectivityRepository.defaultConnections.value = DefaultConnectionModel()
            wifiRepository.wifiScanResults.value =
                listOf(
                    WifiScanEntry(ssid = "ssid 1"),
                    WifiScanEntry(ssid = "ssid 2"),
                )

            runCurrent()
            looper.processAllMessages()

            assertThat(underTest.state.secondaryLabel.toString())
                .isEqualTo(context.getString(R.string.quick_settings_networks_available))
            assertThat(underTest.state.state).isEqualTo(1)
        }

    @Test
    fun airplaneMode_enabled_wifiDisabled() =
        testScope.runTest {
            airplaneModeRepository.setIsAirplaneMode(true)
            connectivityRepository.defaultConnections.value = DefaultConnectionModel()
            wifiRepository.setIsWifiEnabled(false)

            runCurrent()
            looper.processAllMessages()

            assertThat(underTest.state.state).isEqualTo(Tile.STATE_INACTIVE)
            assertThat(underTest.state.secondaryLabel)
                .isEqualTo(context.getString(R.string.status_bar_airplane))
        }

    @Test
    fun airplaneMode_enabled_wifiEnabledButNotConnected() =
        testScope.runTest {
            airplaneModeRepository.setIsAirplaneMode(true)
            connectivityRepository.defaultConnections.value = DefaultConnectionModel()
            wifiRepository.setIsWifiEnabled(true)

            runCurrent()
            looper.processAllMessages()

            assertThat(underTest.state.state).isEqualTo(Tile.STATE_INACTIVE)
            assertThat(underTest.state.secondaryLabel)
                .isEqualTo(context.getString(R.string.status_bar_airplane))
        }

    @Test
    fun airplaneMode_enabled_wifiEnabledAndConnected() =
        testScope.runTest {
            airplaneModeRepository.setIsAirplaneMode(true)
            connectivityRepository.defaultConnections.value =
                DefaultConnectionModel(
                    wifi = Wifi(true),
                    isValidated = true,
                )
            wifiRepository.setIsWifiEnabled(true)
            wifiRepository.setWifiNetwork(ACTIVE_WIFI)

            runCurrent()
            looper.processAllMessages()

            assertThat(underTest.state.state).isEqualTo(Tile.STATE_ACTIVE)
            assertThat(underTest.state.secondaryLabel).isEqualTo(WIFI_SSID)
        }

    @Test
    fun wifiConnected() =
        testScope.runTest {
            connectivityRepository.defaultConnections.value =
                DefaultConnectionModel(
                    wifi = Wifi(true),
                    isValidated = true,
                )

            wifiRepository.setIsWifiEnabled(true)
            wifiRepository.setWifiNetwork(ACTIVE_WIFI)

            runCurrent()
            looper.processAllMessages()

            assertThat(underTest.state.state).isEqualTo(Tile.STATE_ACTIVE)
            assertThat(underTest.state.secondaryLabel).isEqualTo(WIFI_SSID)
        }

    companion object {
        const val WIFI_SSID = "test ssid"
        val ACTIVE_WIFI =
            WifiNetworkModel.Active(
                networkId = 1,
                isValidated = true,
                level = 4,
                ssid = WIFI_SSID,
            )
    }
}
