package com.seven.ikar.kotlin.app

import android.content.Context
import com.seven.ikar.kotlin.BuildConfig
import com.seven.ikar.kotlin.core.contacts.AndroidContactDiscoveryService
import com.seven.ikar.kotlin.core.contacts.ResolvedContactsRepository
import com.seven.ikar.kotlin.core.local.ChatListCache
import com.seven.ikar.kotlin.core.local.ChatMessageCache
import com.seven.ikar.kotlin.core.local.IkarLocalDatabase
import com.seven.ikar.kotlin.core.model.AuthSessionDto
import com.seven.ikar.kotlin.core.network.ApiClient
import com.seven.ikar.kotlin.core.network.ClientVersionProvider
import com.seven.ikar.kotlin.core.push.AndroidPushSettings
import com.seven.ikar.kotlin.core.push.IncomingCallInviteStore
import com.seven.ikar.kotlin.core.push.IncomingPushNavigationStore
import com.seven.ikar.kotlin.core.push.PushRegistrationManager
import com.seven.ikar.kotlin.core.realtime.RealtimeService
import com.seven.ikar.kotlin.core.settings.AndroidChatSettings
import com.seven.ikar.kotlin.core.settings.AndroidAppSettings
import com.seven.ikar.kotlin.core.settings.AndroidPrivacyRuntimeSettings
import com.seven.ikar.kotlin.core.settings.AppLockState
import com.seven.ikar.kotlin.core.settings.AppProxyType
import com.seven.ikar.kotlin.core.settings.AppSettingsSnapshot
import com.seven.ikar.kotlin.core.session.AuthorizedSessionManager
import com.seven.ikar.kotlin.core.session.SessionStore
import com.seven.ikar.kotlin.core.update.ArgusAppUpdateManager
import com.seven.ikar.kotlin.feature.archive.AndroidArchiveFileService
import com.seven.ikar.kotlin.feature.archive.IncomingArchiveStore
import com.seven.ikar.kotlin.feature.call.AndroidWebRtcCallService
import com.seven.ikar.kotlin.feature.call.AndroidCallTonePlayer
import com.seven.ikar.kotlin.feature.call.CallManager
import com.seven.ikar.kotlin.feature.chat.AndroidAttachmentFileService
import com.seven.ikar.kotlin.feature.chat.AndroidVoiceNotePlayerService
import com.seven.ikar.kotlin.feature.chat.AndroidVoiceNoteRecorderService
import com.seven.ikar.kotlin.feature.chat.ChatScrollStateStore
import com.seven.ikar.kotlin.feature.share.IncomingShareStore
import com.seven.ikar.kotlin.feature.transfer.TransferQueueRepository
import com.seven.ikar.kotlin.feature.transfer.TransferWorkScheduler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import java.net.InetSocketAddress
import java.net.Proxy

