package com.laseroptek.raman.ui.screens.home.preset

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.laseroptek.raman.const.EnergyTable_10_10
import com.laseroptek.raman.const.EnergyTable_12_12
import com.laseroptek.raman.const.EnergyTable_3_15
import com.laseroptek.raman.const.EnergyTable_5_5
import com.laseroptek.raman.const.EnergyTable_7_7
import com.laseroptek.raman.const.Fluences
import com.laseroptek.raman.const.HzTable_10_10
import com.laseroptek.raman.const.HzTable_12_12
import com.laseroptek.raman.const.HzTable_3_15
import com.laseroptek.raman.const.HzTable_5_5
import com.laseroptek.raman.const.HzTable_7_7
import com.laseroptek.raman.const.KEY_YELLOW
import com.laseroptek.raman.const.PresetList
import com.laseroptek.raman.const.PulseDurations
import com.laseroptek.raman.const.RepetitionsByColorKey
import com.laseroptek.raman.const.UpDownState
import com.laseroptek.raman.data.model.Preset
import com.laseroptek.raman.utils.ext.getKey2ListForKey1
import com.laseroptek.raman.utils.ext.getValue
import com.laseroptek.raman.utils.ext.toFluenceMutableStateFlowMap
import com.laseroptek.raman.utils.ext.toMutableStateFlow
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject

