package com.fmdxconnector

import android.app.Application
import android.content.Intent
import android.content.SharedPreferences
import androidx.core.content.ContextCompat
import androidx.lifecycle.AndroidViewModel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import org.json.JSONArray
import org.json.JSONObject
import java.text.SimpleDateFormat
import java.util.*

data class MainUiState(
    val hosts: List<HostState> = emptyList(),
    val activeHostId: Int? = null,
    val textConnected: Boolean = false,
    val pluginsConnected: Boolean = false,
    val globalLogs: List<LogEntry> = emptyList()
)

data class HostState(
    val id: Int,
    val address: String,
    val adminPassword: String,
    val serverName: String = ""
)

class MainViewModel(application: Application) : AndroidViewModel(application) {

    companion object {
        const val PREFS_NAME = "fmdx_prefs"
        const val HOSTS_KEY = "saved_hosts"
        val logFlow = MutableSharedFlow<Pair<String, String>>(replay = 100)
    }

    private val prefs: SharedPreferences =
        application.getSharedPreferences(PREFS_NAME, Application.MODE_PRIVATE)

    private val _uiState = MutableStateFlow(
        MainUiState(
            hosts = emptyList()
        )
    )
    val uiState: StateFlow<MainUiState> = _uiState.asStateFlow()

    private var nextHostId = 1
    private val timeFormat = SimpleDateFormat("HH:mm:ss", Locale.getDefault())

    private var lastLogMessage: String? = null
    private var lastLogTime: Long = 0

    init {
        NetworkService.logFlow = logFlow
        loadHostsFromPrefs()
    }

    private fun saveHostsToPrefs() {
        val jsonArray = JSONArray()
        for (host in _uiState.value.hosts) {
            val obj = JSONObject().apply {
                put("id", host.id)
                put("address", host.address)
                put("adminPassword", host.adminPassword)
                put("serverName", host.serverName)   // 👈 NEU
            }
            jsonArray.put(obj)
        }
        prefs.edit().putString(HOSTS_KEY, jsonArray.toString()).apply()
    }


    private fun loadHostsFromPrefs() {
        val json = prefs.getString(HOSTS_KEY, null)
        if (json == null) {
            // Erster Start: Einen leeren Host hinzufügen
            val initialHost = HostState(id = 1, address = "", adminPassword = "", serverName = "")
            _uiState.value = _uiState.value.copy(hosts = listOf(initialHost))
            nextHostId = 2
            saveHostsToPrefs()
            return
        }
        try {
            val array = JSONArray(json)
            val loadedHosts = mutableListOf<HostState>()
            for (i in 0 until array.length()) {
                val obj = array.getJSONObject(i)
                loadedHosts.add(
                    HostState(
                        id = obj.getInt("id"),
                        address = obj.getString("address"),
                        adminPassword = obj.optString("adminPassword", obj.optString("scanKey", "")),
                        serverName = obj.optString("serverName", "")   // 👈 NEU: Fallback = ""
                    )
                )
            }
            _uiState.value = _uiState.value.copy(hosts = loadedHosts)
            nextHostId = (loadedHosts.maxOfOrNull { it.id } ?: 0) + 1
        } catch (e: Exception) {
            addGlobalLog("Error loading hosts: ${e.message}", LogLevel.ERROR)
        }
    }

    fun addHost() {
        val newHost = HostState(nextHostId, "", "", "")   // 👈 serverName = ""
        nextHostId++
        _uiState.value = _uiState.value.copy(
            hosts = _uiState.value.hosts + newHost
        )
        addGlobalLog("Server added", LogLevel.SUCCESS)
        saveHostsToPrefs()
    }

    fun onServerNameChanged(hostId: Int, newName: String) {
        val newHosts = _uiState.value.hosts.map { host ->
            if (host.id == hostId) host.copy(serverName = newName) else host
        }
        _uiState.value = _uiState.value.copy(hosts = newHosts)
        saveHostsToPrefs()
    }

    fun deleteHost(hostId: Int) {
        if (_uiState.value.activeHostId == hostId) {
            addGlobalLog("Disconnecting from server...", LogLevel.INFO)
            disconnectAll()
        }

        val newHosts = _uiState.value.hosts.filter { it.id != hostId }
        _uiState.value = _uiState.value.copy(
            hosts = newHosts,
            activeHostId = if (_uiState.value.activeHostId == hostId) null else _uiState.value.activeHostId,
            textConnected = if (_uiState.value.activeHostId == hostId) false else _uiState.value.textConnected,
            pluginsConnected = if (_uiState.value.activeHostId == hostId) false else _uiState.value.pluginsConnected
        )

        addGlobalLog("Server deleted", LogLevel.SUCCESS)
        saveHostsToPrefs()
    }

    fun onAddressChanged(hostId: Int, newAddress: String) {
        val newHosts = _uiState.value.hosts.map { host ->
            if (host.id == hostId) host.copy(address = newAddress) else host
        }
        _uiState.value = _uiState.value.copy(hosts = newHosts)
        saveHostsToPrefs()
    }

