package dev.stave.di
import dev.stave.data.SessionStore
import dev.stave.data.SettingsStore
import dev.stave.lyrics.LyricsRepository
import dev.stave.lyrics.LyricsViewModel
import dev.stave.playlist.M3uPlaylistStore
import dev.stave.playlist.M3uPlaylistWriter
import dev.stave.ui.browse.BrowseViewModel
import dev.stave.ui.events.UiEvents
import dev.stave.ui.nowplaying.NowPlayingViewModel
import dev.stave.ui.servers.ServersViewModel
import dev.stave.upnp.AudioMetadataReader
import dev.stave.upnp.ContentDirectoryClient
import dev.stave.upnp.DidlParser
import dev.stave.upnp.MediaCollector
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.module
/**
* Shared Koin module — binds the UPnP layer, the playlist writer, and the ViewModels.
* Platforms bind the UpnpServiceHolder and Renderer concretes themselves (see
* desktopModule / androidModule).
*
* Design note: the VMs are bound as plain singletons rather than Koin-ViewModels because
* Koin 4.0's multiplatform `viewModelOf` DSL has a type-constraint mismatch with the
* JB `lifecycle-viewmodel` KMP artifact in this configuration. App-scoped singletons are
* fine for a desktop app that shows one of each screen at a time — the VM survives
* navigation, which is actually useful here (caching the browse state).
*
* Resolution in composables: `koinInject<BrowseViewModel>()` (not `koinViewModel()`).
*/
val sharedModule = module {
singleOf(::DidlParser)
single { ContentDirectoryClient(holder = get(), parser = get(), settings = get()) }
singleOf(::MediaCollector)
singleOf(::M3uPlaylistWriter)
single { M3uPlaylistStore() }
singleOf(::AudioMetadataReader)
single {
val settings: SettingsStore = get()
dev.stave.upnp.AlbumInfoLoader(
reader = get(),
allowExternalCalls = { settings.allowExternalCalls.value },
)
}
single { dev.stave.ui.albuminfo.AlbumInfoViewModel(loader = get()) }
single { LyricsRepository(get()) }
single { SettingsStore() }
single { SessionStore() }
single { dev.stave.data.SearchHistoryStore() }
single { UiEvents() }
single { dev.stave.playback.LocalPlaybackGate(rawCast = get(), uiEvents = get()) }
single {
BrowseViewModel(
cd = get(),
renderer = get(),
collector = get(),
playGate = get(),
uiEvents = get(),
holder = get(),
)
}
// Explicit get() forms for NowPlayingViewModel + LyricsViewModel: their
// constructor arg counts (7 and 2 respectively) push against Koin 4's
// reflective singleOf reliability. The typed get() calls resolve at module
// build time, so any missing binding surfaces there rather than at first use.
single {
NowPlayingViewModel(
renderer = get(),
rawCast = get(),
cd = get(),
collector = get(),
playlistWriter = get(),
playlistStore = get(),
sessionStore = get(),
settings = get(),
uiEvents = get(),
playGate = get(),
)
}
single { dev.stave.upnp.MinimServerControl(holder = get()) }
single { ServersViewModel(holder = get(), cd = get(), minim = get()) }
single { LyricsViewModel(repository = get(), renderer = get(), rawCast = get(), settings = get()) }
}