package theorycrafter

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.size
import androidx.compose.material.Icon
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.Text
import androidx.compose.material.icons.outlined.Info
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import compose.utils.HSpacer
import compose.utils.VSpacer
import compose.utils.VerticallyCenteredRow
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okio.use
import theorycrafter.ui.TheorycrafterTheme
import theorycrafter.ui.tooltip
import theorycrafter.utils.sendRequestWithRetry


/**
 * Sends an app start event to the analytics system.
 */
suspend fun reportAppLaunch() {
    println("Reporting app launch to analytics")
    reportEvent(
        name = "app_launch",
        params = AppAndOsParams
    )
}


/**
 * Sends an app use event to the analytics system.
 */
suspend fun reportAppUse() {
    println("Reporting daily app use to analytics")
    reportEvent(
        name = "app_use",
        params = AppAndOsParams
    )
}

/**
 * Reports a change in the value of analytics tracking consent.
 */
suspend fun reportTrackingConsent(consent: AnalyticsConsent) {
    println("Reporting tracking consent: $consent")

    val consentText = when (consent) {
        AnalyticsConsent.Yes -> "yes"
        AnalyticsConsent.No -> "no"
        AnalyticsConsent.Undecided -> "undecided"
    }
    reportEvent(
        name = "tracking_consent",
        params = mapOf("consent" to consentText) + AppAndOsParams
    )
}


/**
 * Parameters to send that describe the app and OS.
 */
private val AppAndOsParams = mapOf(
    "version" to Theorycrafter.Version,
    "version_code" to Theorycrafter.VersionCode.toString(),
    "os_name" to System.getProperty("os.name"),
    "os_version" to System.getProperty("os.version")
)


/**
 * Displays the a preview of the information collected.
 */
@OptIn(ExperimentalSerializationApi::class)
@Composable
fun CollectedInformationDescription(modifier: Modifier = Modifier) {
    val jsonText = remember {
        buildString {
            val encoder = Json {
                prettyPrint = true
                prettyPrintIndent = "  "
            }
            append(
                encoder.encodeToString(AppAndOsParams)
            )
        }
    }

    Column(verticalArrangement = Arrangement.spacedBy(TheorycrafterTheme.spacing.xxxsmall)) {
        Text("Information collected:")
        VSpacer(TheorycrafterTheme.spacing.xxxsmall)

        VerticallyCenteredRow {
            Text("• ${Theorycrafter.AppName} version, your Operating System name and version.")
            HSpacer(TheorycrafterTheme.spacing.xxsmall)
            Icon(
                imageVector = TheorycrafterTheme.iconStyle.Info,
                contentDescription = "Preview",
                modifier = modifier
                    .size(with(LocalDensity.current) { LocalTextStyle.current.fontSize.toDp() })
                    .tooltip {
                        Column {
                            Text("Preview:")
                            Text(
                                text = jsonText,
                                style = TextStyle(fontFamily = FontFamily.Monospace),
                            )
                        }
                    }
            )
        }
        Text("• How many times you've used Theorycrafter or specific features.")

        VSpacer(TheorycrafterTheme.spacing.medium)

        Text("Absolutely NOT collected or sent anywhere:")
        VSpacer(TheorycrafterTheme.spacing.xxxsmall)
        Text("• Any information that can personally identify you.")
        Text("• Your ship fits and tournament compositions.")
        Text("• Information about your EVE Online account or characters.")
    }
}


/**
 * The [OkHttpClient] used for Google Analytics.
 */
private val GA_HTTP_CLIENT = OkHttpClient()


/**
 * The base URL where we report events.
 */
private val BASE_URL = "https://www.google-analytics.com/mp/collect".toHttpUrl()


/**
 * The content type of our HTTP requests reporting events.
 */
private val REPORT_EVENT_CONTENT_TYPE = "application/json".toMediaType()


/**
 * A parameter of an event reported to analytics.
 */
@Serializable
private data class EventParam(
    val name: String,
    val params: Map<String, String>
)


/**
 * The payload of the
 */
@Serializable
private data class EventPayload(
    @SerialName("client_id")
    val clientId: String,
    val events: List<EventParam>
) {
    constructor(
        clientId: String,
        name: String,
        params: Map<String, String>,
    ): this(
        clientId = clientId,
        events = listOf(EventParam(name, params))
    )
}


/**
 * Reports an event with the given name and parameters.
 */
private suspend fun reportEvent(name: String, params: Map<String, String> = emptyMap()) {
    val url = BASE_URL.newBuilder()
        .addQueryParameter("measurement_id", Theorycrafter.GaMeasurementId)
        .addQueryParameter("api_secret", Theorycrafter.GaApiSecret)
        .build()

    val payload = EventPayload(
        clientId = TheorycrafterContext.settings.analytics.clientId,
        name = name,
        params = params,
    )

    val bodyText = Json.encodeToString(payload)
    val requestBody = bodyText.toRequestBody(REPORT_EVENT_CONTENT_TYPE)
    val request = Request.Builder()
        .url(url)
        .post(requestBody)
        .build()

    sendRequestWithRetry(
        onFirstException = {
            System.err.println("Analytics request failed.\n${it.javaClass.simpleName} ${it.message}")
        }
    ) {
        GA_HTTP_CLIENT.newCall(request).execute().use { response ->
            if (!response.isSuccessful) {
                System.err.println("Analytics request failed: ${response.message}")
            }
        }
    }
}
