package com.tomhempel.rbarometer

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.os.Build
import android.os.Bundle
import android.content.Intent
import androidx.activity.OnBackPressedCallback
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import com.tomhempel.rbarometer.LanguageManager
import com.tomhempel.rbarometer.ui.AppLanguageStore
import com.tomhempel.rbarometer.ui.NotificationPermissionPrompter
import com.tomhempel.rbarometer.ui.AuthActivity
import com.tomhempel.rbarometer.ui.HandlerOpeningScreen
import com.tomhempel.rbarometer.ui.LogoutDialogs
import com.tomhempel.rbarometer.ui.SessionLogout
import com.tomhempel.rbarometer.ui.SystemBarInsets
import com.tomhempel.rbarometer.utils.SyncProgressOverlay
import com.tomhempel.rbarometer.utils.SyncProgressTexts
import com.tomhempel.rbarometer.utils.ViewUtils
import com.tomhempel.rbarometer.data.PendingUploadRepository
import com.tomhempel.rbarometer.network.NetworkUtils
import com.tomhempel.rbarometer.network.QuestionnaireApiClient
import com.tomhempel.rbarometer.network.SyncLog
import com.tomhempel.rbarometer.network.QuestionnaireCache
import com.tomhempel.rbarometer.network.SyncCoordinator
import com.tomhempel.rbarometer.network.SyncProgress
import com.tomhempel.rbarometer.network.SyncResult
import com.tomhempel.rbarometer.network.TokenStore
import com.tomhempel.rbarometer.questionnaire.QuestionnaireBase
import com.tomhempel.rbarometer.questionnaire.QuestionnaireLanguagePicker
import com.tomhempel.rbarometer.session.SessionFlowController
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class MainActivity : AppCompatActivity() {

    private companion object {
        private const val STARTUP_SYNC_PREFS = "startup_sync_state"
        private const val KEY_LAST_STARTUP_SYNC_MS = "last_startup_sync_ms"
        private const val STARTUP_SYNC_INTERVAL_MS = 30L * 60L * 1000L
    }

    private lateinit var openingScreenHandler: HandlerOpeningScreen

    var isInQuestionnaire: Boolean = false
    private var sessionFlowController: SessionFlowController? = null
    private var activeQuestionnaire: QuestionnaireBase<*>? = null

    val isInSessionFlow: Boolean get() = sessionFlowController != null
    var isFirstQuestionnairePage: Boolean = false
    /** Set before opening DevSettings so onResume runs a full opening-screen rebuild. */
    private var needsOpeningReinit: Boolean = false

    private var assignedClientsRefreshGeneration: Long = 0L

    fun markOpeningScreenReinit() {
        needsOpeningReinit = true
    }

    fun bumpAssignedClientsRefreshGeneration() {
        assignedClientsRefreshGeneration++
    }

    private var syncProgressOverlay: SyncProgressOverlay? = null
    private var backPressInterceptor: (() -> Boolean)? = null
    private var notificationPromptRequested = false

    private val requestNotificationPermission =
        registerForActivityResult(ActivityResultContracts.RequestPermission()) { /* no-op */ }

    private val authLauncher = registerForActivityResult(
        ActivityResultContracts.StartActivityForResult()
    ) { result ->
        if (result.resultCode != RESULT_OK) {
            finishAffinity()
            return@registerForActivityResult
        }
        val token = result.data?.getStringExtra(AuthActivity.EXTRA_TOKEN)
        if (token.isNullOrBlank()) {
            finishAffinity()
            return@registerForActivityResult
        }
        proceedAfterLogin(token)
    }

    // LIVE: Network-Callback (optional für Statusleiste)
    private var netCb: ConnectivityManager.NetworkCallback? = null

    private val bootLanguageId: String get() = AppLanguageStore.get(this)
    private fun t(key: String): String = LanguageManager.getText(bootLanguageId, key)

    override fun onCreate(savedInstanceState: Bundle?) {
        enableEdgeToEdge()
        super.onCreate(savedInstanceState)

        onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                when {
                    isInQuestionnaire -> confirmExitQuestionnaire()
                    isInSessionFlow -> sessionFlowController?.pauseFromBackPress()
                    backPressInterceptor?.invoke() == true -> Unit
                    ::openingScreenHandler.isInitialized && openingScreenHandler.onBackPressed() -> Unit
                    else -> {
                        isEnabled = false
                        onBackPressedDispatcher.onBackPressed()
                    }
                }
            }
        })

        TokenStore.ensureLegacyLoginTimestamp(this)
        setContentView(R.layout.activity_startup_loading)

        // === Offline-Start ermöglichen ===
        // Bedingung: gespeicherter User/Token UND lokaler API-Cache vorhanden -> direkt OpeningScreen
        val hasCreds = !TokenStore.getUsername(this).isNullOrBlank() && TokenStore.hasToken(this)
        val hasCache = QuestionnaireCache.hasQuestionnaireCache(this)
        SyncLog.d("MAIN", "onCreate: hasCreds=$hasCreds, hasCache=$hasCache")
        SyncLog.d("MAIN", "onCreate: cached assigned clients count=${QuestionnaireCache.getAssignedClients(this).size}")
        if (hasCreds && hasCache) {
            validateStoredSessionThenOpen()
            return
        }

        SyncCoordinator.loadCachedTranslations(this)
        CoroutineScope(Dispatchers.Main).launch {
            SyncCoordinator.refreshPublicTranslations(this@MainActivity)
        }

        // Sonst: Login-Screen -> Login -> Daten laden -> OpeningScreen
        launchAuthScreen()
    }

    private fun validateStoredSessionThenOpen() {
        val token = TokenStore.getToken(this)
        if (token.isNullOrBlank() || !TokenStore.isSessionValid(this)) {
            requestLogin()
            return
        }
        if (!NetworkUtils.isOnline(this)) {
            showCachedOpeningScreen(refreshAfterOpen = false)
            return
        }
        CoroutineScope(Dispatchers.Main).launch {
            val sessionCheck = withContext(Dispatchers.IO) {
                runCatching { QuestionnaireApiClient.getSession(token) }
            }
            val session = sessionCheck.getOrNull()
            val error = sessionCheck.exceptionOrNull()
            if (session != null && session.valid && session.role == "coach" && !session.mustChangePassword) {
                routeCachedStartup(token)
            } else if (error != null && TokenStore.isAuthError(error)) {
                requestLogin()
            } else if (error == null) {
                requestLogin()
            } else {
                // Network/server transient failure: keep offline-capable cached mode.
                showCachedOpeningScreen(refreshAfterOpen = false)
            }
        }
    }

    private suspend fun routeCachedStartup(token: String) {
        val hasPendingUploads = withContext(Dispatchers.IO) {
            PendingUploadRepository.hasUploadReady(this@MainActivity)
        }
        if (hasPendingUploads || !NetworkUtils.isOnline(this)) {
            showCachedOpeningScreen(refreshAfterOpen = false, showUploadPrompt = hasPendingUploads)
            return
        }
        if (!shouldRunStartupSync()) {
            showCachedOpeningScreen(refreshAfterOpen = false)
            return
        }

        showSyncProgressOverlay(bootLanguageId)
        val syncResult = withContext(Dispatchers.IO) {
            SyncCoordinator.refreshManual(
                context = this@MainActivity,
                token = token,
                onProgress = { reportSyncProgress(it) },
            )
        }
        hideSyncProgressOverlay()

        when (syncResult) {
            SyncResult.AuthRequired -> {
                requestLogin()
                return
            }
            else -> {
                if (syncResult is SyncResult.Success) {
                    markSuccessfulSync()
                }
                showCachedOpeningScreen(refreshAfterOpen = false)
                if (syncResult is SyncResult.Failure) {
                    SyncCoordinator.toastForResult(this, bootLanguageId, syncResult)
                }
            }
        }
    }

    private fun shouldRunStartupSync(): Boolean {
        val lastSyncAt = getSharedPreferences(STARTUP_SYNC_PREFS, Context.MODE_PRIVATE)
            .getLong(KEY_LAST_STARTUP_SYNC_MS, 0L)
        return lastSyncAt <= 0L ||
            System.currentTimeMillis() - lastSyncAt >= STARTUP_SYNC_INTERVAL_MS
    }

    fun markSuccessfulSync() {
        getSharedPreferences(STARTUP_SYNC_PREFS, Context.MODE_PRIVATE)
            .edit()
            .putLong(KEY_LAST_STARTUP_SYNC_MS, System.currentTimeMillis())
            .apply()
    }

    private fun showCachedOpeningScreen(refreshAfterOpen: Boolean, showUploadPrompt: Boolean = false) {
        SyncCoordinator.loadCachedTranslations(this)
        openingScreenHandler = HandlerOpeningScreen(this)
        openingScreenHandler.init()
        if (showUploadPrompt) {
            openingScreenHandler.runAfterFirstClientLoad {
                openingScreenHandler.showStartupUploadPrompt()
            }
        }
        if (refreshAfterOpen) {
            openingScreenHandler.runAfterFirstClientLoad {
                refreshAssignedClientsInBackground()
            }
        }
        scheduleNotificationPermissionPrompt()
    }

    /** Refreshes assigned clients from the server when online (e.g. after app restart). */
    private fun refreshAssignedClientsInBackground() {
        val token = TokenStore.getToken(this) ?: return
        if (!NetworkUtils.isOnline(this)) {
            SyncLog.d("MAIN", "refreshAssignedClients skipped: offline")
            return
        }
        val generation = assignedClientsRefreshGeneration
        CoroutineScope(Dispatchers.IO).launch {
            when (val result = SyncCoordinator.refreshClientsOnly(this@MainActivity, token)) {
                SyncResult.AuthRequired -> {
                    withContext(Dispatchers.Main) { requestLogin() }
                    return@launch
                }
                SyncResult.Success -> {
                    withContext(Dispatchers.Main) {
                        if (generation != assignedClientsRefreshGeneration) return@withContext
                        if (::openingScreenHandler.isInitialized && !isInQuestionnaire) {
                            openingScreenHandler.onAssignedClientsSynced()
                        }
                    }
                }
                else -> {}
            }
        }
    }

    /**
     * Clears all local data and shows the login screen (e.g. expired token).
     * Does not check pending uploads — session is already invalid.
     */
    fun requestLogin() {
        isInQuestionnaire = false
        CoroutineScope(Dispatchers.Main).launch {
            val previousUser = TokenStore.getUsername(this@MainActivity)
            withContext(Dispatchers.IO) { SessionLogout.wipeAllLocalData(this@MainActivity) }
            launchAuthScreen(prefillUsername = previousUser)
        }
    }

    /** User-initiated logout from settings; blocked when offline or unuploaded data remains. */
    fun attemptLogout(languageId: String? = null) {
        val lang = languageId ?: bootLanguageId
        CoroutineScope(Dispatchers.Main).launch {
            val block = withContext(Dispatchers.IO) { SessionLogout.evaluate(this@MainActivity) }
            when (block) {
                null -> LogoutDialogs.showConfirm(this@MainActivity, lang) {
                    CoroutineScope(Dispatchers.Main).launch {
                        withContext(Dispatchers.IO) { SessionLogout.wipeAllLocalData(this@MainActivity) }
                        isInQuestionnaire = false
                        launchAuthScreen()
                    }
                }
                SessionLogout.BlockReason.Offline -> LogoutDialogs.showBlocked(
                    activity = this@MainActivity,
                    languageId = lang,
                    title = SessionLogout.message(lang, "logout_offline_title", "Cannot log out"),
                    message = SessionLogout.message(
                        lang,
                        "logout_offline_message",
                        "You are offline. Connect to the internet, upload pending data, then try again.",
                    ),
                    tone = LogoutDialogs.Tone.Info,
                )
                is SessionLogout.BlockReason.UnuploadedWork -> {
                    val details = SessionLogout.formatPendingDetails(lang, block.summary)
                    val bodyTemplate = SessionLogout.message(
                        lang,
                        "logout_pending_message",
                        "You still have data on this device that has not been uploaded.\n\n{details}\n\nPlease tap Upload and wait until it finishes.",
                    )
                    val message = bodyTemplate.replace("{details}", "").trim()
                    LogoutDialogs.showBlocked(
                        activity = this@MainActivity,
                        languageId = lang,
                        title = SessionLogout.message(lang, "logout_pending_title", "Cannot log out"),
                        message = message,
                        details = details,
                        tone = LogoutDialogs.Tone.Warning,
                    )
                }
            }
        }
    }

    private fun launchAuthScreen(prefillUsername: String? = null) {
        val intent = Intent(this, AuthActivity::class.java)
        prefillUsername?.takeIf { it.isNotBlank() }?.let {
            intent.putExtra(AuthActivity.EXTRA_PREFILL_USERNAME, it)
        }
        authLauncher.launch(intent)
    }

    fun showSyncProgressOverlay(languageId: String? = null) {
        val locale = SyncProgressTexts.Locale.fromLanguageId(languageId ?: bootLanguageId)
        ensureSyncProgressOverlay().setLocale(locale)
        ensureSyncProgressOverlay().show()
    }

    fun hideSyncProgressOverlay() {
        syncProgressOverlay?.hide()
    }

    fun reportSyncProgress(progress: SyncProgress) {
        runOnUiThread { ensureSyncProgressOverlay().update(progress) }
    }

    private fun ensureSyncProgressOverlay(): SyncProgressOverlay {
        if (syncProgressOverlay == null) {
            syncProgressOverlay = SyncProgressOverlay(this)
        }
        return syncProgressOverlay!!
    }

    private fun proceedAfterLogin(token: String) {
        SyncLog.d("MAIN", "Login success")
        SyncLog.d("MAIN", "Assigned clients from cache after login count=${QuestionnaireCache.getAssignedClients(this).size}")
        showSyncProgressOverlay(bootLanguageId)
        CoroutineScope(Dispatchers.IO).launch {
            val syncResult = SyncCoordinator.refreshAfterLogin(
                context = this@MainActivity,
                token = token,
                languageId = bootLanguageId,
                onProgress = { reportSyncProgress(it) },
            )

            withContext(Dispatchers.Main) {
                hideSyncProgressOverlay()
                when (syncResult) {
                    SyncResult.AuthRequired -> {
                        requestLogin()
                        return@withContext
                    }
                    is SyncResult.Failure -> {
                        SyncCoordinator.toastForResult(this@MainActivity, bootLanguageId, syncResult)
                    }
                    SyncResult.Success -> markSuccessfulSync()
                    else -> {}
                }
                openingScreenHandler = HandlerOpeningScreen(this@MainActivity)
                openingScreenHandler.init()
                openingScreenHandler.refreshHeaderStatusLive()
                scheduleNotificationPermissionPrompt()
            }
        }
    }

    fun setBackPressInterceptor(interceptor: (() -> Boolean)?) {
        backPressInterceptor = interceptor
    }

    private fun scheduleNotificationPermissionPrompt() {
        if (notificationPromptRequested) return
        notificationPromptRequested = true
        window.decorView.post {
            NotificationPermissionPrompter.showIfNeeded(this, requestNotificationPermission)
        }
    }

    private fun confirmExitQuestionnaire() {
        fun td(key: String, fallbackDe: String, fallbackEn: String): String {
            val raw = LanguageManager.getText(bootLanguageId, key)
            if (raw != key) return raw
            return if (bootLanguageId == "GERMAN") fallbackDe else fallbackEn
        }
        MaterialAlertDialogBuilder(this)
            .setTitle(td("questionnaire_exit_title", "Fragebogen verlassen?", "Leave questionnaire?"))
            .setMessage(
                td(
                    "questionnaire_exit_message",
                    "Nicht gespeicherte Eingaben in diesem Schritt können verloren gehen.",
                    "Unsaved answers on this screen may be lost.",
                ),
            )
            .setNegativeButton(td("cancel", "Abbrechen", "Cancel")) { d, _ -> d.dismiss() }
            .setPositiveButton(td("leave", "Verlassen", "Leave")) { _, _ -> finishQuestionnaire() }
            .show()
    }

    // --- LIVE NETZSTATUS (optional, für deine Status-Leiste) ---
    override fun onResume() {
        super.onResume()
        registerNetworkCallback()
        if (::openingScreenHandler.isInitialized && !isInQuestionnaire) {
            if (needsOpeningReinit) {
                needsOpeningReinit = false
                openingScreenHandler.init()
            } else {
                openingScreenHandler.refreshHeaderStatusLive()
            }
            if (QuestionnaireCache.getAssignedClients(this).isEmpty()) {
                refreshAssignedClientsInBackground()
            }
        }
    }

    override fun onPause() {
        super.onPause()
        unregisterNetworkCallback()
    }

    private fun registerNetworkCallback() {
        if (netCb != null) return
        val cm = getSystemService(ConnectivityManager::class.java)
        val req = NetworkRequest.Builder()
            .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
            .build()
        netCb = object : ConnectivityManager.NetworkCallback() {
            override fun onAvailable(network: Network) {
                runOnUiThread {
                    if (::openingScreenHandler.isInitialized && !isInQuestionnaire) {
                        openingScreenHandler.refreshHeaderStatusLive()
                    }
                }
            }
            override fun onLost(network: Network) {
                runOnUiThread {
                    if (::openingScreenHandler.isInitialized && !isInQuestionnaire) {
                        openingScreenHandler.refreshHeaderStatusLive()
                    }
                }
            }
            override fun onCapabilitiesChanged(network: Network, caps: NetworkCapabilities) {
                runOnUiThread {
                    if (::openingScreenHandler.isInitialized && !isInQuestionnaire) {
                        openingScreenHandler.refreshHeaderStatusLive()
                    }
                }
            }
        }
        cm.registerNetworkCallback(req, netCb!!)
    }

    private fun unregisterNetworkCallback() {
        val cb = netCb ?: return
        val cm = getSystemService(ConnectivityManager::class.java)
        cm.unregisterNetworkCallback(cb)
        netCb = null
    }
    // --- /LIVE NETZSTATUS ---

    override fun onContentChanged() {
        super.onContentChanged()
        SystemBarInsets.applyToContentRoot(this, ensureMinContentPadding = true)
    }

    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
    }

    fun startSessionFlow(clientCode: String, languageID: String) {
        sessionFlowController = SessionFlowController(this, languageID, clientCode) { completion ->
            sessionFlowController = null
            isInQuestionnaire = false
            if (::openingScreenHandler.isInitialized) {
                openingScreenHandler.refreshOpeningUi()
                if (completion != null) {
                    openingScreenHandler.showSessionCompletionDialog(completion)
                }
            }
        }
        sessionFlowController?.start()
    }

    fun startQuestionnaire(questionnaire: QuestionnaireBase<*>, languageID: String) {
        activeQuestionnaire = questionnaire
        isInQuestionnaire = true
        isFirstQuestionnairePage = true
        questionnaire.attach(this, languageID)
        questionnaire.startQuestionnaire()
    }

    fun finishQuestionnaire(wasCompleted: Boolean = false) {
        val draftSnapshot = activeQuestionnaire?.draftSnapshot()
        activeQuestionnaire = null
        QuestionnaireLanguagePicker.detach(this)
        if (sessionFlowController?.handleQuestionnaireFinished(wasCompleted, draftSnapshot) == true) {
            isInQuestionnaire = false
            isFirstQuestionnairePage = false
            ViewUtils.setTextSizeScale(1f)
            LanguageManager.clearQuestionnaireTranslations()
            return
        }
        isInQuestionnaire = false
        isFirstQuestionnairePage = false
        ViewUtils.setTextSizeScale(1f)
        LanguageManager.clearQuestionnaireTranslations()
        sessionFlowController = null
        if (::openingScreenHandler.isInitialized) {
            openingScreenHandler.refreshOpeningUi()
        }
    }
}
