package com.syncflow.ui.home

import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.WorkInfo
import androidx.work.WorkManager
import androidx.work.WorkQuery
import com.syncflow.data.db.SyncPairDao
import com.syncflow.data.db.entities.SyncPairEntity
import com.syncflow.domain.model.ScheduleType
import com.syncflow.domain.model.SyncStatus
import com.syncflow.ui.shared.SyncProgress
import com.syncflow.worker.FileWatchService
import com.syncflow.worker.SyncWorker
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class HomeViewModel @Inject constructor(
    private val syncPairDao: SyncPairDao,
    private val workManager: WorkManager,
    @ApplicationContext private val context: Context,
) : ViewModel() {

    val syncPairs = syncPairDao.observeAll()
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())

    val syncProgressMap: kotlinx.coroutines.flow.StateFlow<Map<Long, SyncProgress>> =
        workManager.getWorkInfosFlow(WorkQuery.fromStates(WorkInfo.State.RUNNING))
            .map { infos ->
                infos
                    .mapNotNull { info ->
                        val tag = info.tags.firstOrNull { it.startsWith("sync_") } ?: return@mapNotNull null
                        val pairId = tag.removePrefix("sync_").toLongOrNull() ?: return@mapNotNull null
                        val up = info.progress.getInt(SyncWorker.KEY_PROGRESS_UPLOADED, 0)
                        val down = info.progress.getInt(SyncWorker.KEY_PROGRESS_DOWNLOADED, 0)
                        val del = info.progress.getInt(SyncWorker.KEY_PROGRESS_DELETED, 0)
                        val bytes = info.progress.getLong(SyncWorker.KEY_PROGRESS_BYTES, 0L)
                        if (up > 0 || down > 0 || del > 0) pairId to SyncProgress(up, down, del, bytes) else null
                    }
                    .toMap()
            }
            .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyMap())

    fun triggerSync(pair: SyncPairEntity) {
        val req = SyncWorker.buildOneTimeRequest(pair.id, wifiOnly = false, chargingOnly = false)
        workManager.enqueue(req)
    }

    fun pauseSync(pair: SyncPairEntity) {
        workManager.cancelAllWorkByTag("sync_${pair.id}")
        viewModelScope.launch { syncPairDao.updateStatus(pair.id, SyncStatus.PAUSED) }
    }

    fun toggleEnabled(pair: SyncPairEntity) {
        viewModelScope.launch {
            val nowEnabled = !pair.isEnabled
            syncPairDao.update(pair.copy(isEnabled = nowEnabled))
            if (nowEnabled) {
                when (pair.scheduleType) {
                    ScheduleType.ON_CHANGE -> FileWatchService.start(context)
                    ScheduleType.MANUAL -> { /* nothing */ }
                    else -> {
                        val req = SyncWorker.buildPeriodicRequest(
                            pair.id,
                            pair.scheduleIntervalMinutes.toLong().coerceAtLeast(15),
                            pair.wifiOnly,
                            pair.chargingOnly,
                        )
                        workManager.enqueueUniquePeriodicWork("periodic_${pair.id}", ExistingPeriodicWorkPolicy.UPDATE, req)
                    }
                }
            } else {
                workManager.cancelAllWorkByTag("sync_${pair.id}")
                // Refresh watcher (it will stop itself if no ON_CHANGE pairs remain)
                if (pair.scheduleType == ScheduleType.ON_CHANGE) {
                    FileWatchService.start(context)
                }
            }
        }
    }
}
