package com.rosetta.messenger.update

import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.util.Log
import androidx.core.content.FileProvider
import com.rosetta.messenger.BuildConfig
import com.rosetta.messenger.network.PacketRequestUpdate
import com.rosetta.messenger.network.ProtocolManager
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import okhttp3.OkHttpClient
import okhttp3.Request
import java.io.File
import java.util.concurrent.TimeUnit

/**
 * Менеджер обновлений приложения через SDU (Software Distribution Unit).
 *
 * Аналог UpdateProvider из desktop-приложения.
 * Поток:
 * 1. После аутентификации отправляем PacketRequestUpdate (0x0A) для получения URL SDU сервера
 * 2. Запрашиваем /updates/get?kernel={version}&platform=android&arch={arch} на SDU
 * 3. Если есть обновление — показываем пользователю
 * 4. Скачиваем APK и открываем установщик
 */
object UpdateManager {
    private const val TAG = "UpdateManager"

    private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())

    // ═══ Debug log buffer ═══
    private val _debugLogs = MutableStateFlow<List<String>>(emptyList())
    val debugLogs: StateFlow<List<String>> = _debugLogs.asStateFlow()
    private val timeFmt = SimpleDateFormat("HH:mm:ss.SSS", Locale.US)

    private fun sduLog(msg: String) {
        Log.i(TAG, msg)
        val ts = timeFmt.format(Date())
        val line = "[$ts] $msg"
        _debugLogs.value = (_debugLogs.value + line).takeLast(200)
    }

    // SDU server URL, получаем от основного сервера через PacketRequestUpdate
    @Volatile
    private var sduServerUrl: String? = null

    // Текущая версия приложения
    val appVersion: String = BuildConfig.VERSION_NAME

    // Состояния обновления
    private val _updateState = MutableStateFlow<UpdateState>(UpdateState.Idle)
    val updateState: StateFlow<UpdateState> = _updateState.asStateFlow()

    private val _downloadProgress = MutableStateFlow(0)
    val downloadProgress: StateFlow<Int> = _downloadProgress.asStateFlow()

    // Информация о доступном обновлении
    @Volatile
    private var latestUpdateInfo: UpdateInfo? = null

    private var downloadJob: Job? = null

    private val httpClient = OkHttpClient.Builder()
        .connectTimeout(30, TimeUnit.SECONDS)
        .readTimeout(60, TimeUnit.SECONDS)
        .build()

    /**
     * Инициализация: регистрируем обработчик пакета 0x0A и запрашиваем SDU сервер
     */
    fun init() {
        sduLog("init() called, appVersion=$appVersion")
        sduLog("Registering waitPacket(0x0A) listener...")
        ProtocolManager.waitPacket(0x0A) { packet ->
            sduLog("Received packet 0x0A, type=${packet::class.simpleName}")
            if (packet is PacketRequestUpdate) {
                val server = packet.updateServer
                sduLog("PacketRequestUpdate.updateServer='$server'")
                if (server.isNotEmpty()) {
                    sduServerUrl = server
                    sduLog("SDU server set: $server")
                    scope.launch {
                        sduLog("Auto-checking for updates...")
                        checkForUpdates()
                    }
                } else {
                    sduLog("WARNING: updateServer is empty!")
                }
            } else {
                sduLog("WARNING: packet is not PacketRequestUpdate, got ${packet::class.simpleName}")
            }
        }
        sduLog("init() complete")
    }

    /**
     * Запросить URL SDU сервера у основного сервера
     */
    fun requestSduServer() {
        sduLog("requestSduServer() — sending PacketRequestUpdate with empty updateServer")
        val packet = PacketRequestUpdate().apply {
            updateServer = ""
        }
        ProtocolManager.sendPacket(packet)
        sduLog("PacketRequestUpdate sent")
    }

    /**
     * Проверить наличие обновлений на SDU сервере
     */
    suspend fun checkForUpdates(): UpdateInfo? = withContext(Dispatchers.IO) {
        sduLog("checkForUpdates() called")
        val sdu = sduServerUrl
        if (sdu == null) {
            sduLog("ERROR: sduServerUrl is null! Requesting again...")
            requestSduServer()
            return@withContext null
        }

        _updateState.value = UpdateState.Checking
        sduLog("State -> Checking")

        try {
            val arch = Build.SUPPORTED_ABIS.firstOrNull() ?: "arm64-v8a"
            val url = "$sdu/updates/all?app=$appVersion&kernel=$appVersion&platform=android&arch=$arch"
            sduLog("HTTP GET: $url")

            val request = Request.Builder().url(url).get().build()
            val response = httpClient.newCall(request).execute()

            sduLog("HTTP response: ${response.code} ${response.message}")

            if (!response.isSuccessful) {
                sduLog("ERROR: SDU returned ${response.code}")
                _updateState.value = UpdateState.Error("Server returned ${response.code}")
                return@withContext null
            }

            val body = response.body?.string() ?: run {
                sduLog("ERROR: Empty response body")
                _updateState.value = UpdateState.Error("Empty response")
                return@withContext null
            }

            sduLog("Response body: $body")
            val info = parseUpdateResponse(body)
            latestUpdateInfo = info
            sduLog("Parsed: version=${info.version}, servicePackUrl=${info.servicePackUrl}, kernelUpdateRequired=${info.kernelUpdateRequired}")

            when {
                info.servicePackUrl != null -> {
                    sduLog("State -> UpdateAvailable(${info.version})")
                    _updateState.value = UpdateState.UpdateAvailable(info.version)
                }
                else -> {
                    sduLog("State -> UpToDate")
                    _updateState.value = UpdateState.UpToDate
                }
            }

            return@withContext info
        } catch (e: Exception) {
            sduLog("EXCEPTION: ${e::class.simpleName}: ${e.message}")
            _updateState.value = UpdateState.Error(e.message ?: "Unknown error")
            return@withContext null
        }
    }

    /**
     * Скачать и установить обновление
     */
    fun downloadAndInstall(context: Context) {
        val info = latestUpdateInfo ?: return
        val sdu = sduServerUrl ?: return
        val url = info.servicePackUrl ?: return

        downloadJob?.cancel()
        downloadJob = scope.launch {
            _updateState.value = UpdateState.Downloading(0)
            _downloadProgress.value = 0

            try {
                val fullUrl = if (url.startsWith("http")) url else "$sdu$url"
                Log.i(TAG, "Downloading update from: $fullUrl")

                val request = Request.Builder().url(fullUrl).get().build()
                val response = httpClient.newCall(request).execute()

                if (!response.isSuccessful) {
                    _updateState.value = UpdateState.Error("Download failed: ${response.code}")
                    return@launch
                }

                val responseBody = response.body ?: run {
                    _updateState.value = UpdateState.Error("Empty download response")
                    return@launch
                }

                val totalBytes = responseBody.contentLength()
                val apkFile = File(context.cacheDir, "Rosetta-${info.version}.apk")

                responseBody.byteStream().use { input ->
                    apkFile.outputStream().use { output ->
                        val buffer = ByteArray(8192)
                        var bytesRead: Long = 0
                        var read: Int

                        while (input.read(buffer).also { read = it } != -1) {
                            if (!isActive) {
                                apkFile.delete()
                                return@launch
                            }
                            output.write(buffer, 0, read)
                            bytesRead += read
                            if (totalBytes > 0) {
                                val progress = ((bytesRead * 100) / totalBytes).toInt()
                                _downloadProgress.value = progress
                                _updateState.value = UpdateState.Downloading(progress)
                            }
                        }
                    }
                }

                Log.i(TAG, "Download complete: ${apkFile.absolutePath}")
                _updateState.value = UpdateState.ReadyToInstall(apkFile.absolutePath)

                // Запускаем установку APK
                installApk(context, apkFile)

            } catch (e: CancellationException) {
                Log.i(TAG, "Download cancelled")
                _updateState.value = UpdateState.UpdateAvailable(info.version)
            } catch (e: Exception) {
                Log.e(TAG, "Download failed", e)
                _updateState.value = UpdateState.Error("Download failed: ${e.message}")
            }
        }
    }

    /**
     * Открыть системный установщик APK
     */
    private fun installApk(context: Context, apkFile: File) {
        try {
            val uri: Uri = FileProvider.getUriForFile(
                context,
                "${context.packageName}.provider",
                apkFile
            )

            val intent = Intent(Intent.ACTION_VIEW).apply {
                setDataAndType(uri, "application/vnd.android.package-archive")
                flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION
            }

            context.startActivity(intent)
        } catch (e: Exception) {
            Log.e(TAG, "Failed to launch installer", e)
            _updateState.value = UpdateState.Error("Failed to open installer: ${e.message}")
        }
    }

    /**
     * Отменить текущее скачивание
     */
    fun cancelDownload() {
        downloadJob?.cancel()
        downloadJob = null
        val info = latestUpdateInfo
        _updateState.value = if (info?.servicePackUrl != null)
            UpdateState.UpdateAvailable(info.version) else UpdateState.Idle
        _downloadProgress.value = 0
    }

    /**
     * Сбросить состояние
     */
    fun reset() {
        _updateState.value = UpdateState.Idle
        _downloadProgress.value = 0
        latestUpdateInfo = null
    }

    /**
     * Парсинг JSON ответа от SDU /updates/all
     * Формат: {"items":[{"platform":"android","arch":"x64","version":"1.0.7","downloadUrl":"/kernel/android/x64/Rosetta-1.0.7.apk"}, ...]}
     * Ищем запись с platform=android, берём самую новую версию
     */
    private fun parseUpdateResponse(json: String): UpdateInfo {
        val obj = org.json.JSONObject(json)
        val items = obj.optJSONArray("items")

        if (items == null || items.length() == 0) {
            sduLog("No items in response, trying legacy format...")
            // Fallback на старый формат /updates/get
            return UpdateInfo(
                version = obj.optString("version", ""),
                platform = obj.optString("platform", ""),
                arch = obj.optString("arch", ""),
                kernelUpdateRequired = obj.optBoolean("kernel_update_required", false),
                servicePackUrl = obj.optString("service_pack_url", "").takeIf { it.isNotEmpty() && it != "null" },
                kernelUrl = obj.optString("kernel_url", "").takeIf { it.isNotEmpty() && it != "null" }
            )
        }

        // Ищем все android-записи
        var bestVersion: String? = null
        var bestDownloadUrl: String? = null
        var bestArch: String? = null

        val deviceArch = Build.SUPPORTED_ABIS.firstOrNull() ?: "arm64-v8a"
        // Маппинг ABI -> SDU arch
        val sduArch = when {
            deviceArch.contains("arm64") -> "arm64"
            deviceArch.contains("arm") -> "arm"
            deviceArch.contains("x86_64") -> "x64"
            deviceArch.contains("x86") -> "x86"
            else -> deviceArch
        }

        sduLog("Looking for platform=android, preferring arch=$sduArch (device ABI=$deviceArch)")

        for (i in 0 until items.length()) {
            val item = items.getJSONObject(i)
            val platform = item.optString("platform", "")
            if (platform != "android") continue

            val itemArch = item.optString("arch", "")
            val itemVersion = item.optString("version", "")
            val itemUrl = item.optString("downloadUrl", "")

            sduLog("Found android entry: arch=$itemArch, version=$itemVersion, url=$itemUrl")

            // Берём самую новую версию (предпочитаем совпадение по arch)
            if (bestVersion == null || compareVersions(itemVersion, bestVersion) > 0 ||
                (compareVersions(itemVersion, bestVersion) == 0 && itemArch == sduArch)) {
                bestVersion = itemVersion
                bestDownloadUrl = itemUrl
                bestArch = itemArch
            }
        }

        if (bestVersion == null) {
            sduLog("No android entries found in items")
            return UpdateInfo("", "android", "", false, null, null)
        }

        sduLog("Best android update: version=$bestVersion, arch=$bestArch, url=$bestDownloadUrl")

        // Сравниваем с текущей версией
        val isNewer = compareVersions(bestVersion, appVersion) > 0
        sduLog("Current=$appVersion, available=$bestVersion, isNewer=$isNewer")

        return UpdateInfo(
            version = bestVersion,
            platform = "android",
            arch = bestArch ?: "",
            kernelUpdateRequired = false,
            servicePackUrl = if (isNewer) bestDownloadUrl else null,
            kernelUrl = null
        )
    }

    /**
     * Сравнение версий: "1.0.7" vs "1.0.6" -> 1 (первая больше)
     */
    private fun compareVersions(v1: String, v2: String): Int {
        val parts1 = v1.split(".").map { it.toIntOrNull() ?: 0 }
        val parts2 = v2.split(".").map { it.toIntOrNull() ?: 0 }
        val maxLen = maxOf(parts1.size, parts2.size)
        for (i in 0 until maxLen) {
            val p1 = parts1.getOrElse(i) { 0 }
            val p2 = parts2.getOrElse(i) { 0 }
            if (p1 != p2) return p1.compareTo(p2)
        }
        return 0
    }
}

/**
 * Информация об обновлении от SDU сервера
 */
data class UpdateInfo(
    val version: String,
    val platform: String,
    val arch: String,
    val kernelUpdateRequired: Boolean,
    val servicePackUrl: String?,
    val kernelUrl: String?
)

/**
 * Состояния процесса обновления
 */
sealed class UpdateState {
    /** Начальное состояние */
    data object Idle : UpdateState()

    /** Проверяем наличие обновлений */
    data object Checking : UpdateState()

    /** Приложение обновлено */
    data object UpToDate : UpdateState()

    /** Доступно обновление */
    data class UpdateAvailable(val version: String) : UpdateState()

    /** Скачивание APK */
    data class Downloading(val progress: Int) : UpdateState()

    /** Готово к установке */
    data class ReadyToInstall(val apkPath: String) : UpdateState()

    /** Ошибка */
    data class Error(val message: String) : UpdateState()
}