@HiltViewModel
class PresetViewModel @Inject constructor(
) : ViewModel() {

    // energyTable - (PulseWidth(ms), Fluence(J/cm²)) -> Energy(J)
    // - Handpiece 타입에 따라 각각의 Energy Table로 구분
    // - Handpiece 연결시, 시리얼 명령으로 수신된 handpiece type에 따라 energyTable 업데이트
    // - 앱 실행시 저장된 테이블에서 값 해당 값 로딩 (없으면 기본값 = 상수값 = 공장초기화 값)
    private val _energyTable: MutableStateFlow<MutableMap<Pair<Float, Float>, Float>> =
        EnergyTable_5_5.toFluenceMutableStateFlowMap()
    val energyTable = _energyTable.asStateFlow()
    fun setEnergyTable(e: MutableMap<Pair<Float, Float>, Float>) {
        _energyTable.value = e
    }

    // fluenceList - handpiece type에 따른 Fluence 단계 값 (Guage 단계값 테이블)
    // - Handpiece 연결시, 시리얼 명령으로 수신된 handpiece type에 따라 energyTable 업데이트
    // - Fluences는 5_5 일때의 목록 값 (초기값: handPeiece none 상태)
    // - ref. Repetitions, PulseDurations는 fixed Table 임
    private val _fluenceList: MutableStateFlow<List<Float>> =
        Fluences.toMutableStateFlow()
    val fluenceList = _fluenceList.asStateFlow()
    fun setFluenceList(f: List<Float>) {
        viewModelScope.launch {
            _fluenceList.value = f
        }
    }

    // hzTable - (PulseWidth(ms), Fluence(J/cm²)) -> hzType(int)
    // - Handpiece 타입에 따라 각각의 Energy Table로 구분
    // - Handpiece 연결시, 시리얼 명령으로 수신된 handpiece type에 따라 hzTable 업데이트
    // - 앱 실행시 저장된 테이블에서 값 해당 값 로딩 (없으면 기본값 = 상수값 = 공장초기화 값)
    private val _hzTable: MutableStateFlow<MutableMap<Pair<Float, Float>, Int>> =
        HzTable_5_5.toFluenceMutableStateFlowMap()
    val hzTable = _hzTable.asStateFlow()
    fun setHzTable(h: MutableMap<Pair<Float, Float>, Int>) {
        _hzTable.value = h
    }

    // repetitionList - handpiece type에 따른 Repetition 단계 값 (Guage 단계값 테이블)
    // - Handpiece 연결시, 시리얼 명령으로 수신된 handpiece type에 따라 hzTable 업데이트
    // - Repetition은 5_5 일때의 목록 값 (초기값: handPeiece none 상태)
    // - ref. PulseDurations는 fixed Table 임
    private val _repetitionList: MutableStateFlow<List<Float>> =
        RepetitionsByColorKey[KEY_YELLOW]!!.toMutableStateFlow()
    val repetitionList = _repetitionList.asStateFlow()
    fun setRepetitionList(r: List<Float>) {
        _repetitionList.value = r
    }

    fun loadHzTable(handPieceType: Int) {
        Timber.d("loadHzTable($handPieceType)")

        val hzTable = when (handPieceType) {
            2 -> HzTable_7_7
            3 -> HzTable_10_10
            4 -> HzTable_12_12
            5 -> HzTable_3_15
            else -> HzTable_5_5
        }

        setHzTable( hzTable.mapValues { (_, fValue) -> fValue }.toMutableMap() )
    }

    fun loadFluenceTable(handPieceType: Int) {
        Timber.d("loadFluenceTable($handPieceType)")

        val energyTable = when (handPieceType) {
            2 -> EnergyTable_7_7
            3 -> EnergyTable_10_10
            4 -> EnergyTable_12_12
            5 -> EnergyTable_3_15
            else -> EnergyTable_5_5
        }

        setEnergyTable( energyTable.mapValues { (_, fValue) -> fValue }.toMutableMap() )
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////

    private val _selectedRowIndex: MutableStateFlow<Int> = MutableStateFlow(0)
    val selectedRowIndex = _selectedRowIndex.asStateFlow()
    fun setSelectedRowIndex(i: Int) { _selectedRowIndex.value = i }

    private val _isEditMode: MutableStateFlow<Boolean> = MutableStateFlow(false)
    val isEditMode = _isEditMode.asStateFlow()
    fun setIsEditMode(e: Boolean) { _isEditMode.value = e }
    //fun toggleIsEditMode() { _isEditMode.value = !_isEditMode.value }

    // copied from mainViewModel
    // selectedPresetIndex -
    private val _selectedPresetIndex: MutableStateFlow<Int> = MutableStateFlow(0)
    val selectedPresetIndex = _selectedPresetIndex.asStateFlow()
    fun setSelectedPresetIndex(i: Int) { _selectedPresetIndex.value = i }

    // Define the custom comparator
    private val presetComparator = Comparator<Preset> { a, b ->
        val aIsZero = a.priority == 0
        val bIsZero = b.priority == 0

        when {        // If a's priority is 0 and b's is not, a comes after b.
            aIsZero && !bIsZero -> 1
            // If b's priority is 0 and a's is not, a comes before b.
            !aIsZero && bIsZero -> -1
            // Otherwise (both are 0 or both are non-zero), sort by priority,
            // then by name as a secondary sort.
            else -> {
                val priorityCompare = a.priority.compareTo(b.priority)
                if (priorityCompare != 0) {
                    priorityCompare
                } else {
                    a.name.compareTo(b.name)
                }
            }
        }
    }

    // copied from mainViewModel
    // presetList - Preset 목록
    // - Preset Save Popup & Load Popup 에서 목록 변경
    // - 앱 실행시 저장된 목록을  preference 에서 값 해당 값 로딩 (없으면 기본값 = 상수값 = 공장초기화 값)
    private val _presetList = MutableStateFlow<List<Preset>>(
        // Initialize with a deep copy of PresetList, sorted as per your original logic
        PresetList.map { it.copy() }
            .sortedWith(presetComparator)
    )
    val presetList = _presetList
    fun setPresetList(list: List<Preset> = PresetList) {
        // deep copy
        _presetList.value = list
            .map{ it.copy() }
    }
    fun setPresetListSorted(list: List<Preset> = PresetList) {
        // deep copy
        _presetList.value = list
            .map{ it.copy() }
            .sortedWith(presetComparator)
    }
    fun clearPreset() {
        setPresetList( emptyList() )
    }
    fun deletePreset(index: Int) {
        val currentList = _presetList.value.toMutableList()
        if (index >= 0 && index < currentList.size) {
            currentList.removeAt(index)
            //_presetList.value = currentList // Assign new list to StateFlow
            setPresetList(currentList)
        } else {
            Timber.e("deletePreset: Invalid index $index for list size ${currentList.size}")
        }
    }
    fun getPreset(index: Int): Preset? {
        var preset:Preset? = null
        val currentList = _presetList.value.toMutableList()
        if (index >= 0 && index < currentList.size) {
            preset = currentList.get(index)
        } else {
            Timber.e("deletePreset: Invalid index $index for list size ${currentList.size}")
        }
        return preset
    }
    fun setPreset(newPreset: Preset) {
        // 1. create a new list with the updated item - immutable way to update a StateFlow<List<...>>.
        val updatedList = _presetList.value.map { localPreset ->
            if (localPreset.id == newPreset.id) {
                // This is the preset to update. Replace it with the master version.
                Timber.d("Refreshing preset (id: ${newPreset.id}, name: '${newPreset.name}').")
                newPreset.copy() // Use a copy to be safe
            } else {
                // This is not the preset we're looking for, so return it unchanged.
                localPreset
            }
        }

        // 2. check if the list was actually changed before emitting a new value.
        if (_presetList.value != updatedList) {
            // 3. Update the local StateFlow with the new list.
            _presetList.value = updatedList
        }
    }
    fun addPreset(preset: Preset) {
        val currentList = _presetList.value.toMutableList()
        currentList.add(preset.copy()) // Add a copy to ensure immutability if preset is reused
        // Re-sort if adding can change order, or decide on insertion logic
        _presetList.value = currentList.sortedWith(presetComparator)
    }
    fun updatePresetName(index: Int, newName: String) {
        val currentList = _presetList.value.toMutableList()
        if (index >= 0 && index < currentList.size) {
            val updatedPreset = currentList[index].copy(name = newName)
            currentList[index] = updatedPreset
            //_presetList.value = currentList // Assign new list to StateFlow
            setPresetList(currentList)
            Timber.d("Updated preset at index $index with name: $newName. List: ${_presetList.value}")
        } else {
            Timber.e("updatePresetName: Invalid index $index for list size ${currentList.size}")
        }
    }

    /** 선택한 handPieceType의 초기값으로 pulseWidth, fluence, repetition 설정) */
    fun updateHandPieceType(index: Int, handPieceType: Int, isUp: UpDownState) {
        val currentList = _presetList.value.toMutableList() // Get current list from StateFlow
        if (index >= 0 && index < currentList.size) {
            val no_of_hand_piece = 5
            // 1. handPienceType Update
            val newHandPieceType = if (isUp == UpDownState.Up) {
                if (handPieceType < no_of_hand_piece -1) handPieceType + 1 else handPieceType
            } else {
                if (handPieceType > 0) handPieceType -1 else handPieceType
            }

            // 2. load tables
            loadFluenceTable(newHandPieceType)
            loadHzTable(newHandPieceType)

            // 3. get pulseWidth with index 0
            val newPulseWidthIndex = 0
            val newPulseWidth = PulseDurations[newPulseWidthIndex]

            // 4. fluence update (energyTable)
            val newFluenceList = energyTable.value.getKey2ListForKey1(newPulseWidth)
            val newFluence = newFluenceList.get(0)

            // 5. repetition update (hzTable)
            val hzType = hzTable.value.getValue(newPulseWidth, newFluence)
            Timber.d("hzType: ${hzType}")
            val list = RepetitionsByColorKey.get(hzType)!!
            setRepetitionList( list )

            val newRepetitionIndex = 0
            val newRepetition = list[newRepetitionIndex]

            val updatedPreset = currentList[index].copy(
                handPieceType = newHandPieceType,
                pulseWidth = newPulseWidth,
                fluence = newFluence,
                repetition = newRepetition
            )
            currentList[index] = updatedPreset
            // Instead of calling setPresetList (which re-sorts and deep copies everything again),
            // directly update the value for better performance if sorting isn't needed here.
            //_presetList.value = currentList // Assign new list to StateFlow
            setPresetList(currentList)
            Timber.d("Updated handPieceType at index $index with handPieceType: $newHandPieceType.")

            // get index of updatedPreset
            // The sorted list is now the current value of _presetList
            val newIndex = _presetList.value.indexOf(updatedPreset)
            if (newIndex != -1) {
                Timber.d("Found updatedPreset at new index: $newIndex")
                // Now you can use this newIndex, for example, to update the selected row
                setSelectedPresetIndex(newIndex)
            } else {
                Timber.e("Could not find the updated preset in the list after sorting.")
            }
        } else {
            Timber.e("updateHandPieceType: Invalid index $index for list size ${currentList.size}")
        }
    }

    fun updatePulseWidth(index: Int, handPieceType: Int, pulseWidth: Float, isUp: UpDownState) {
        val currentList = _presetList.value.toMutableList()
        if (index < 0 || index >= currentList.size) {
            Timber.e("updatePulseWidth: Invalid index $index for list size ${currentList.size}")
            return
        }

        // 1. load tables
        loadFluenceTable(handPieceType)
        loadHzTable(handPieceType)

        // 2. update pulseWidth
        val pulseWidthIndex = PulseDurations.indexOf(pulseWidth).takeIf { it != -1 } ?: 0
        val newPulseWidth = if (isUp == UpDownState.Up) {
            if (pulseWidthIndex < PulseDurations.size - 1) PulseDurations[pulseWidthIndex + 1] else pulseWidth
        } else {
            if (pulseWidthIndex > 0) PulseDurations[pulseWidthIndex - 1] else pulseWidth
        }

        // 3. update fluence
        val newFluenceList = energyTable.value.getKey2ListForKey1(newPulseWidth)
        val newFluenceIndex = 0
        val newFluence = newFluenceList.get(newFluenceIndex)
        
        // 4. update repetition
        val hzType = hzTable.value.getValue(newPulseWidth, newFluence)
        val list = RepetitionsByColorKey[hzType]!!
        setRepetitionList( list )

        val newRepetitionIndex = 0
        val newRepetition = repetitionList.value[newRepetitionIndex]

        val updatedPreset = currentList[index].copy(
            pulseWidth = newPulseWidth,
            fluence = newFluence,
            repetition = newRepetition
        )
        currentList[index] = updatedPreset
        //_presetList.value = currentList
        setPresetList(currentList)
        Timber.d("Updated pulseWidth at index $index with newPulseWidth: $newPulseWidth.")
    }

    fun updateFluence(index: Int, handPieceType: Int, pulseWidth: Float, fluence: Float, isUp: UpDownState) {
        val currentList = _presetList.value.toMutableList()
        if (index < 0 || index >= currentList.size) {
            Timber.e("updateFluence: Invalid index $index for list size ${currentList.size}")
            return
        }

        // 1. load tables
        loadFluenceTable(handPieceType)
        loadHzTable(handPieceType)

        // 2. get fluence
        val newFluenceList = energyTable.value.getKey2ListForKey1(pulseWidth)
        val newFluenceIndex = newFluenceList.indexOf(fluence).takeIf { it != -1 } ?: 0
        val newFluence = if (isUp == UpDownState.Up) {
            if (newFluenceIndex < newFluenceList.size - 1) newFluenceList[newFluenceIndex + 1] else fluence
        } else {
            if (newFluenceIndex > 0) newFluenceList[newFluenceIndex - 1] else fluence
        }

        // 3. update repetition (0)
        val hzType = hzTable.value.getValue(pulseWidth, newFluence)
        val list = RepetitionsByColorKey[hzType]!!
        setRepetitionList( list )

        val newRepetitionIndex = 0
        val newRepetition = repetitionList.value[newRepetitionIndex]

        // The original code here had a bug: it was updating `pulseWidth = newFluence`
        // It should be `fluence = newFluence`
        val updatedPreset = currentList[index].copy(
            fluence = newFluence,
            repetition = newRepetition
        )
        currentList[index] = updatedPreset
        //_presetList.value = currentList
        setPresetList(currentList)
        Timber.d("Updated fluence at index $index with newFluence: $newFluence. FluenceList: $fluenceList")
    }

    fun updateRepetition(index: Int, handPieceType: Int, pulseWidth: Float, fluence: Float, repetition: Float, isUp: UpDownState) {
        val currentList = _presetList.value.toMutableList()
        if (index < 0 || index >= currentList.size) {
            Timber.e("updateRepetition: Invalid index $index for list size ${currentList.size}")
            return
        }

        // 1. load tables
        loadFluenceTable(handPieceType)
        loadHzTable(handPieceType)

        // update repetition
        val hzType = hzTable.value.getValue(pulseWidth, fluence)
        val list = RepetitionsByColorKey[hzType]!!
        setRepetitionList( list )

        val newRepetitionIndex = repetitionList.value.indexOf(repetition).takeIf { it != -1 } ?: 0
        val newRepetition = if (isUp == UpDownState.Up) {
            // Original logic: repetitionIndex < PulseDurations.size -1 (Likely a typo, should be Repetitions.size)
            if (newRepetitionIndex < repetitionList.value.size - 1) repetitionList.value[newRepetitionIndex + 1] else repetition
        } else {
            // Original logic: repetitionIndex > 1
            if (newRepetitionIndex > 0) repetitionList.value[newRepetitionIndex - 1] else repetition
        }

        val updatedPreset = currentList[index].copy(repetition = newRepetition)
        currentList[index] = updatedPreset
        //_presetList.value = currentList
        setPresetList(currentList)
        Timber.d("Updated repetition at index $index with newRepetition: $newRepetition.")
    }

    fun updatePriority(index: Int, priority: Int, isUp: UpDownState) {
        val currentList = _presetList.value.toMutableList()
        if (index < 0 || index >= currentList.size) {
            Timber.e("updateRepetition: Invalid index $index for list size ${currentList.size}")
            return
        }

        val maxPriority = 5 // Assuming 5 is the max assignable priority
        val newPriority = if (isUp == UpDownState.Up) {
            if (priority == 0) 1 else (priority + 1).coerceAtMost(maxPriority)
        } else {
            if (priority == 1) 0 else (priority - 1).coerceAtLeast(0)
        }

        // update conflicting priority to 0
        /*
        val curHandPieceType = currentList[index].handPieceType
        currentList.filter{ it.handPieceType == curHandPieceType && it.priority == newPriority }.forEach {
            it.priority = 0
        }
        */

        val updatedPreset = currentList[index].copy(priority = newPriority)
        currentList[index] = updatedPreset
        //_presetList.value = currentList
        setPresetList(currentList)
        Timber.d("Updated priority at index $index with newPriority: $newPriority.")

        // get index of updatedPreset
        // The sorted list is now the current value of _presetList
        val newIndex = _presetList.value.indexOf(updatedPreset)

        if (newIndex != -1) {
            Timber.d("Found updatedPreset at new index: $newIndex")
            // Now you can use this newIndex, for example, to update the selected row
            setSelectedPresetIndex(newIndex)
        } else {
            Timber.e("Could not find the updated preset in the list after sorting.")
        }
    }

    init {
        Timber.d("PresetViewModel init")
    }
}