import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import cafe.adriel.voyager.core.model.ScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import mg.dot.feufaro.DeepLinkHandler
import mg.dot.feufaro.FileRepository
import mg.dot.feufaro.data.DrawerItem
import mg.dot.feufaro.data.GridTUOData
import mg.dot.feufaro.data.getDynamicDrawerItems
import mg.dot.feufaro.solfa.TimeUnitObject
import mg.dot.feufaro.midi.FMediaPlayer
import mg.dot.feufaro.viewmodel.MidiMarkers

class SharedScreenModel(private val fileRepository: FileRepository) : ScreenModel {
    private val _nextLabel = MutableStateFlow<String>("Next ...")
    val nextLabel: StateFlow<String> = _nextLabel.asStateFlow()

    private val _loadFile = MutableStateFlow<String>("Load ...")

    val loadFile: StateFlow<String> = _loadFile.asStateFlow()
    private val _measure = MutableStateFlow<String>("")
    val measure: StateFlow<String> = _measure.asStateFlow()
    private val _songTitle = MutableStateFlow<String>("")
    val songTitle: StateFlow<String> = _songTitle.asStateFlow()
    private val _stanza = MutableStateFlow<Int>(0)
    val stanza: StateFlow<Int> = _stanza.asStateFlow()
    private val _songKey = MutableStateFlow<String>("")
    val songKey: StateFlow<String> = _songKey.asStateFlow()
    private val _transposeTo = MutableStateFlow("")
    val transposeTo: StateFlow<String> = _transposeTo.asStateFlow()
    private val _transposeAsIf = MutableStateFlow("")
    val transposeAsIf: StateFlow<String> = _transposeAsIf.asStateFlow()
    private val _songAuthor = MutableStateFlow<String>("")
    val songAuthor: StateFlow<String> = _songAuthor.asStateFlow()
    private val _songComposer = MutableStateFlow<String>("")
    val songComposer: StateFlow<String> = _songComposer.asStateFlow()
    private val _songRhythm = MutableStateFlow<String>("")
    val songRhythm: StateFlow<String> = _songRhythm.asStateFlow()
    private val _nbStanzas = MutableStateFlow<Int>(0)
    val nbStanzas: StateFlow<Int> = _nbStanzas.asStateFlow()
    private var _tuoList =
        MutableStateFlow<List<TimeUnitObject>>(emptyList())
    val tuoList: StateFlow<List<TimeUnitObject>> = _tuoList.asStateFlow()
    private var _playlist =
        MutableStateFlow<List<String>>(emptyList())
    val playlist: StateFlow<List<String>> = _playlist.asStateFlow()
    private var _nextPlayed = MutableStateFlow(0)
    val nextPlayed: StateFlow<Int> = _nextPlayed.asStateFlow()
    private val tempTimeUnitObjectList = mutableListOf<TimeUnitObject>()
    var _hasMarker = MutableStateFlow<Boolean>(false)
    val hasMarker: StateFlow<Boolean> = _hasMarker.asStateFlow()

    private val _searchTitle = MutableStateFlow<String>("")
    val searchTitle: StateFlow<String> = _searchTitle.asStateFlow()

    private val _drawerItems = MutableStateFlow<List<DrawerItem>>(emptyList())
    val drawerItems: StateFlow<List<DrawerItem>> = _drawerItems.asStateFlow()

    init {
        loadItems()
    }

    private fun loadItems() {
        screenModelScope.launch {
            _drawerItems.value = getDynamicDrawerItems()
        }
    }

    @OptIn(ExperimentalCoroutinesApi::class)
    val filteredSongs: StateFlow<List<DrawerItem>> = searchTitle
        .mapLatest { currentTitle ->
            val searchTxt = if (currentTitle == "") "" else currentTitle.trim()
            return@mapLatest if(searchTxt.isEmpty()) {
                emptyList()
            } else {
                drawerItems.value.filter { item ->
                    item.contentTitle.contains(searchTxt, ignoreCase = true)
                }
            }
        }.stateIn (
            scope = CoroutineScope(Dispatchers.Default),
            started = SharingStarted.WhileSubscribed(5000),
            initialValue = emptyList()
        )
    private val _fileContent = mutableStateOf<String?>(null)
    val fileContent: State<String?> = _fileContent

