/* 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.components.metrics

import android.content.Context
import android.content.SharedPreferences
import androidx.annotation.VisibleForTesting
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import mozilla.components.support.base.log.logger.Logger
import org.mozilla.fenix.GleanMetrics.FirstSession
import org.mozilla.fenix.GleanMetrics.Pings
import org.mozilla.fenix.ext.settings

class FirstSessionPing(private val context: Context) {

    private val prefs: SharedPreferences by lazy {
        context.getSharedPreferences(
            "${this.javaClass.canonicalName}.prefs", Context.MODE_PRIVATE
        )
    }

    /**
     * Checks whether or not the installation ping was already
     * triggered by the application.
     *
     * Note that this only tells us that Fenix triggered the
     * ping and then delegated the transmission to Glean. We
     * have no way to tell if it was actually sent or not.
     *
     * @return true if it was already triggered, false otherwise.
     */
    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    internal fun wasAlreadyTriggered(): Boolean {
        return prefs.getBoolean("ping_sent", false)
    }

    /**
     * Marks the "installation" ping as triggered by the application.
     * This ensures the ping is not triggered again at the next app
     * start.
     */
    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    internal fun markAsTriggered() {
        prefs.edit().putBoolean("ping_sent", true).apply()
    }

    /**
     * Fills the metrics and triggers the 'installation' ping.
     * This is a separate function to simplify unit-testing.
     */
    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    internal fun triggerPing() {
        if (checkMetricsNotEmpty()) {
            context.settings().also {
                FirstSession.campaign.set(it.adjustCampaignId)
                FirstSession.adgroup.set(it.adjustAdGroup)
                FirstSession.creative.set(it.adjustCreative)
                FirstSession.network.set(it.adjustNetwork)
                FirstSession.timestamp.set()
            }

            CoroutineScope(Dispatchers.IO).launch {
                Pings.firstSession.submit()
                markAsTriggered()
            }
        }
    }

    /**
     * Check that at least one of the metrics values is set before sending the ping.
     * Note: it is normal for many of these values to not be set as campaigns do not always
     * utilize every attribute!
     * */
    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    internal fun checkMetricsNotEmpty(): Boolean = listOf(
        context.settings().adjustCampaignId,
        context.settings().adjustAdGroup,
        context.settings().adjustCreative,
        context.settings().adjustNetwork
    ).any { it.isNotEmpty() }

    /**
     * Trigger sending the `installation` ping if it wasn't sent already.
     * Then, mark it so that it doesn't get triggered next time Fenix
     * starts.
     */
    fun checkAndSend() {
        if (wasAlreadyTriggered()) {
            Logger.debug("InstallationPing - already generated")
            return
        }
        triggerPing()
    }
}
