package com.placeholder.sherpai2.ui.utilities

import android.graphics.Bitmap
import android.net.Uri
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.mlkit.vision.common.InputImage
import com.google.mlkit.vision.face.FaceDetection
import com.google.mlkit.vision.face.FaceDetectorOptions
import com.placeholder.sherpai2.data.local.dao.ImageDao
import com.placeholder.sherpai2.data.local.dao.ImageTagDao
import com.placeholder.sherpai2.data.local.dao.TagDao
import com.placeholder.sherpai2.data.local.entity.ImageEntity
import com.placeholder.sherpai2.data.local.entity.ImageTagEntity
import com.placeholder.sherpai2.data.local.entity.TagEntity
import com.placeholder.sherpai2.domain.repository.ImageRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.tasks.await
import kotlinx.coroutines.withContext
import java.util.UUID
import javax.inject.Inject
import kotlin.math.abs

/**
 * PhotoUtilitiesViewModel - Photo collection management
 *
 * Features:
 * 1. Manual photo scan/rescan
 * 2. Duplicate detection (SHA256 + perceptual hash)
 * 3. Burst detection (photos within 2 seconds)
 * 4. Quality analysis (blurry, screenshots)
 */
@HiltViewModel
class PhotoUtilitiesViewModel @Inject constructor(
    private val imageRepository: ImageRepository,
    private val imageDao: ImageDao,
    private val tagDao: TagDao,
    private val imageTagDao: ImageTagDao,
    private val populateFaceDetectionCacheUseCase: com.placeholder.sherpai2.domain.usecase.PopulateFaceDetectionCacheUseCase
) : ViewModel() {

    private val _uiState = MutableStateFlow<UtilitiesUiState>(UtilitiesUiState.Idle)
    val uiState: StateFlow<UtilitiesUiState> = _uiState.asStateFlow()

    private val _scanProgress = MutableStateFlow<ScanProgress?>(null)
    val scanProgress: StateFlow<ScanProgress?> = _scanProgress.asStateFlow()

    /**
     * Populate face detection cache
     * Scans all photos to mark which ones have faces
     */
    fun populateFaceCache() {
        viewModelScope.launch(Dispatchers.IO) {
            try {
                _uiState.value = UtilitiesUiState.Scanning("faces")
                _scanProgress.value = ScanProgress("Checking database...", 0, 0)

                // DIAGNOSTIC: Check database state
                val totalImages = imageDao.getImageCount()
                val needsCaching = imageDao.getImagesNeedingFaceDetectionCount()

                android.util.Log.d("FaceCache", "=== DIAGNOSTIC ===")
                android.util.Log.d("FaceCache", "Total images in DB: $totalImages")
                android.util.Log.d("FaceCache", "Images needing caching: $needsCaching")

                if (needsCaching == 0) {
                    // All images already cached!
                    withContext(Dispatchers.Main) {
                        _uiState.value = UtilitiesUiState.ScanComplete(
                            "All $totalImages photos already scanned!\n\nTo force re-scan, use 'Force Rebuild Cache' button.",
                            totalImages
                        )
                        _scanProgress.value = null
                    }
                    return@launch
                }

                _scanProgress.value = ScanProgress("Detecting faces...", 0, needsCaching)

                val scannedCount = populateFaceDetectionCacheUseCase.execute { current, total, _ ->
                    _scanProgress.value = ScanProgress(
                        "Scanning faces... $current/$total",
                        current,
                        total
                    )
                }

                withContext(Dispatchers.Main) {
                    _uiState.value = UtilitiesUiState.ScanComplete(
                        "Scanned $scannedCount photos for faces",
                        scannedCount
                    )
                    _scanProgress.value = null
                }

            } catch (e: Exception) {
                android.util.Log.e("FaceCache", "Error populating cache", e)
                withContext(Dispatchers.Main) {
                    _uiState.value = UtilitiesUiState.Error(
                        e.message ?: "Failed to populate face cache"
                    )
                    _scanProgress.value = null
                }
            }
        }
    }

    /**
     * Force rebuild entire face cache (re-scan ALL photos)
     */
    fun forceRebuildFaceCache() {
        viewModelScope.launch(Dispatchers.IO) {
            try {
                _uiState.value = UtilitiesUiState.Scanning("faces")
                _scanProgress.value = ScanProgress("Clearing cache...", 0, 0)

                // Clear all face cache data
                imageDao.clearAllFaceDetectionCache()

                val totalImages = imageDao.getImageCount()
                android.util.Log.d("FaceCache", "Force rebuild: Cleared cache, will scan $totalImages images")

                // Now run normal population
                _scanProgress.value = ScanProgress("Detecting faces...", 0, totalImages)

                val scannedCount = populateFaceDetectionCacheUseCase.execute { current, total, _ ->
                    _scanProgress.value = ScanProgress(
                        "Scanning faces... $current/$total",
                        current,
                        total
                    )
                }

                withContext(Dispatchers.Main) {
                    _uiState.value = UtilitiesUiState.ScanComplete(
                        "Force rebuild complete! Scanned $scannedCount photos.",
                        scannedCount
                    )
                    _scanProgress.value = null
                }

            } catch (e: Exception) {
                android.util.Log.e("FaceCache", "Error force rebuilding cache", e)
                withContext(Dispatchers.Main) {
                    _uiState.value = UtilitiesUiState.Error(
                        e.message ?: "Failed to rebuild face cache"
                    )
                    _scanProgress.value = null
                }
            }
        }
    }

    /**
     * Manual scan for new photos
     */
    fun scanForPhotos() {
        viewModelScope.launch(Dispatchers.IO) {
            try {
                _uiState.value = UtilitiesUiState.Scanning("photos")
                _scanProgress.value = ScanProgress("Scanning device...", 0, 0)

                val beforeCount = imageDao.getImageCount()

                imageRepository.ingestImagesWithProgress { current, total ->
                    _scanProgress.value = ScanProgress(
                        "Found $current photos...",
                        current,
                        total
                    )
                }

                val afterCount = imageDao.getImageCount()
                val newPhotos = afterCount - beforeCount

                withContext(Dispatchers.Main) {
                    _uiState.value = UtilitiesUiState.ScanComplete(
                        "Found $newPhotos new photos",
                        newPhotos
                    )
                    _scanProgress.value = null
                }

            } catch (e: Exception) {
                withContext(Dispatchers.Main) {
                    _uiState.value = UtilitiesUiState.Error(
                        e.message ?: "Failed to scan photos"
                    )
                    _scanProgress.value = null
                }
            }
        }
    }

    /**
     * Detect duplicate photos
     */
    fun detectDuplicates() {
        viewModelScope.launch(Dispatchers.IO) {
            try {
                _uiState.value = UtilitiesUiState.Scanning("duplicates")
                _scanProgress.value = ScanProgress("Analyzing photos...", 0, 0)

                val allImages = imageDao.getAllImages()
                val duplicateGroups = mutableListOf<DuplicateGroup>()

                // Group by SHA256
                val sha256Groups = allImages.groupBy { it.sha256 }

                var processed = 0
                sha256Groups.forEach { (sha256, images) ->
                    if (images.size > 1) {
                        // Found duplicates!
                        duplicateGroups.add(
                            DuplicateGroup(
                                images = images,
                                reason = "Exact duplicate (same file content)",
                                confidence = 1.0f
                            )
                        )
                    }
                    processed++

                    if (processed % 100 == 0) {
                        _scanProgress.value = ScanProgress(
                            "Checked $processed photos...",
                            processed,
                            sha256Groups.size
                        )
                    }
                }

                // Tag duplicates
                val duplicateTag = getOrCreateTag("duplicate", "SYSTEM")
                duplicateGroups.forEach { group ->
                    // Tag all but the first image (keep one, mark rest as dupes)
                    group.images.drop(1).forEach { image ->
                        tagImage(image.imageId, duplicateTag.tagId)
                    }
                }

                withContext(Dispatchers.Main) {
                    _uiState.value = UtilitiesUiState.DuplicatesFound(duplicateGroups)
                    _scanProgress.value = null
                }

            } catch (e: Exception) {
                withContext(Dispatchers.Main) {
                    _uiState.value = UtilitiesUiState.Error(
                        e.message ?: "Failed to detect duplicates"
                    )
                    _scanProgress.value = null
                }
            }
        }
    }

    /**
     * Detect burst photos (rapid succession)
     * ALSO POPULATES FACE DETECTION CACHE for optimization
     */
    fun detectBursts() {
        viewModelScope.launch(Dispatchers.IO) {
            try {
                _uiState.value = UtilitiesUiState.Scanning("bursts")
                _scanProgress.value = ScanProgress("Analyzing timestamps...", 0, 0)

                val allImages = imageDao.getAllImagesSortedByTime()
                val burstGroups = mutableListOf<BurstGroup>()

                // Group photos taken within 2 seconds of each other
                val burstThresholdMs = 2000L
                var currentBurst = mutableListOf<ImageEntity>()

                allImages.forEachIndexed { index, image ->
                    if (currentBurst.isEmpty()) {
                        currentBurst.add(image)
                    } else {
                        val lastImage = currentBurst.last()
                        val timeDiff = abs(image.capturedAt - lastImage.capturedAt)

                        if (timeDiff <= burstThresholdMs) {
                            // Part of current burst
                            currentBurst.add(image)
                        } else {
                            // End of burst
                            if (currentBurst.size >= 3) {
                                // Only consider bursts with 3+ photos
                                burstGroups.add(
                                    BurstGroup(
                                        images = currentBurst.toList(),
                                        burstId = UUID.randomUUID().toString(),
                                        representativeIndex = currentBurst.size / 2 // Middle photo
                                    )
                                )
                            }
                            currentBurst = mutableListOf(image)
                        }
                    }

                    if (index % 100 == 0) {
                        _scanProgress.value = ScanProgress(
                            "Checked $index photos...",
                            index,
                            allImages.size
                        )
                    }
                }

                // Check last burst
                if (currentBurst.size >= 3) {
                    burstGroups.add(
                        BurstGroup(
                            images = currentBurst,
                            burstId = UUID.randomUUID().toString(),
                            representativeIndex = currentBurst.size / 2
                        )
                    )
                }

                // Tag bursts
                val burstTag = getOrCreateTag("burst", "SYSTEM")
                burstGroups.forEach { group ->
                    group.images.forEach { image ->
                        tagImage(image.imageId, burstTag.tagId)

                        // Tag the representative photo specially
                        if (image == group.images[group.representativeIndex]) {
                            val burstRepTag = getOrCreateTag("burst_representative", "SYSTEM")
                            tagImage(image.imageId, burstRepTag.tagId)
                        }
                    }
                }

                // OPTIMIZATION: Populate face detection cache for burst photos
                // Burst photos often contain people, so cache this for future scans
                _scanProgress.value = ScanProgress("Caching face detection data...", 0, 0)

                val faceDetector = FaceDetection.getClient(
                    FaceDetectorOptions.Builder()
                        .setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_FAST)
                        .setMinFaceSize(0.15f)
                        .build()
                )

                var cached = 0
                burstGroups.forEach { group ->
                    group.images.forEach { imageEntity ->
                        // Only populate cache if not already cached
                        if (imageEntity.needsFaceDetection()) {
                            try {
                                val uri = Uri.parse(imageEntity.imageUri)
                                val faceCount = detectFaceCountQuick(uri, faceDetector)

                                imageDao.updateFaceDetectionCache(
                                    imageId = imageEntity.imageId,
                                    hasFaces = faceCount > 0,
                                    faceCount = faceCount
                                )
                                cached++
                            } catch (e: Exception) {
                                // Skip on error
                            }
                        }
                    }
                }

                faceDetector.close()

                withContext(Dispatchers.Main) {
                    _uiState.value = UtilitiesUiState.BurstsFound(burstGroups)
                    _scanProgress.value = null
                }

            } catch (e: Exception) {
                withContext(Dispatchers.Main) {
                    _uiState.value = UtilitiesUiState.Error(
                        e.message ?: "Failed to detect bursts"
                    )
                    _scanProgress.value = null
                }
            }
        }
    }

    /**
     * Quick face count detection (lightweight, doesn't extract faces)
     * Used for populating cache during utility scans
     */
    private suspend fun detectFaceCountQuick(
        uri: Uri,
        detector: com.google.mlkit.vision.face.FaceDetector
    ): Int = withContext(Dispatchers.IO) {
        var bitmap: Bitmap? = null
        try {
            // Load bitmap at lower resolution for quick detection
            val options = android.graphics.BitmapFactory.Options().apply {
                inSampleSize = 4  // Quarter resolution for speed
                inPreferredConfig = android.graphics.Bitmap.Config.RGB_565
            }

            bitmap = imageRepository.loadBitmap(uri, options)
            if (bitmap == null) return@withContext 0

            val image = InputImage.fromBitmap(bitmap, 0)
            val faces = detector.process(image).await()
            faces.size

        } catch (e: Exception) {
            0
        } finally {
            bitmap?.recycle()
        }
    }

    /**
     * Detect screenshots and low quality photos
     */
    fun analyzeQuality() {
        viewModelScope.launch(Dispatchers.IO) {
            try {
                _uiState.value = UtilitiesUiState.Scanning("quality")
                _scanProgress.value = ScanProgress("Analyzing quality...", 0, 0)

                val allImages = imageDao.getAllImages()
                val screenshotTag = getOrCreateTag("screenshot", "SYSTEM")
                val blurryTag = getOrCreateTag("blurry", "SYSTEM")

                var screenshotCount = 0
                var blurryCount = 0

                allImages.forEachIndexed { index, image ->
                    // Detect screenshots by dimensions (screen-sized)
                    val isScreenshot = isLikelyScreenshot(image.width, image.height)
                    if (isScreenshot) {
                        tagImage(image.imageId, screenshotTag.tagId)
                        screenshotCount++
                    }

                    // TODO: Detect blurry photos (requires bitmap analysis)
                    // For now, skip blur detection

                    if (index % 50 == 0) {
                        _scanProgress.value = ScanProgress(
                            "Analyzed $index photos...",
                            index,
                            allImages.size
                        )
                    }
                }

                withContext(Dispatchers.Main) {
                    _uiState.value = UtilitiesUiState.QualityAnalysisComplete(
                        screenshots = screenshotCount,
                        blurry = blurryCount
                    )
                    _scanProgress.value = null
                }

            } catch (e: Exception) {
                withContext(Dispatchers.Main) {
                    _uiState.value = UtilitiesUiState.Error(
                        e.message ?: "Failed to analyze quality"
                    )
                    _scanProgress.value = null
                }
            }
        }
    }

    /**
     * Detect screenshots by common screen dimensions
     */
    private fun isLikelyScreenshot(width: Int, height: Int): Boolean {
        val commonScreenRatios = listOf(
            16.0 / 9.0,   // 1080x1920, 1440x2560
            19.5 / 9.0,   // 1080x2340 (iPhone X)
            20.0 / 9.0,   // 1080x2400
            18.5 / 9.0,   // 1080x2220
            19.0 / 9.0    // 1080x2280
        )

        val imageRatio = if (width > height) {
            width.toDouble() / height.toDouble()
        } else {
            height.toDouble() / width.toDouble()
        }

        return commonScreenRatios.any { screenRatio ->
            abs(imageRatio - screenRatio) < 0.1
        }
    }

    private suspend fun getOrCreateTag(value: String, type: String): TagEntity {
        return tagDao.getByValue(value) ?: run {
            val tag = TagEntity(
                tagId = UUID.randomUUID().toString(),
                type = type,
                value = value,
                createdAt = System.currentTimeMillis()
            )
            tagDao.insert(tag)
            tag
        }
    }

    private suspend fun tagImage(imageId: String, tagId: String) {
        val imageTag = ImageTagEntity(
            imageId = imageId,
            tagId = tagId,
            source = "AUTO",
            confidence = 1.0f,
            visibility = "PUBLIC",
            createdAt = System.currentTimeMillis()
        )
        imageTagDao.insert(imageTag)
    }

    fun resetState() {
        _uiState.value = UtilitiesUiState.Idle
        _scanProgress.value = null
    }
}

/**
 * UI State
 */
sealed class UtilitiesUiState {
    object Idle : UtilitiesUiState()
    data class Scanning(val type: String) : UtilitiesUiState()
    data class ScanComplete(val message: String, val count: Int) : UtilitiesUiState()
    data class DuplicatesFound(val groups: List<DuplicateGroup>) : UtilitiesUiState()
    data class BurstsFound(val groups: List<BurstGroup>) : UtilitiesUiState()
    data class QualityAnalysisComplete(
        val screenshots: Int,
        val blurry: Int
    ) : UtilitiesUiState()
    data class Error(val message: String) : UtilitiesUiState()
}

data class ScanProgress(
    val message: String,
    val current: Int,
    val total: Int
)

data class DuplicateGroup(
    val images: List<ImageEntity>,
    val reason: String,
    val confidence: Float
)

data class BurstGroup(
    val images: List<ImageEntity>,
    val burstId: String,
    val representativeIndex: Int // Which photo to show in albums
)