    private val _isQRCodeVisible = mutableStateOf(false)
    val isQRCodeVisible: State<Boolean> = _isQRCodeVisible

    private val _activeFilePath = mutableStateOf("")
    val activeFilePath: State<String> = _activeFilePath

    fun setFileContent(content: String?, path: String) {
        _fileContent.value = content
        _activeFilePath.value = path
    }
    fun toggleQRCodeVisibility() {
        _isQRCodeVisible.value = !_isQRCodeVisible.value
    }
    val qrCodeContent: State<String?>
        get() = mutableStateOf(
            _activeFilePath.value.takeIf { it.isNotBlank() }?.let { path ->
                val fileName = path.substringAfterLast("/").substringAfterLast("\\")
                "feufaro://song?file=${fileName}"
            }
        )

    fun updateSearchTxt(searchValue: String) {
        _searchTitle.value = searchValue
    }
    private val _showMidiCtrl = MutableStateFlow<Boolean>(false)
    val showMidiCtrl: StateFlow<Boolean> = _showMidiCtrl.asStateFlow()
    fun setMidiCtrl(value: Boolean) {
        _showMidiCtrl.value = value
    }

    private val _expandedFAB = MutableStateFlow<Boolean>(false)
    val expandedFAB: StateFlow<Boolean> = _expandedFAB.asStateFlow()

    fun setExpandedFAB(value: Boolean) {
        _expandedFAB.value = value
    }

    private val _showSearchMenu = MutableStateFlow<Boolean>(false)
    val showSearchMenu: StateFlow<Boolean> = _showSearchMenu.asStateFlow()
    fun showSearchMenu(value: Boolean) {
        _showSearchMenu.value = value
    }

    private var _mediaPlayer by mutableStateOf<FMediaPlayer?>(null)
    val mediaPlayer: FMediaPlayer? get() = _mediaPlayer

    private val _isPlay = MutableStateFlow(false)
    val isPlay = _isPlay.asStateFlow()

    private val _isPos = MutableStateFlow(true)
    val isPos = _isPos.asStateFlow()

    private val _isDragging = MutableStateFlow(true)
    val isDragging = _isDragging.asStateFlow()

    private val _currentPos = MutableStateFlow(0f)
    val currentPos = _currentPos.asStateFlow()
    private val _duration = MutableStateFlow(0f)
    val duration = _duration.asStateFlow()

    private val _volumeLevel = MutableStateFlow(0.8f)
    val volumeLevel = _volumeLevel.asStateFlow()

    private val _isPlayMid = MutableStateFlow(false)
    val isPlayMid = _isPlayMid.asStateFlow()

    private val _canUpdPositionFromPartition = MutableStateFlow(false)
    val canUpdPositionFromPartition = _canUpdPositionFromPartition.asStateFlow()

    private val _dcDone = MutableStateFlow(false)
    val dcDone = _dcDone.asStateFlow()

    private val _dsDone = MutableStateFlow(false)
    val dsDone = _dsDone.asStateFlow()

    private val _tuoTimestamps = MutableStateFlow<List<Long>>(emptyList())
    val tuoTimestamps: StateFlow<List<Long>> = _tuoTimestamps

    fun updateTimestamps(list: List<Long>) {
        _tuoTimestamps.value = list
    }

    fun seekToTimestamp(micros: Long) {
        _mediaPlayer?.seekTo(micros / 1000)
    }

    private val _midiMarkersList = MutableStateFlow<List<MidiMarkers>>(emptyList())
    val midiMarkersList: StateFlow<List<MidiMarkers>> = _midiMarkersList
    private val _activeIndex = MutableStateFlow(-1)
    val activeIndex: StateFlow<Int> = _activeIndex.asStateFlow()

    val currentGridData: GridTUOData
        get() = GridTUOData(
            tuoList = _tuoList.value,
            measure = _measure.value,
            stanza = _stanza.value
        )
    fun updateActiveIndex(currentPosMs: Long) {
        val currentPosMicros = currentPosMs * 1000
        val index = _tuoTimestamps.value.indexOfLast { it <= currentPosMicros }
        if (_activeIndex.value != index) {
            _activeIndex.value = index.coerceAtLeast(0)
        }
    }
    fun updateActiveIndexByIndex(index: Int) {
        val clamped = index.coerceAtLeast(0)
        if (_activeIndex.value != clamped) {
            _activeIndex.value = clamped
        }
    }
    private val _gridCount = MutableStateFlow(0)
    val gridCount: StateFlow<Int> = _gridCount.asStateFlow()

