package com.yorvana

import androidx.test.core.app.ApplicationProvider
import com.google.common.truth.Truth.assertThat
import io.mockk.every
import io.mockk.justRun
import io.mockk.mockkStatic
import io.mockk.unmockkStatic
import io.mockk.verify
import io.sentry.Hint
import io.sentry.Sentry
import io.sentry.SentryEvent
import io.sentry.android.core.SentryAndroidOptions
import io.sentry.protocol.User
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config

internal class SentryEnabledApp : TestYorvanaApplication() {
    var doSentryInitCalled = false

    override val sentryDsn = "https://test@sentry.io/123"

    override fun initDependencies() {
        super.initDependencies()
        every { preferences.isCrashReportingEnabledSync() } returns true
    }

    override fun doSentryInit(dsn: String) {
        doSentryInitCalled = true
    }
}

internal class SentryDisabledApp : TestYorvanaApplication() {
    var doSentryInitCalled = false

    override val sentryDsn = "https://test@sentry.io/123"

    override fun doSentryInit(dsn: String) {
        doSentryInitCalled = true
    }
}

internal class EnableNowApp : TestYorvanaApplication() {
    var doSentryInitCalled = false

    override val sentryDsn = "https://test@sentry.io/123"

    override fun doSentryInit(dsn: String) {
        doSentryInitCalled = true
    }
}

internal class EnableNowEmptyDsnApp : TestYorvanaApplication() {
    var doSentryInitCalled = false

    override val sentryDsn = ""

    override fun doSentryInit(dsn: String) {
        doSentryInitCalled = true
    }
}

internal class SentryEmptyDsnApp : TestYorvanaApplication() {
    var doSentryInitCalled = false

    override val sentryDsn = ""

    override fun initDependencies() {
        super.initDependencies()
        every { preferences.isCrashReportingEnabledSync() } returns true
    }

    override fun doSentryInit(dsn: String) {
        doSentryInitCalled = true
    }
}

@RunWith(RobolectricTestRunner::class)
class YorvanaApplicationTest {
    @Test
    @Config(application = SentryEnabledApp::class, sdk = [34])
    fun `initSentry calls doSentryInit when enabled and dsn present`() {
        val app = ApplicationProvider.getApplicationContext<SentryEnabledApp>()
        assertThat(app.doSentryInitCalled).isTrue()
    }

    @Test
    @Config(application = SentryDisabledApp::class, sdk = [34])
    fun `initSentry does not init when crash reporting disabled`() {
        val app = ApplicationProvider.getApplicationContext<SentryDisabledApp>()
        assertThat(app.doSentryInitCalled).isFalse()
    }

    @Test
    @Config(application = SentryEmptyDsnApp::class, sdk = [34])
    fun `initSentry does not init when dsn is empty`() {
        val app = ApplicationProvider.getApplicationContext<SentryEmptyDsnApp>()
        assertThat(app.doSentryInitCalled).isFalse()
    }

    @Test
    @Config(application = SentryEnabledApp::class, sdk = [34])
    fun `beforeSend strips user and serverName when crash reporting enabled`() {
        val app = ApplicationProvider.getApplicationContext<SentryEnabledApp>()
        val event =
            SentryEvent().apply {
                user = User().apply { email = "test@example.com" }
                serverName = "test-server"
            }
        val result = app.beforeSend(event, Hint())
        assertThat(result).isNotNull()
        assertThat(result!!.user).isNull()
        assertThat(result.serverName).isNull()
    }

    @Test
    @Config(application = SentryDisabledApp::class, sdk = [34])
    fun `beforeSend drops event when crash reporting disabled`() {
        val app = ApplicationProvider.getApplicationContext<SentryDisabledApp>()
        assertThat(app.beforeSend(SentryEvent(), Hint())).isNull()
    }

    @Test
    @Config(application = SentryEnabledApp::class, sdk = [34])
    fun `configureSentryOptions sets dsn, environment and release with privacy defaults`() {
        val app = ApplicationProvider.getApplicationContext<SentryEnabledApp>()
        val options = SentryAndroidOptions()

        app.configureSentryOptions(options)

        assertThat(options.dsn).isEqualTo("https://test@sentry.io/123")
        assertThat(options.environment).isEqualTo("debug")
        assertThat(options.release).isEqualTo(
            "${BuildConfig.APPLICATION_ID}@${BuildConfig.VERSION_NAME}+${BuildConfig.VERSION_CODE}",
        )
        assertThat(options.sampleRate).isEqualTo(1.0)
        assertThat(options.tracesSampleRate).isEqualTo(0.0)
        assertThat(options.isSendDefaultPii).isFalse()
        assertThat(options.isEnableUserInteractionTracing).isFalse()
        assertThat(options.isEnableAutoSessionTracking).isFalse()
        assertThat(options.isAttachScreenshot).isFalse()
        assertThat(options.isAttachViewHierarchy).isFalse()
    }

    @Test
    @Config(application = EnableNowApp::class, sdk = [34])
    fun `enableSentryNow calls doSentryInit when dsn present`() {
        val app = ApplicationProvider.getApplicationContext<EnableNowApp>()
        app.enableSentryNow()
        assertThat(app.doSentryInitCalled).isTrue()
    }

    @Test
    @Config(application = EnableNowEmptyDsnApp::class, sdk = [34])
    fun `enableSentryNow is no-op when dsn is empty`() {
        val app = ApplicationProvider.getApplicationContext<EnableNowEmptyDsnApp>()
        app.enableSentryNow()
        assertThat(app.doSentryInitCalled).isFalse()
    }

    @Test
    @Config(application = SentryEnabledApp::class, sdk = [34])
    fun `disableSentryNow closes Sentry when enabled`() =
        runTest {
            mockkStatic(Sentry::class)
            try {
                every { Sentry.isEnabled() } returns true
                justRun { Sentry.close() }

                val app = ApplicationProvider.getApplicationContext<SentryEnabledApp>()
                app.disableSentryNow()

                verify { Sentry.close() }
            } finally {
                unmockkStatic(Sentry::class)
            }
        }

    @Test
    @Config(application = SentryEnabledApp::class, sdk = [34])
    fun `disableSentryNow is no-op when Sentry not enabled`() =
        runTest {
            mockkStatic(Sentry::class)
            try {
                every { Sentry.isEnabled() } returns false

                val app = ApplicationProvider.getApplicationContext<SentryEnabledApp>()
                app.disableSentryNow()

                verify(exactly = 0) { Sentry.close() }
            } finally {
                unmockkStatic(Sentry::class)
            }
        }
}