class AppContainer(context: Context) {
    val appContext: Context = context.applicationContext
    val applicationScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)

    val json: Json = Json {
        ignoreUnknownKeys = true
        explicitNulls = false
        isLenient = true
    }

    private val loggingInterceptor = HttpLoggingInterceptor().apply {
        level = HttpLoggingInterceptor.Level.BASIC
    }

    val pushSettings = AndroidPushSettings(appContext)
    val chatSettings = AndroidChatSettings(appContext)
    val appSettings = AndroidAppSettings(appContext)
    val privacyRuntimeSettings = AndroidPrivacyRuntimeSettings(appContext)
    val appLockState = AppLockState()

    val clientVersionProvider = ClientVersionProvider(
        versionCode = BuildConfig.VERSION_CODE.toString(),
        versionName = BuildConfig.VERSION_NAME
    )

    @Volatile
    private var activeHttpClient: OkHttpClient = buildHttpClient(appSettings.loadSnapshot())

    val httpClient: OkHttpClient
        get() = activeHttpClient

    val sessionStore = SessionStore(appContext, json)
    val localDatabase = IkarLocalDatabase.getInstance(appContext)
    val chatMessageCache = ChatMessageCache(
        database = localDatabase,
        dao = localDatabase.cachedChatMessageDao(),
        json = json
    )
    val chatListCache = ChatListCache(
        dao = localDatabase.cachedChatDao(),
        json = json
    )
    val appUpdateManager = ArgusAppUpdateManager(appContext)
    val apiClient = ApiClient(
        httpClientProvider = { httpClient },
        json = json,
        clientVersionProvider = clientVersionProvider
    )
    val authorizedSessionManager = AuthorizedSessionManager(
        apiClient = apiClient,
        sessionStore = sessionStore
    )
    val contactDiscoveryService = AndroidContactDiscoveryService(appContext)
    val resolvedContactsRepository = ResolvedContactsRepository(
        apiClient = apiClient,
        sessionStore = sessionStore,
        authorizedSessionManager = authorizedSessionManager,
        contactDiscoveryService = contactDiscoveryService
    )
    val attachmentFileService = AndroidAttachmentFileService(appContext)
    val archiveFileService = AndroidArchiveFileService(appContext)
    val transferWorkScheduler = TransferWorkScheduler(appContext)
    val transferQueueRepository = TransferQueueRepository(
        dao = localDatabase.transferQueueDao(),
        scheduler = transferWorkScheduler
    )
    val chatScrollStateStore = ChatScrollStateStore(appContext, json)
    val incomingShareStore = IncomingShareStore()
    val incomingArchiveStore = IncomingArchiveStore()
    val incomingPushNavigationStore = IncomingPushNavigationStore()
    val incomingCallInviteStore = IncomingCallInviteStore()
    val voiceNotePlayerService = AndroidVoiceNotePlayerService()
    val voiceNoteRecorderService = AndroidVoiceNoteRecorderService(appContext)
    val realtimeService = RealtimeService(clientVersionProvider)
    val webRtcCallService = AndroidWebRtcCallService(appContext)
    val callTonePlayer = AndroidCallTonePlayer()
    val callManager = CallManager(
        realtimeService = realtimeService,
        authorizedSessionManager = authorizedSessionManager,
        webRtcCallService = webRtcCallService,
        callTonePlayer = callTonePlayer,
        appContext = appContext
    )
    val pushRegistrationManager = PushRegistrationManager(
        context = appContext,
        apiClient = apiClient,
        sessionStore = sessionStore,
        authorizedSessionManager = authorizedSessionManager,
        settings = pushSettings
    )

    init {
        applicationScope.launch {
            appSettings.snapshot.collect { snapshot ->
                activeHttpClient = buildHttpClient(snapshot)
                runCatching {
                    attachmentFileService.enforceCachePolicy(
                        maxCacheMb = snapshot.maxCacheMb,
                        keepMediaDays = snapshot.keepMediaDays
                    )
                }
            }
        }
    }

    suspend fun <T> executeAuthorized(block: suspend (AuthSessionDto) -> T): T =
        authorizedSessionManager.executeAuthorized(block)

    private fun buildHttpClient(settings: AppSettingsSnapshot): OkHttpClient {
        val proxy = buildProxy(settings)
        return OkHttpClient.Builder()
            .addInterceptor(loggingInterceptor)
            .apply {
                if (proxy != null) {
                    proxy(proxy)
                }
            }
            .build()
    }

    private fun buildProxy(settings: AppSettingsSnapshot): Proxy? {
        val host = settings.proxyHost.trim()
        if (!settings.proxyEnabled || host.isBlank()) {
            return null
        }

        val proxyType = when (settings.proxyType) {
            AppProxyType.Http -> Proxy.Type.HTTP
            AppProxyType.Socks5,
            AppProxyType.MtProto -> Proxy.Type.SOCKS
        }
        return Proxy(proxyType, InetSocketAddress(host, settings.proxyPort))
    }
}
