package com.yorvana

import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.lifecycle.Lifecycle
import androidx.test.core.app.ActivityScenario
import com.google.common.truth.Truth.assertThat
import com.yorvana.data.billing.FakeBillingManager
import com.yorvana.ui.theme.YorvanaTheme
import com.yorvana.util.CrashReporter
import io.mockk.every
import io.mockk.mockkObject
import io.mockk.unmockkAll
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config

@RunWith(RobolectricTestRunner::class)
@Config(application = TestYorvanaApplication::class, sdk = [34])
class MainActivityTest {
    @get:Rule
    val composeTestRule = createComposeRule()

    @Before
    fun setUp() {
        mockkObject(CrashReporter)
        // Default: no crash log
        every { CrashReporter.read(any()) } returns null
        every { CrashReporter.install(any()) } returns Unit
        every { CrashReporter.clear(any()) } returns Unit
    }

    @After
    fun tearDown() {
        unmockkAll()
    }

    @Test
    fun `launches to Setup screen when no vault URI is present`() {
        val application = org.robolectric.RuntimeEnvironment.getApplication() as TestYorvanaApplication

        composeTestRule.setContent {
            YorvanaTheme {
                MainContent(application)
            }
        }
        composeTestRule.waitForIdle()

        composeTestRule.onNodeWithText("Welcome", substring = true).assertExists()
    }

    @Test
    fun `displays crash report dialog when crash log is detected`() {
        val crashLog = "Something exploded!"
        every { CrashReporter.read(any()) } returns crashLog
        val application = org.robolectric.RuntimeEnvironment.getApplication() as TestYorvanaApplication

        composeTestRule.setContent {
            YorvanaTheme {
                MainContent(application)
            }
        }
        composeTestRule.waitForIdle()

        composeTestRule.onNodeWithText("Previous crash detected").assertExists()
        composeTestRule.onNodeWithText(crashLog).assertExists()
    }

    @Test
    fun `dismissing crash report dialog clears log and navigates`() {
        every { CrashReporter.read(any()) } returns "Crash log"
        val application = org.robolectric.RuntimeEnvironment.getApplication() as TestYorvanaApplication

        composeTestRule.setContent {
            YorvanaTheme {
                MainContent(application)
            }
        }
        composeTestRule.waitForIdle()

        composeTestRule.onNodeWithText("Dismiss").performClick()
        composeTestRule.waitForIdle()
        composeTestRule.onNodeWithText("Previous crash detected").assertDoesNotExist()
    }

    @Test
    fun `applyVaultUri updates storage and refreshes repository`() {
        val application = org.robolectric.RuntimeEnvironment.getApplication() as TestYorvanaApplication
        val uri = "content://com.android.externalstorage.documents/tree/primary%3AYorvana"

        application.applyVaultUri(uri)

        io.mockk.verify { application.vaultStorage.setVaultUri(any()) }
        application.testScope.testScheduler.advanceUntilIdle()
        io.mockk.coVerify { application.vehicleRepository.refresh() }
    }

    @Test
    fun `billing startConnection called on create, refresh suppressed on first resume`() {
        val application = org.robolectric.RuntimeEnvironment.getApplication() as TestYorvanaApplication
        val fakeBilling = application.billingManager as FakeBillingManager

        ActivityScenario.launch(MainActivity::class.java).use {
            assertThat(fakeBilling.startConnectionCalled).isTrue()
            // The first resume immediately follows onCreate; refresh is suppressed to avoid
            // a redundant back-to-back query alongside the one triggered by startConnection.
            assertThat(fakeBilling.refreshCalled).isFalse()
        }
    }

    @Test
    fun `billing refresh called on subsequent resume after going to background`() {
        val application = org.robolectric.RuntimeEnvironment.getApplication() as TestYorvanaApplication
        val fakeBilling = application.billingManager as FakeBillingManager

        ActivityScenario.launch(MainActivity::class.java).use { scenario ->
            // Simulate background → foreground; this is a non-first resume so refresh fires.
            scenario.moveToState(Lifecycle.State.STARTED)
            scenario.moveToState(Lifecycle.State.RESUMED)
            assertThat(fakeBilling.refreshCalled).isTrue()
        }
    }

    @Test
    fun `billing endConnection called on real destroy`() {
        val application = org.robolectric.RuntimeEnvironment.getApplication() as TestYorvanaApplication
        val fakeBilling = application.billingManager as FakeBillingManager

        val scenario = ActivityScenario.launch(MainActivity::class.java)
        scenario.close()
        assertThat(fakeBilling.endConnectionCalled).isTrue()
    }

    @Test
    fun `billing endConnection skipped on configuration change`() {
        val application = org.robolectric.RuntimeEnvironment.getApplication() as TestYorvanaApplication
        val fakeBilling = application.billingManager as FakeBillingManager

        val scenario = ActivityScenario.launch(MainActivity::class.java)
        // recreate() triggers onDestroy with isChangingConfigurations=true followed by a
        // fresh onCreate; endConnection must NOT fire during the config-change destroy.
        scenario.recreate()
        assertThat(fakeBilling.endConnectionCalled).isFalse()

        scenario.close()
    }
}
