package com.tomhempel.unityudp.ui

import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.tomhempel.unityudp.data.ProjectData
import com.tomhempel.unityudp.data.ReceivedMessage
import com.tomhempel.unityudp.data.StorageService
import com.tomhempel.unityudp.data.UdpPackageData
import com.tomhempel.unityudp.data.UdpService
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch

sealed class Screen {
    data object Home : Screen()
    data class Project(val projectId: String) : Screen()
}

data class SendResult(
    val message: String,
    val isSuccess: Boolean,
    val isPartial: Boolean = false
)

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

    private val storageService = StorageService(application)
    private val udpService = UdpService()

    private val _projects = MutableStateFlow<List<ProjectData>>(emptyList())
    val projects: StateFlow<List<ProjectData>> = _projects.asStateFlow()

    private val _screen = MutableStateFlow<Screen>(Screen.Home)
    val screen: StateFlow<Screen> = _screen.asStateFlow()

    private val _isSending = MutableStateFlow(false)
    val isSending: StateFlow<Boolean> = _isSending.asStateFlow()

    private val _lastSentPackageId = MutableStateFlow<String?>(null)
    val lastSentPackageId: StateFlow<String?> = _lastSentPackageId.asStateFlow()

    private val _sendResult = MutableStateFlow<SendResult?>(null)
    val sendResult: StateFlow<SendResult?> = _sendResult.asStateFlow()

    // Listener state
    private val _isListening = MutableStateFlow(false)
    val isListening: StateFlow<Boolean> = _isListening.asStateFlow()

    private val _listenError = MutableStateFlow<String?>(null)
    val listenError: StateFlow<String?> = _listenError.asStateFlow()

    private val _receivedMessages = MutableStateFlow<List<ReceivedMessage>>(emptyList())
    val receivedMessages: StateFlow<List<ReceivedMessage>> = _receivedMessages.asStateFlow()

    private var listenJob: Job? = null

    init {
        loadProjects()
    }

    override fun onCleared() {
        super.onCleared()
        udpService.stopListening()
    }

    private fun loadProjects() {
        _projects.value = storageService.loadProjects()
    }

    private fun saveProjects() {
        storageService.saveProjects(_projects.value)
    }

    fun navigateToProject(projectId: String) {
        _screen.value = Screen.Project(projectId)
    }

    fun navigateHome() {
        _screen.value = Screen.Home
    }

    fun getProject(projectId: String): ProjectData? =
        _projects.value.find { it.id == projectId }

    fun addProject(project: ProjectData) {
        _projects.value = _projects.value + project
        saveProjects()
    }

    fun updateProject(project: ProjectData) {
        _projects.value = _projects.value.map {
            if (it.id == project.id) project else it
        }
        saveProjects()
    }

    fun deleteProject(projectId: String) {
        _projects.value = _projects.value.filter { it.id != projectId }
        saveProjects()
    }

    fun addPackage(projectId: String, pkg: UdpPackageData) {
        val project = getProject(projectId) ?: return
        updateProject(project.copyWith(packages = project.packages + pkg))
    }

    fun updatePackage(projectId: String, pkg: UdpPackageData) {
        val project = getProject(projectId) ?: return
        updateProject(project.copyWith(
            packages = project.packages.map { if (it.id == pkg.id) pkg else it }
        ))
    }

    fun deletePackage(projectId: String, packageId: String) {
        val project = getProject(projectId) ?: return
        updateProject(project.copyWith(
            packages = project.packages.filter { it.id != packageId }
        ))
    }

    fun copyPackage(projectId: String, pkg: UdpPackageData) {
        val copy = pkg.copyWith(
            id = System.currentTimeMillis().toString(),
            name = "${pkg.name} (Copy)"
        )
        addPackage(projectId, copy)
    }

    fun sendPackage(project: ProjectData, pkg: UdpPackageData) {
        viewModelScope.launch {
            _isSending.value = true
            _sendResult.value = null

            val isBroadcast = pkg.isBroadcast || (project.isBroadcast && pkg.ipAddresses.isEmpty())
            val ipAddresses = if (pkg.ipAddresses.isNotEmpty()) pkg.ipAddresses else project.ipAddresses

            val results = udpService.sendAdvanced(
                data = pkg.data,
                port = project.port,
                isBroadcast = isBroadcast,
                ipAddresses = if (isBroadcast) emptyList() else ipAddresses
            )

            val successCount = results.values.count { it }
            val totalCount = results.size
            val allSuccess = successCount == totalCount

            _isSending.value = false

            val result = when {
                allSuccess -> {
                    _lastSentPackageId.value = pkg.id
                    kotlinx.coroutines.delay(2000)
                    _lastSentPackageId.value = null
                    val msg = when {
                        isBroadcast -> "\"${pkg.name}\" broadcast successfully!"
                        totalCount > 1 -> "\"${pkg.name}\" sent to $totalCount addresses!"
                        else -> "\"${pkg.name}\" sent successfully!"
                    }
                    SendResult(msg, isSuccess = true)
                }
                successCount > 0 -> SendResult(
                    "\"${pkg.name}\" sent to $successCount of $totalCount addresses",
                    isSuccess = true,
                    isPartial = true
                )
                else -> SendResult("Failed to send \"${pkg.name}\"", isSuccess = false)
            }

            _sendResult.value = result
        }
    }

    fun clearSendResult() {
        _sendResult.value = null
    }

    // ── Listener ──────────────────────────────────────────────────────────────

    fun startListening(port: Int) {
        if (_isListening.value) return
        _listenError.value = null
        listenJob = viewModelScope.launch {
            _isListening.value = true
            try {
                udpService.startListening(port) { data, senderIp, senderPort ->
                    val msg = ReceivedMessage(
                        id = "${System.currentTimeMillis()}_${senderIp}_${senderPort}",
                        data = data,
                        senderIp = senderIp,
                        port = senderPort
                    )
                    _receivedMessages.value = listOf(msg) + _receivedMessages.value
                }
            } catch (e: Exception) {
                _listenError.value = e.message ?: "Failed to start listener"
            }
            _isListening.value = false
        }
    }

    fun stopListening() {
        udpService.stopListening()
        listenJob?.cancel()
        listenJob = null
        _isListening.value = false
    }

    fun clearReceivedMessages() {
        _receivedMessages.value = emptyList()
    }

    fun clearListenError() {
        _listenError.value = null
    }
}
