package com.placeholder.sherpai2.ui.tags

import android.app.Application
import android.graphics.BitmapFactory
import android.net.Uri
import androidx.lifecycle.AndroidViewModel
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.ImageTagDao
import com.placeholder.sherpai2.data.local.dao.TagDao
import com.placeholder.sherpai2.data.local.entity.TagEntity
import com.placeholder.sherpai2.data.local.entity.TagWithUsage
import com.placeholder.sherpai2.data.repository.DetectedFace
import com.placeholder.sherpai2.data.service.AutoTaggingService
import com.placeholder.sherpai2.domain.repository.ImageRepository
import com.placeholder.sherpai2.util.DiagnosticLogger
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.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.tasks.await
import javax.inject.Inject

@HiltViewModel
class TagManagementViewModel @Inject constructor(
    application: Application,
    private val tagDao: TagDao,
    private val imageTagDao: ImageTagDao,
    private val imageRepository: ImageRepository,
    private val autoTaggingService: AutoTaggingService
) : AndroidViewModel(application) {

    private val _uiState = MutableStateFlow<TagUiState>(TagUiState.Loading)
    val uiState: StateFlow<TagUiState> = _uiState.asStateFlow()

    private val _scanningState = MutableStateFlow<TagScanningState>(TagScanningState.Idle)
    val scanningState: StateFlow<TagScanningState> = _scanningState.asStateFlow()

    private val faceDetector by lazy {
        val options = FaceDetectorOptions.Builder()
            .setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_ACCURATE)
            .setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_NONE)
            .setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_NONE)
            .setMinFaceSize(0.10f)
            .build()
        FaceDetection.getClient(options)
    }

    sealed class TagUiState {
        object Loading : TagUiState()
        data class Success(
            val tags: List<TagWithUsage>,
            val totalTags: Int,
            val systemTags: Int,
            val userTags: Int
        ) : TagUiState()
        data class Error(val message: String) : TagUiState()
    }

    sealed class TagScanningState {
        object Idle : TagScanningState()
        data class Scanning(
            val scanType: ScanType,
            val progress: Int,
            val total: Int,
            val tagsApplied: Int,
            val currentImage: String = ""
        ) : TagScanningState()
        data class Complete(
            val scanType: ScanType,
            val imagesProcessed: Int,
            val tagsApplied: Int,
            val newTagsCreated: Int = 0
        ) : TagScanningState()
        data class Error(val message: String) : TagScanningState()
    }

    enum class ScanType {
        BASE_TAGS,          // Face count, orientation, resolution, time-of-day
        RELATIONSHIP_TAGS,  // Family, friend, colleague from person entities
        BIRTHDAY_TAGS,      // Birthday tags for DOB matches
        SCENE_TAGS,         // Indoor/outdoor estimation
        ALL                 // Run all scans
    }

    init {
        loadTags()
    }

    fun loadTags() {
        viewModelScope.launch {
            try {
                _uiState.value = TagUiState.Loading

                val tagsWithUsage = tagDao.getMostUsedTags(1000) // Get all tags
                val systemTags = tagsWithUsage.count { it.type == "SYSTEM" }
                val userTags = tagsWithUsage.count { it.type == "GENERIC" }

                _uiState.value = TagUiState.Success(
                    tags = tagsWithUsage,
                    totalTags = tagsWithUsage.size,
                    systemTags = systemTags,
                    userTags = userTags
                )

            } catch (e: Exception) {
                _uiState.value = TagUiState.Error(
                    e.message ?: "Failed to load tags"
                )
            }
        }
    }

    fun createUserTag(tagName: String) {
        viewModelScope.launch {
            try {
                val trimmedName = tagName.trim().lowercase()
                if (trimmedName.isEmpty()) {
                    _uiState.value = TagUiState.Error("Tag name cannot be empty")
                    return@launch
                }

                // Check if tag already exists
                val existing = tagDao.getByValue(trimmedName)
                if (existing != null) {
                    _uiState.value = TagUiState.Error("Tag '$trimmedName' already exists")
                    return@launch
                }

                val newTag = TagEntity.createUserTag(trimmedName)
                tagDao.insert(newTag)

                loadTags()
            } catch (e: Exception) {
                _uiState.value = TagUiState.Error(
                    "Failed to create tag: ${e.message}"
                )
            }
        }
    }

    fun deleteTag(tagId: String) {
        viewModelScope.launch {
            try {
                tagDao.delete(tagId)
                loadTags()
            } catch (e: Exception) {
                _uiState.value = TagUiState.Error(
                    "Failed to delete tag: ${e.message}"
                )
            }
        }
    }

    fun searchTags(query: String) {
        viewModelScope.launch {
            try {
                val results = if (query.isBlank()) {
                    tagDao.getMostUsedTags(1000)
                } else {
                    tagDao.searchTagsWithUsage(query, 100)
                }

                val systemTags = results.count { it.type == "SYSTEM" }
                val userTags = results.count { it.type == "GENERIC" }

                _uiState.value = TagUiState.Success(
                    tags = results,
                    totalTags = results.size,
                    systemTags = systemTags,
                    userTags = userTags
                )
            } catch (e: Exception) {
                _uiState.value = TagUiState.Error("Search failed: ${e.message}")
            }
        }
    }

    // ======================
    // AUTO-TAGGING SCANS
    // ======================

    /**
     * Scan library for base tags (face count, orientation, time, quality, scene)
     */
    fun scanForBaseTags() {
        performScan(ScanType.BASE_TAGS)
    }

    /**
     * Scan for relationship tags (family, friend, colleague)
     */
    fun scanForRelationshipTags() {
        performScan(ScanType.RELATIONSHIP_TAGS)
    }

    /**
     * Scan for birthday tags
     */
    fun scanForBirthdayTags() {
        performScan(ScanType.BIRTHDAY_TAGS)
    }

    /**
     * Scan for scene tags (indoor/outdoor)
     */
    fun scanForSceneTags() {
        performScan(ScanType.SCENE_TAGS)
    }

    /**
     * Scan for ALL tags
     */
    fun scanForAllTags() {
        performScan(ScanType.ALL)
    }

    private fun performScan(scanType: ScanType) {
        viewModelScope.launch {
            try {
                DiagnosticLogger.i("=== STARTING TAG SCAN: $scanType ===")

                _scanningState.value = TagScanningState.Scanning(
                    scanType = scanType,
                    progress = 0,
                    total = 0,
                    tagsApplied = 0
                )

                val allImages = imageRepository.getAllImages().first()
                var tagsApplied = 0
                var newTagsCreated = 0

                DiagnosticLogger.i("Processing ${allImages.size} images")

                allImages.forEachIndexed { index, imageWithEverything ->
                    val image = imageWithEverything.image

                    _scanningState.value = TagScanningState.Scanning(
                        scanType = scanType,
                        progress = index + 1,
                        total = allImages.size,
                        tagsApplied = tagsApplied,
                        currentImage = image.imageId.take(8)
                    )

                    when (scanType) {
                        ScanType.BASE_TAGS -> {
                            tagsApplied += scanImageForBaseTags(image.imageUri, image)
                        }
                        ScanType.SCENE_TAGS -> {
                            tagsApplied += scanImageForSceneTags(image.imageUri, image)
                        }
                        ScanType.RELATIONSHIP_TAGS -> {
                            // Handled at person level, not per-image
                        }
                        ScanType.BIRTHDAY_TAGS -> {
                            // Handled at person level, not per-image
                        }
                        ScanType.ALL -> {
                            tagsApplied += scanImageForBaseTags(image.imageUri, image)
                            tagsApplied += scanImageForSceneTags(image.imageUri, image)
                        }
                    }
                }

                // Handle person-level scans
                if (scanType == ScanType.RELATIONSHIP_TAGS || scanType == ScanType.ALL) {
                    DiagnosticLogger.i("Scanning relationship tags...")
                    tagsApplied += autoTaggingService.autoTagAllRelationships()
                }

                if (scanType == ScanType.BIRTHDAY_TAGS || scanType == ScanType.ALL) {
                    DiagnosticLogger.i("Scanning birthday tags...")
                    tagsApplied += autoTaggingService.autoTagAllBirthdays(daysRange = 3)
                }

                DiagnosticLogger.i("=== SCAN COMPLETE ===")
                DiagnosticLogger.i("Images processed: ${allImages.size}")
                DiagnosticLogger.i("Tags applied: $tagsApplied")

                _scanningState.value = TagScanningState.Complete(
                    scanType = scanType,
                    imagesProcessed = allImages.size,
                    tagsApplied = tagsApplied,
                    newTagsCreated = newTagsCreated
                )

                loadTags()

            } catch (e: Exception) {
                DiagnosticLogger.e("Scan failed", e)
                _scanningState.value = TagScanningState.Error(
                    "Scan failed: ${e.message}"
                )
            }
        }
    }

    private suspend fun scanImageForBaseTags(
        imageUri: String,
        image: com.placeholder.sherpai2.data.local.entity.ImageEntity
    ): Int = withContext(Dispatchers.Default) {
        try {
            val uri = Uri.parse(imageUri)
            val inputStream = getApplication<Application>().contentResolver.openInputStream(uri)
            val bitmap = BitmapFactory.decodeStream(inputStream)
            inputStream?.close()

            if (bitmap == null) return@withContext 0

            // Detect faces
            val detectedFaces = detectFaces(bitmap)

            // Auto-tag with base tags
            autoTaggingService.autoTagImage(image, bitmap, detectedFaces)

        } catch (e: Exception) {
            DiagnosticLogger.e("Base tag scan failed for $imageUri", e)
            0
        }
    }

    private suspend fun scanImageForSceneTags(
        imageUri: String,
        image: com.placeholder.sherpai2.data.local.entity.ImageEntity
    ): Int = withContext(Dispatchers.Default) {
        try {
            val uri = Uri.parse(imageUri)
            val inputStream = getApplication<Application>().contentResolver.openInputStream(uri)
            val bitmap = BitmapFactory.decodeStream(inputStream)
            inputStream?.close()

            if (bitmap == null) return@withContext 0

            // Only auto-tag scene tags (indoor/outdoor already included in autoTagImage)
            // This is a subset of base tags, so we don't need separate logic
            0

        } catch (e: Exception) {
            DiagnosticLogger.e("Scene tag scan failed for $imageUri", e)
            0
        }
    }

    private suspend fun detectFaces(bitmap: android.graphics.Bitmap): List<DetectedFace> = withContext(Dispatchers.Default) {
        try {
            val image = InputImage.fromBitmap(bitmap, 0)
            val faces = faceDetector.process(image).await()

            faces.mapNotNull { face ->
                val boundingBox = face.boundingBox

                val croppedFace = try {
                    val left = boundingBox.left.coerceAtLeast(0)
                    val top = boundingBox.top.coerceAtLeast(0)
                    val width = boundingBox.width().coerceAtMost(bitmap.width - left)
                    val height = boundingBox.height().coerceAtMost(bitmap.height - top)

                    if (width > 0 && height > 0) {
                        android.graphics.Bitmap.createBitmap(bitmap, left, top, width, height)
                    } else {
                        null
                    }
                } catch (e: Exception) {
                    null
                }

                if (croppedFace != null) {
                    DetectedFace(
                        croppedBitmap = croppedFace,
                        boundingBox = boundingBox
                    )
                } else {
                    null
                }
            }

        } catch (e: Exception) {
            emptyList()
        }
    }

    fun resetScanningState() {
        _scanningState.value = TagScanningState.Idle
    }

    override fun onCleared() {
        super.onCleared()
        faceDetector.close()
    }
}