    fun addGridCount(nbGrid: Int) {
        _gridCount.value += nbGrid
    }
    fun descGridCount(nbGrid: Int) {
        if(_gridCount.value > 0) {
            _gridCount.value -= nbGrid
        }
    }
    fun resetGridCount() {
        _gridCount.value = 0
    }
    fun seekToGrid(gridIndex: Int) {
        _mediaPlayer?.seekToGrid(gridIndex)
    }

    fun getFullMarkers(): List<MidiMarkers> {
        return _midiMarkersList.value
    }

    fun setDcDone(state: Boolean) {
        _dcDone.value = state
    }
    fun setDsDone(state: Boolean) {
        _dsDone.value = state
    }
    fun getDcDone(): Boolean {
        return _dcDone.value
    }
    fun getDsDone(): Boolean {
        return _dsDone.value
    }

    fun getTotalGridCount(): Int {
        return _tuoList.value.drop(1).size - 1
    }
    fun updateAndFinalizeMidiData(rawList: List<MidiMarkers>) {
//        val timestamps = _tuoTimestamps.value
        val tuos = _tuoList.value.drop(1)

        val finalizedList = rawList.map { marker ->
            var markerText = marker.marker
            val index = marker.gridIndex ?: 0 // On utilise l'index passé par l'UI
            val isNearEnd = index >= (tuos.size - 2)

            val isDC = markerText.contains(Regex("""D\.?C\.?"""))
            val isDS = markerText.contains(Regex("""D\.?S\.?"""))
            val isFarany = markerText.trim().equals("Farany", ignoreCase = true)

            val isRit = Regex("""rit\.?|ritard\.?|ritenuto\.?|ritardando""", RegexOption.IGNORE_CASE)

            var resultMarker: MidiMarkers = marker
            if(isFarany) {
                var forwardIndex = index
                while (forwardIndex < tuos.size) {
                    val sep = tuos.getOrNull(forwardIndex)?.sep0 ?: ""
                    if (sep == "/") {
                        resultMarker = marker.copy(
                            gridIndex = forwardIndex - 1,
                            marker = "Farany_GROUP_PART"
                        )
                        println("Farany finalisé : grille $index → ${forwardIndex - 1}")
                        break
                    }
                    forwardIndex++
                }
            } else if (isDC) {
                var forwardIndex = index
                while (forwardIndex < tuos.size) {
                    val currentTuo = tuos.getOrNull(forwardIndex)
                    val currentNote = currentTuo?.tuNotes?.getOrNull(1)?.toString() ?: ""
                    val currentSep = currentTuo?.sep0 ?: ""

//                    println("Je suis sur $forwardIndex note $currentNote Sep $currentSep \t condition: ${(currentNote == "―")} || ${(currentSep == "/")}")
                    // Tant que fin de mesure
                    if (currentSep == "/") {
                        resultMarker = marker.copy(
                            gridIndex = forwardIndex-1,
                            marker = "${markerText.trim()}_GROUP_PART"
                        )
                        break
                    }
                    forwardIndex++
                }

            } else if (isDS && isNearEnd) {
                val currentNote = tuos.getOrNull(index)?.tuNotes?.getOrNull(1)?.toString() ?: ""
                val nextNote = tuos.getOrNull(index + 1)?.tuNotes?.getOrNull(1)?.toString() ?: ""

                // Cas où le marker et la note suivante sont vides
                val isEmptySituation = currentNote.trim().isEmpty() && nextNote.trim().isEmpty()

                if (isEmptySituation) {
                    // vers arrière une note non vide
                    var backwardIndex = index - 1
                    while (backwardIndex >= 0) {
                        val note = tuos.getOrNull(backwardIndex)?.tuNotes?.getOrNull(1)?.toString() ?: ""
                        if (note.trim().isNotEmpty()) {
                            val cleanText = if (markerText.trim() == "DSFin") "DS" else markerText.trim()

                            resultMarker = marker.copy(
                                gridIndex = backwardIndex,
                                marker = "${cleanText}_GROUP_PART"
                            )
                            break
                        }
                        backwardIndex--
                    }
                } else {
                    // vers avant le séparateur "/"
                    var forwardIndex = index
                    while (forwardIndex < tuos.size) {
                        val sep = tuos.getOrNull(forwardIndex)?.sep0 ?: ""
                        if (sep == "/") {
                            val cleanText = if (markerText.trim() == "DSFin") "DS" else markerText.trim()

                            resultMarker = marker.copy(
                                gridIndex = forwardIndex,
                                marker = "${cleanText}_GROUP_PART"
                            )
                            break
                        }
                        forwardIndex++
                    }
                }
            } else if(isRit.containsMatchIn(markerText)) {
                var forwardIndex = index + 1
                var foundSeparator = false

                while (forwardIndex < tuos.size) {
                    val sep = tuos.getOrNull(forwardIndex)?.sep0 ?: ""
                    if (sep == "/") {
                        resultMarker = marker.copy(
                            gridIndex = index,
                            lastCallerMarker = forwardIndex - 1,
                            marker = "Ritenuto"
                        )
                        println("Rit finalisé : grille $index → ${forwardIndex - 1}")
                        foundSeparator = true
                        break
                    }
                    forwardIndex++
                }
            } else {
                marker
            }
            resultMarker
        }

        _midiMarkersList.value = finalizedList
        println("Markers finalisés et mis à jour : ${finalizedList.size}")
    }