    fun onAdminPasswordChanged(hostId: Int, newPassword: String) {
        val newHosts = _uiState.value.hosts.map { host ->
            if (host.id == hostId) host.copy(adminPassword = newPassword) else host
        }
        _uiState.value = _uiState.value.copy(hosts = newHosts)
        saveHostsToPrefs()
    }

    fun connectToHost(host: HostState) {
        val ctx = getApplication<Application>()

        val currentHosts = _uiState.value.hosts
        // Pager-Index ermitteln: 0 = Box #0, 1 = MEM #1, ...
        val serverIndex = currentHosts.indexOfFirst { it.id == host.id }.coerceAtLeast(0)

        val intent = Intent(ctx, NetworkService::class.java).apply {
            putExtra("HOST_ADDRESS", host.address)
            putExtra("ADMIN_PASSWORD", host.adminPassword)

            // Host-ID für LEDs / Status:
            putExtra("ACTIVE_HOST_ID", host.id)

            // Pager-Index für UDP:
            putExtra("ACTIVE_SERVER_INDEX", serverIndex)
        }

        ContextCompat.startForegroundService(ctx, intent)

        _uiState.value = _uiState.value.copy(
            activeHostId = host.id
        )
    }

    private fun startConnection(host: HostState) {
        if (host.address.isBlank()) {
            addGlobalLog("Host address is empty, cannot connect.", LogLevel.WARNING)
            return
        }
        _uiState.value = _uiState.value.copy(activeHostId = host.id)
        addGlobalLog("Connecting to ${host.address}...", LogLevel.INFO)

        val context = getApplication<Application>()
        val intent = Intent(context, NetworkService::class.java).apply {
            putExtra("HOST_ADDRESS", host.address)
            putExtra("ADMIN_PASSWORD", host.adminPassword)
            putExtra("ACTIVE_HOST_ID", host.id)
        }
        try {
            ContextCompat.startForegroundService(context, intent)
            addGlobalLog("NetworkService started for ${host.address}", LogLevel.SUCCESS)
        } catch (e: Exception) {
            addGlobalLog("ERROR starting service: ${e.message}", LogLevel.ERROR)
            _uiState.value = _uiState.value.copy(activeHostId = null)
        }
    }

    fun disconnectAll() {
        val context = getApplication<Application>()
        context.stopService(Intent(context, NetworkService::class.java))

        if (_uiState.value.activeHostId != null) {
            addGlobalLog("All connections closed", LogLevel.INFO)
            _uiState.value = _uiState.value.copy(
                activeHostId = null,
                textConnected = false,
                pluginsConnected = false
            )
        }
    }

    private var lastTextSocketLogTime: Long = 0
    private var lastPluginSocketLogTime: Long = 0

    fun updateLedState(socketName: String, isConnected: Boolean, hostId: Int) {
        val current = _uiState.value
        if (socketName == "text" && current.textConnected == isConnected) return
        if (socketName == "data_plugins" && current.pluginsConnected == isConnected) return

        val newTextConnected = if (socketName == "text") isConnected else current.textConnected
        val newPluginsConnected = if (socketName == "data_plugins") isConnected else current.pluginsConnected

        val newActiveHostId = when {
            // Wenn eine der Verbindungen (wieder)hergestellt wird, die Host-ID setzen.
            newTextConnected || newPluginsConnected -> hostId
            // Wenn beide Verbindungen getrennt sind, die Host-ID entfernen.
            else -> null
        }

        _uiState.value = current.copy(
            textConnected = newTextConnected,
            pluginsConnected = newPluginsConnected,
            activeHostId = newActiveHostId
        )

        val now = System.currentTimeMillis()
        when (socketName) {
            "text" -> {
                if (now - lastTextSocketLogTime >= 1000) {
                    addGlobalLog("WebSocket 'text' ${if (isConnected) "connected" else "disconnected"}", if (isConnected) LogLevel.SUCCESS else LogLevel.WARNING)
                    lastTextSocketLogTime = now
                }
            }
            "data_plugins" -> {
                if (now - lastPluginSocketLogTime >= 1000) {
                    addGlobalLog("WebSocket 'data_plugins' ${if (isConnected) "connected" else "disconnected"}", if (isConnected) LogLevel.SUCCESS else LogLevel.WARNING)
                    lastPluginSocketLogTime = now
                }
            }
        }
    }

    private fun addGlobalLog(message: String, level: LogLevel = LogLevel.INFO) {
        val now = System.currentTimeMillis()
        if (message == lastLogMessage && (now - lastLogTime) < 1000) return
        lastLogMessage = message
        lastLogTime = now

        val timestamp = timeFormat.format(Date())
        val logEntry = LogEntry(timestamp, level, message)
        val newGlobalLogs = (_uiState.value.globalLogs + logEntry).takeLast(100)
        _uiState.value = _uiState.value.copy(globalLogs = newGlobalLogs)
    }

    fun setActiveHost(id: Int) {
        _uiState.value = _uiState.value.copy(activeHostId = id)
    }

}