package com.ponzischeme89.memby

import android.annotation.SuppressLint
import android.content.Context
import com.ponzischeme89.memby.data.EmbyRepository
import com.ponzischeme89.memby.data.MaintenanceMonitor
import com.ponzischeme89.memby.data.PreferencesSync
import com.ponzischeme89.memby.data.SettingsStore
import com.ponzischeme89.memby.data.ThemeSync
import com.ponzischeme89.memby.data.playback.installAudioCapabilityProbe
import com.ponzischeme89.memby.update.RequiredUpdateGuard

/**
 * Tiny manual dependency container. Initialised once from [MembyApp] so that the
 * DreamService, activities and composables can all share a single repository /
 * settings instance without pulling in a DI framework.
 */
object ServiceLocator {
    // [init] only ever stores an applicationContext, whose lifetime is the process, so this
    // holds nothing that could outlive its owner. Keep it that way.
    @SuppressLint("StaticFieldLeak")
    lateinit var settings: SettingsStore
        private set
    lateinit var repository: EmbyRepository
        private set
    lateinit var maintenance: MaintenanceMonitor
        private set

    /**
     * Held rather than discarded because it is a long-lived collector, not a service
     * anything calls: it starts working when it is constructed and must not be collected
     * while a viewer's settings are half-synced.
     */
    lateinit var preferencesSync: PreferencesSync
        private set

    /**
     * Held for the same reason [preferencesSync] is, and read as well as held: the settings
     * picker asks it which schemes this viewer may choose and whether a season has taken the
     * choice away for the moment.
     */
    lateinit var themeSync: ThemeSync
        private set

    /**
     * Held for the same reason [preferencesSync] is: it is a collector, and the refusal it
     * writes down can arrive on any request at any moment of a session.
     */
    lateinit var requiredUpdateGuard: RequiredUpdateGuard
        private set

    fun init(context: Context) {
        if (::repository.isInitialized) return
        // Only hands the probe an application context; it does no work until the first
        // request or playback negotiation asks what this television's receiver accepts.
        installAudioCapabilityProbe(context)
        settings = SettingsStore(context.applicationContext)
        // Started before the repository, so a refusal answering the very first request a
        // television makes has somewhere to be recorded.
        requiredUpdateGuard = RequiredUpdateGuard(settings)
        repository = EmbyRepository(settings)
        maintenance = MaintenanceMonitor(repository, settings)
        // Takes the revision channel from the status poll rather than polling itself: the
        // app already asks the gateway a question every ten seconds, and settings do not
        // deserve a second connection.
        preferencesSync = PreferencesSync(repository, settings, maintenance.preferencesRevision)
        // Likewise rides the status poll. It is constructed here rather than by a screen
        // because the palette has to be applied before the first frame of the launcher, and
        // because the surfaces that obey it — the launcher, the player's Compose islands,
        // the screensaver's DreamService — are separate roots with no common owner but this.
        themeSync = ThemeSync(repository, settings, maintenance.theme)
    }
}