    fun loadNewSong(newMidiFile: String) {
        _mediaPlayer?.stop()
        _mediaPlayer?.release()
        _mediaPlayer = null
        _isPos.value = true
        _isPlay.value = false
        _currentPos.value = 0f
        _dcDone.value = false
        _dsDone.value = false
        try {
            val midiFileName = fileRepository.getFileName(newMidiFile)
            println("Opening xx129 $midiFileName")
            _mediaPlayer = FMediaPlayer(filename = midiFileName, onFinished = {
//            _isPos.value = true
//            _isPlay.value = false
                _currentPos.value = 0f
                seekToGrid(0)
                _mediaPlayer?.stop()
                setDcDone(false)
                setDsDone(false)
                println("fin de lecture du Midi $newMidiFile")
                _mediaPlayer?.syncNavigationMonitor(this)
            })
//            synchro
            _mediaPlayer?.requestSync(this)
            //finalizeMarkers()
            _mediaPlayer?.syncNavigationMonitor(this)
        } catch(e: Exception) {
            println("Erreur d'ouverture de mediaPlayer : ${e.message} ")
        }
        println("New media Player crée $newMidiFile")
    }

    fun togglePlayPause() {
        _canUpdPositionFromPartition.value=true
        _mediaPlayer?.let { player ->
            if (_isPlay.value) {
                _isPlay.value = false
                _isPos.value = true
                player.pause()
            } else {
                _isPlay.value = true
                _isPos.value = false
                player.play()
                player.setVolume(_volumeLevel.value)
                if(currentPos.value == 0f) {
                    player.seekTo(0)
                }
            }
//            println("128: Status de isPlay ${_isPlay.value} \nisPos ${_isPos.value} \ncurrentPos ${_currentPos.value} \n volume ${_volumeLevel.value}")
        }
    }
    fun stopMidi() {
        _mediaPlayer?.let { player ->
            _isPlay.value = false
            _isPos.value = true
            player.pause()
        }
    }
    fun seekTo(pos: Float) {
        _currentPos.value = pos
        _mediaPlayer?.let { player ->
            player.seekTo(pos.toLong())
            }
    }

    fun setDragging(dragState: Boolean) {
        _isDragging.value = dragState
    }
    fun setVolume(level: Float) {
        _volumeLevel.value = level
        _mediaPlayer?.let { player ->
            player.setVolume(level) }
    }
    fun updateProgress(){
        _mediaPlayer?.let { player->
            if (_isPlay.value) {
                val p = player.getCurrentPosition().toFloat()
                val d = player.getDuration().toFloat()

                if (p >= 0) _currentPos.value = p
                if ((d > 0) && _duration.value != d) _duration.value = d
            }
        }
    }
    var currentNoteIndex by mutableStateOf(0f)

