package com.atridad.ascently.ui.viewmodel

import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.atridad.ascently.data.health.HealthConnectManager
import com.atridad.ascently.data.model.*
import com.atridad.ascently.data.repository.ClimbRepository
import com.atridad.ascently.data.sync.SyncService
import com.atridad.ascently.service.SessionTrackingService
import com.atridad.ascently.utils.AppLogger
import com.atridad.ascently.utils.ImageUtils
import com.atridad.ascently.widget.ClimbStatsWidgetProvider
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import java.io.File

class ClimbViewModel(
    private val repository: ClimbRepository,
    val syncService: SyncService,
    private val context: Context,
) : ViewModel() {

    // Health Connect manager
    private val healthConnectManager = HealthConnectManager(context)

    // UI State flows
    private val _uiState = MutableStateFlow(ClimbUiState())
    val uiState: StateFlow<ClimbUiState> = _uiState.asStateFlow()

    // Data flows
    val gyms =
        repository
            .getAllGyms()
            .stateIn(
                scope = viewModelScope,
                started = SharingStarted.WhileSubscribed(),
                initialValue = emptyList(),
            )

    val problems =
        repository
            .getAllProblems()
            .stateIn(
                scope = viewModelScope,
                started = SharingStarted.WhileSubscribed(),
                initialValue = emptyList(),
            )

    val sessions =
        repository
            .getAllSessions()
            .stateIn(
                scope = viewModelScope,
                started = SharingStarted.WhileSubscribed(),
                initialValue = emptyList(),
            )

    val activeSession =
        repository
            .getActiveSessionFlow()
            .stateIn(
                scope = viewModelScope,
                started = SharingStarted.WhileSubscribed(),
                initialValue = null,
            )

    val attempts =
        repository
            .getAllAttempts()
            .stateIn(
                scope = viewModelScope,
                started = SharingStarted.WhileSubscribed(),
                initialValue = emptyList(),
            )

    // Gym operations
    fun addGym(gym: Gym, updateWidgets: Boolean = true) {
        viewModelScope.launch {
            repository.insertGym(gym)
            if (updateWidgets) {
                ClimbStatsWidgetProvider.updateAllWidgets(context)
            }
        }
    }

    fun updateGym(gym: Gym, updateWidgets: Boolean = true) {
        viewModelScope.launch {
            repository.updateGym(gym)
            if (updateWidgets) {
                ClimbStatsWidgetProvider.updateAllWidgets(context)
            }
        }
    }

    fun deleteGym(gym: Gym, updateWidgets: Boolean = true) {
        viewModelScope.launch {
            repository.deleteGym(gym)
            if (updateWidgets) {
                ClimbStatsWidgetProvider.updateAllWidgets(context)
            }
        }
    }

    fun getGymById(id: String): Flow<Gym?> = flow { emit(repository.getGymById(id)) }

    // Problem operations
    fun addProblem(problem: Problem, updateWidgets: Boolean = true) {
        viewModelScope.launch {
            val finalProblem = renameTemporaryImages(problem)
            repository.insertProblem(finalProblem)
            if (updateWidgets) {
                ClimbStatsWidgetProvider.updateAllWidgets(context)
            }
        }
    }

    private fun renameTemporaryImages(problem: Problem): Problem {
        if (problem.imagePaths.isEmpty()) {
            return problem
        }

        val finalImagePaths = mutableListOf<String>()

        problem.imagePaths.forEachIndexed { index, tempPath ->
            if (tempPath.startsWith("temp_")) {
                val finalPath =
                    ImageUtils.renameTemporaryImage(context, tempPath, problem.id, index)
                finalImagePaths.add(finalPath ?: tempPath)
            } else {
                finalImagePaths.add(tempPath)
            }
        }

        return problem.copy(imagePaths = finalImagePaths)
    }

    fun updateProblem(problem: Problem, updateWidgets: Boolean = true) {
        viewModelScope.launch {
            val finalProblem = renameTemporaryImages(problem)
            repository.updateProblem(finalProblem)
            if (updateWidgets) {
                ClimbStatsWidgetProvider.updateAllWidgets(context)
            }
        }
    }

    fun deleteProblem(problem: Problem, updateWidgets: Boolean = true) {
        viewModelScope.launch {
            problem.imagePaths.forEach { imagePath -> ImageUtils.deleteImage(context, imagePath) }
            repository.deleteProblem(problem)
            cleanupOrphanedImages()
            if (updateWidgets) {
                ClimbStatsWidgetProvider.updateAllWidgets(context)
            }
        }
    }

    private suspend fun cleanupOrphanedImages() {
        val allProblems = repository.getAllProblems().first()
        val referencedImagePaths = allProblems.flatMap { it.imagePaths }.toSet()
        ImageUtils.cleanupOrphanedImages(context, referencedImagePaths)
    }

    fun deleteAllImages() {
        viewModelScope.launch {
            val imagesDir = ImageUtils.getImagesDirectory(context)
            var deletedCount = 0

            imagesDir.listFiles()?.forEach { file ->
                if (file.isFile && file.extension.lowercase() == "jpg") {
                    if (file.delete()) {
                        deletedCount++
                    }
                }
            }

            val allProblems = repository.getAllProblems().first()
            val updatedProblems =
                allProblems.map { problem ->
                    if (problem.imagePaths.isNotEmpty()) {
                        problem.copy(imagePaths = emptyList())
                    } else {
                        problem
                    }
                }

            for (updatedProblem in updatedProblems) {
                if (updatedProblem.imagePaths !=
                    allProblems.find { it.id == updatedProblem.id }?.imagePaths
                ) {
                    repository.insertProblemWithoutSync(updatedProblem)
                }
            }

            AppLogger.i("ClimbViewModel") { "Deleted $deletedCount image files and cleared image references" }
        }
    }

    fun getProblemById(id: String): Flow<Problem?> = flow { emit(repository.getProblemById(id)) }

    fun getProblemsByGym(gymId: String): Flow<List<Problem>> = repository.getProblemsByGym(gymId)

    // Session operations

    fun updateSession(session: ClimbSession, updateWidgets: Boolean = true) {
        viewModelScope.launch {
            repository.updateSession(session)
            if (updateWidgets) {
                ClimbStatsWidgetProvider.updateAllWidgets(context)
            }
        }
    }

    fun deleteSession(session: ClimbSession, updateWidgets: Boolean = true) {
        viewModelScope.launch {
            repository.deleteSession(session)
            if (updateWidgets) {
                ClimbStatsWidgetProvider.updateAllWidgets(context)
            }
        }
    }

    fun getSessionById(id: String): Flow<ClimbSession?> = flow {
        emit(repository.getSessionById(id))
    }

    fun getSessionsByGym(gymId: String): Flow<List<ClimbSession>> =
        repository.getSessionsByGym(gymId)

    // Get last used gym for shortcut functionality
    suspend fun getLastUsedGym(): Gym? = repository.getLastUsedGym()

    // Active session management
    fun startSession(context: Context, gymId: String, notes: String? = null) {
        viewModelScope.launch {
            AppLogger.d("ClimbViewModel") { "startSession called with gymId: $gymId" }

            if (!com.atridad.ascently.utils.NotificationPermissionUtils
                    .isNotificationPermissionGranted(context)
            ) {
                AppLogger.d("ClimbViewModel") { "Notification permission not granted" }
                _uiState.value =
                    _uiState.value.copy(
                        error =
                        "Notification permission is required to track your climbing session. Please enable notifications in settings.",
                    )
                return@launch
            }

            val existingActive = repository.getActiveSession()
            if (existingActive != null) {
                AppLogger.d("ClimbViewModel") { "Active session already exists: ${existingActive.id}" }
                _uiState.value =
                    _uiState.value.copy(
                        error = "There's already an active session. Please end it first.",
                    )
                return@launch
            }

            AppLogger.d("ClimbViewModel") { "Creating new session" }
            val newSession = ClimbSession.create(gymId = gymId, notes = notes)
            repository.insertSession(newSession)

            AppLogger.d("ClimbViewModel") { "Starting tracking service for session: ${newSession.id}" }
            // Start the tracking service
            val serviceIntent = SessionTrackingService.createStartIntent(context, newSession.id)
            context.startForegroundService(serviceIntent)

            ClimbStatsWidgetProvider.updateAllWidgets(context)

            _uiState.value = _uiState.value.copy(message = "Session started successfully!")
        }
    }

    fun endSession(context: Context, sessionId: String) {
        viewModelScope.launch {
            if (!com.atridad.ascently.utils.NotificationPermissionUtils
                    .isNotificationPermissionGranted(context)
            ) {
                _uiState.value =
                    _uiState.value.copy(
                        error =
                        "Notification permission is required to manage your climbing session. Please enable notifications in settings.",
                    )
                return@launch
            }

            val session = repository.getSessionById(sessionId)
            if (session != null && session.status == SessionStatus.ACTIVE) {
                val completedSession = with(ClimbSession) { session.complete() }
                repository.updateSession(completedSession)

                val serviceIntent = SessionTrackingService.createStopIntent(context, sessionId)
                context.startService(serviceIntent)

                ClimbStatsWidgetProvider.updateAllWidgets(context)

                syncToHealthConnect(completedSession)

                _uiState.value = _uiState.value.copy(message = "Session completed!")
            }
        }
    }

    fun ensureSessionTrackingServiceRunning(context: Context) {
        viewModelScope.launch {
            val activeSession = repository.getActiveSession()
            if (activeSession != null && activeSession.status == SessionStatus.ACTIVE) {
                val serviceIntent =
                    SessionTrackingService.createStartIntent(context, activeSession.id)
                context.startForegroundService(serviceIntent)
            }
        }
    }

    // Attempt operations
    fun addAttempt(attempt: Attempt, updateWidgets: Boolean = true) {
        viewModelScope.launch {
            repository.insertAttempt(attempt)
            if (updateWidgets) {
                ClimbStatsWidgetProvider.updateAllWidgets(context)
            }
        }
    }

    fun deleteAttempt(attempt: Attempt, updateWidgets: Boolean = true) {
        viewModelScope.launch {
            repository.deleteAttempt(attempt)
            if (updateWidgets) {
                ClimbStatsWidgetProvider.updateAllWidgets(context)
            }
        }
    }

    fun updateAttempt(attempt: Attempt, updateWidgets: Boolean = true) {
        viewModelScope.launch {
            repository.updateAttempt(attempt)
            if (updateWidgets) {
                ClimbStatsWidgetProvider.updateAllWidgets(context)
            }
        }
    }

    fun getAttemptsBySession(sessionId: String): Flow<List<Attempt>> =
        repository.getAttemptsBySession(sessionId)

    fun getAttemptsByProblem(problemId: String): Flow<List<Attempt>> =
        repository.getAttemptsByProblem(problemId)

    fun exportDataToZipUri(context: Context, uri: android.net.Uri) {
        viewModelScope.launch {
            try {
                _uiState.value =
                    _uiState.value.copy(
                        isLoading = true,
                        message = "Creating ZIP file with images...",
                    )
                repository.exportAllDataToZipUri(context, uri)
                _uiState.value =
                    _uiState.value.copy(
                        isLoading = false,
                        message =
                        "Export complete! Your climbing data and images have been saved.",
                    )
            } catch (e: Exception) {
                _uiState.value =
                    _uiState.value.copy(
                        isLoading = false,
                        error = "Export failed: ${e.message}",
                    )
            }
        }
    }

    fun importData(file: File) {
        viewModelScope.launch {
            try {
                _uiState.value = _uiState.value.copy(isLoading = true)

                if (!file.name.lowercase().endsWith(".zip")) {
                    throw Exception(
                        "Only ZIP files are supported for import. Please use a ZIP file exported from Ascently.",
                    )
                }

                repository.importDataFromZip(file)

                _uiState.value =
                    _uiState.value.copy(
                        isLoading = false,
                        message = "Data imported successfully from ${file.name}",
                    )
            } catch (e: Exception) {
                _uiState.value =
                    _uiState.value.copy(
                        isLoading = false,
                        error = "Import failed: ${e.message}",
                    )
            }
        }
    }

    // UI state operations
    fun clearMessage() {
        _uiState.value = _uiState.value.copy(message = null)
    }

    fun clearError() {
        _uiState.value = _uiState.value.copy(error = null)
    }

    // Sync-related methods
    fun performManualSync() {
        viewModelScope.launch {
            try {
                syncService.syncWithServer()
            } catch (e: Exception) {
                setError("Sync failed: ${e.message}")
            }
        }
    }

    suspend fun testSyncConnection() {
        try {
            syncService.testConnection()
        } catch (e: Exception) {
            setError("Connection test failed: ${e.message}")
        }
    }

    fun setError(message: String) {
        _uiState.value = _uiState.value.copy(error = message)
    }

    fun setMessage(message: String) {
        _uiState.value = _uiState.value.copy(message = message)
    }

    fun resetAllData() {
        viewModelScope.launch {
            try {
                _uiState.value = _uiState.value.copy(isLoading = true)

                repository.resetAllData()

                _uiState.value =
                    _uiState.value.copy(
                        isLoading = false,
                        message = "All data has been reset successfully",
                    )
            } catch (e: Exception) {
                _uiState.value =
                    _uiState.value.copy(isLoading = false, error = "Reset failed: ${e.message}")
            }
        }
    }

    private fun syncToHealthConnect(session: ClimbSession) {
        viewModelScope.launch {
            try {
                val gym = repository.getGymById(session.gymId)
                val gymName = gym?.name ?: "Unknown Gym"

                val attempts = repository.getAttemptsBySession(session.id).first()
                val attemptCount = attempts.size

                val result =
                    healthConnectManager.autoSyncCompletedSession(
                        session,
                        gymName,
                        attemptCount,
                    )

                result.onFailure { error ->
                    if (healthConnectManager.isReadySync()) {
                        AppLogger.w("ClimbViewModel") { "Health Connect sync failed: ${error.message}" }
                    }
                }
            } catch (e: Exception) {
                if (healthConnectManager.isReadySync()) {
                    AppLogger.w("ClimbViewModel") { "Health Connect sync error: ${e.message}" }
                }
            }
        }
    }
}

data class ClimbUiState(
    val isLoading: Boolean = false,
    val message: String? = null,
    val error: String? = null,
)