    fun updatePositionFromPartition(index: Int, totalRow: Int) {
        if(_canUpdPositionFromPartition.value) {     /*upd pos part only if midi on playing*/
            val duration = _duration.value
            if(totalRow > 0) {
                currentNoteIndex = index.toFloat()
                val newPos = (currentNoteIndex / totalRow.toFloat()) * duration
                seekTo(newPos)
                println("Shared:196 currentNoteIndex $currentNoteIndex, Index $index et curret = ${_currentPos.value}")
            }
        }
    }

    fun appendData(otherData: String) {
        _nextLabel.value += otherData
    }

    fun reset() {
        // Close other menus
        _showMidiCtrl.value = false
        _expandedFAB.value = false
        _showSearchMenu.value = false
        _midiMarkersList.value = emptyList()
        _tuoTimestamps.value   = emptyList()
        updateSearchTxt("")
        tempTimeUnitObjectList.clear()
        resetGridCount()
    }

    fun lastTUO(): TimeUnitObject? {
        return tempTimeUnitObjectList.lastOrNull()
    }

    fun addTUO(newTUO: TimeUnitObject) {
        tempTimeUnitObjectList.add(newTUO)
    }

    fun doneTUOList() {
        _tuoList.value = tempTimeUnitObjectList.toList()
    }

    fun setMeasure(theMeasure: String) {
        _measure.value = theMeasure
    }

    fun setSongTitle(theTitle: String) {
        _songTitle.value = theTitle
    }

    fun setStanza(theStanza: Int) {
        try {
            _stanza.value = theStanza
        } catch (e: NumberFormatException) {
            _stanza.value = 0
        }
    }

    fun setSongKey(theSongKey: String) {
        _songKey.value = theSongKey
    }

    fun setTransposeTo(key: String) {
        _transposeTo.value = key
    }

    fun setTransposeAsIf(key: String) {
        _transposeAsIf.value = key
    }

    fun setSongAuthor(theSongAuthor: String) {
        _songAuthor.value = theSongAuthor
    }

    fun setSongComposer(theSongComposer: String) {
        _songComposer.value = theSongComposer
    }

    fun setSongRhythm(theSongRhythm: String) {
        _songRhythm.value = theSongRhythm
    }

    fun setNbStanzas(nbStanzas: Int) {
        _nbStanzas.value = nbStanzas
    }

    fun setPlaylist(thePlaylist: List<String>) {
        _playlist.value = thePlaylist
    }

    fun setHasMarker(theHasMarker: Boolean) {
        _hasMarker.value = theHasMarker

    }
    data class HairPinData(
        val symbol: Char,
        val startGrid: Int,
        val endGrid: Int,
    )

    fun getHairPins(): List<HairPinData> {
        val allMarkers = _midiMarkersList.value
        val starts = allMarkers
            .filter {
                val m = it.marker.trim()
                m == "<" ||
                m == ">" ||
                m.contains("cres", ignoreCase = true) ||
                m.contains("dim",  ignoreCase = true)
            }
            .sortedBy { it.gridIndex }
        val ends = allMarkers
            .filter { it.marker.trim().endsWith("=") }
            .sortedBy { it.gridIndex }
            .toMutableList()

//        println("HairPins starts: ${starts.size} | ends: ${ends.size}")
        return starts.mapNotNull { start ->
            val startGrid = start.gridIndex ?: return@mapNotNull null
            val symbol    = when {
                start.marker.trim() == "<" -> '<'
                start.marker.trim() == ">" -> '>'
                start.marker.contains("cres", ignoreCase = true) -> '<'
                start.marker.contains("dim",  ignoreCase = true) -> '>'
                else -> return@mapNotNull null
            }

            val matchingEnd = ends.firstOrNull { (it.gridIndex ?: 0) > startGrid }
            if (matchingEnd != null) ends.remove(matchingEnd)
            val endGrid = matchingEnd?.gridIndex ?: (startGrid + 4)
//            println("HairPin '$symbol' : $startGrid → $endGrid")
            HairPinData(symbol, startGrid, endGrid)
        }
    }

    fun playNext() {
        val nextIndex = (_nextPlayed.value + 1) % _playlist.value.size
        _nextPlayed.value = nextIndex
        setStanza(1)
    }

    fun currentPlayed(): String {
        val playlistIndex = _nextPlayed.value
        return _playlist.value[playlistIndex]
    }

}