package com.placeholder.sherpai2.domain.clustering

import android.graphics.Rect
import android.util.Log
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.sqrt

/**
 * ClusterQualityAnalyzer - Validate cluster quality BEFORE training
 *
 * RELAXED THRESHOLDS for real-world photos (social media, distant shots):
 * - Face size: 3% (down from 15%)
 * - Outlier threshold: 65% (down from 75%)
 * - GOOD tier: 75% (down from 85%)
 * - EXCELLENT tier: 85% (down from 95%)
 */
@Singleton
class ClusterQualityAnalyzer @Inject constructor() {

    companion object {
        private const val TAG = "ClusterQuality"
        private const val MIN_SOLO_PHOTOS = 6
        private const val MIN_FACE_SIZE_RATIO = 0.03f  // 3% of image (RELAXED)
        private const val MIN_FACE_DIMENSION_PIXELS = 50  // 50px minimum (RELAXED)
        private const val FALLBACK_MIN_DIMENSION = 80  // Fallback when no dimensions
        private const val MIN_INTERNAL_SIMILARITY = 0.75f
        private const val OUTLIER_THRESHOLD = 0.65f  // RELAXED
        private const val EXCELLENT_THRESHOLD = 0.85f  // RELAXED
        private const val GOOD_THRESHOLD = 0.75f  // RELAXED
    }

    fun analyzeCluster(cluster: FaceCluster): ClusterQualityResult {
        Log.d(TAG, "========================================")
        Log.d(TAG, "Analyzing cluster ${cluster.clusterId}")
        Log.d(TAG, "Total faces: ${cluster.faces.size}")

        // Step 1: Filter to solo photos
        val soloFaces = cluster.faces.filter { it.faceCount == 1 }
        Log.d(TAG, "Solo photos: ${soloFaces.size}")

        // Step 2: Filter by face size
        val largeFaces = soloFaces.filter { face ->
            isFaceLargeEnough(face)
        }
        Log.d(TAG, "Large faces (>= 3%): ${largeFaces.size}")

        if (largeFaces.size < soloFaces.size) {
            Log.d(TAG, "⚠️ Filtered out ${soloFaces.size - largeFaces.size} small faces")
        }

        // Step 3: Calculate internal consistency
        val (avgSimilarity, outliers) = analyzeInternalConsistency(largeFaces)

        // Step 4: Clean faces
        val cleanFaces = largeFaces.filter { it !in outliers }
        Log.d(TAG, "Clean faces: ${cleanFaces.size}")

        // Step 5: Calculate quality score
        val qualityScore = calculateQualityScore(
            soloPhotoCount = soloFaces.size,
            largeFaceCount = largeFaces.size,
            cleanFaceCount = cleanFaces.size,
            avgSimilarity = avgSimilarity,
            totalFaces = cluster.faces.size
        )
        Log.d(TAG, "Quality score: ${(qualityScore * 100).toInt()}%")

        // Step 6: Determine quality tier
        val qualityTier = when {
            qualityScore >= EXCELLENT_THRESHOLD -> ClusterQualityTier.EXCELLENT
            qualityScore >= GOOD_THRESHOLD -> ClusterQualityTier.GOOD
            else -> ClusterQualityTier.POOR
        }
        Log.d(TAG, "Quality tier: $qualityTier")

        val canTrain = qualityTier != ClusterQualityTier.POOR && cleanFaces.size >= MIN_SOLO_PHOTOS
        Log.d(TAG, "Can train: $canTrain")
        Log.d(TAG, "========================================")

        return ClusterQualityResult(
            originalFaceCount = cluster.faces.size,
            soloPhotoCount = soloFaces.size,
            largeFaceCount = largeFaces.size,
            cleanFaceCount = cleanFaces.size,
            avgInternalSimilarity = avgSimilarity,
            outlierFaces = outliers,
            cleanFaces = cleanFaces,
            qualityScore = qualityScore,
            qualityTier = qualityTier,
            canTrain = canTrain,
            warnings = generateWarnings(soloFaces.size, largeFaces.size, cleanFaces.size, qualityTier, avgSimilarity)
        )
    }

    private fun isFaceLargeEnough(face: DetectedFaceWithEmbedding): Boolean {
        val faceArea = face.boundingBox.width() * face.boundingBox.height()

        // Check 1: Absolute minimum
        if (face.boundingBox.width() < MIN_FACE_DIMENSION_PIXELS ||
            face.boundingBox.height() < MIN_FACE_DIMENSION_PIXELS) {
            return false
        }

        // Check 2: Relative size if we have dimensions
        if (face.imageWidth > 0 && face.imageHeight > 0) {
            val imageArea = face.imageWidth * face.imageHeight
            val faceRatio = faceArea.toFloat() / imageArea.toFloat()
            return faceRatio >= MIN_FACE_SIZE_RATIO
        }

        // Fallback: Use absolute size
        return face.boundingBox.width() >= FALLBACK_MIN_DIMENSION &&
                face.boundingBox.height() >= FALLBACK_MIN_DIMENSION
    }

    private fun analyzeInternalConsistency(
        faces: List<DetectedFaceWithEmbedding>
    ): Pair<Float, List<DetectedFaceWithEmbedding>> {
        if (faces.size < 2) {
            Log.d(TAG, "Less than 2 faces, skipping consistency check")
            return 0f to emptyList()
        }

        Log.d(TAG, "Analyzing ${faces.size} faces for internal consistency")

        val centroid = calculateCentroid(faces.map { it.embedding })

        val centroidSum = centroid.sum()
        Log.d(TAG, "Centroid sum: $centroidSum, first5=[${centroid.take(5).joinToString()}]")

        val similarities = faces.mapIndexed { index, face ->
            val similarity = cosineSimilarity(face.embedding, centroid)
            Log.d(TAG, "Face $index similarity to centroid: $similarity")
            face to similarity
        }

        val avgSimilarity = similarities.map { it.second }.average().toFloat()
        Log.d(TAG, "Average internal similarity: $avgSimilarity")

        val outliers = similarities
            .filter { (_, similarity) -> similarity < OUTLIER_THRESHOLD }
            .map { (face, _) -> face }

        Log.d(TAG, "Found ${outliers.size} outliers (threshold=$OUTLIER_THRESHOLD)")

        return avgSimilarity to outliers
    }

    private fun calculateCentroid(embeddings: List<FloatArray>): FloatArray {
        val size = embeddings.first().size
        val centroid = FloatArray(size) { 0f }

        embeddings.forEach { embedding ->
            for (i in embedding.indices) {
                centroid[i] += embedding[i]
            }
        }

        val count = embeddings.size.toFloat()
        for (i in centroid.indices) {
            centroid[i] /= count
        }

        val norm = sqrt(centroid.map { it * it }.sum())
        return if (norm > 0) {
            centroid.map { it / norm }.toFloatArray()
        } else {
            centroid
        }
    }

    private fun cosineSimilarity(a: FloatArray, b: FloatArray): Float {
        var dotProduct = 0f
        var normA = 0f
        var normB = 0f

        for (i in a.indices) {
            dotProduct += a[i] * b[i]
            normA += a[i] * a[i]
            normB += b[i] * b[i]
        }

        return dotProduct / (sqrt(normA) * sqrt(normB))
    }

    private fun calculateQualityScore(
        soloPhotoCount: Int,
        largeFaceCount: Int,
        cleanFaceCount: Int,
        avgSimilarity: Float,
        totalFaces: Int
    ): Float {
        val soloRatio = soloPhotoCount.toFloat() / totalFaces.toFloat().coerceAtLeast(1f)
        val soloPhotoScore = soloRatio.coerceIn(0f, 1f) * 0.25f

        val largeFaceScore = (largeFaceCount.toFloat() / 15f).coerceIn(0f, 1f) * 0.25f

        val cleanFaceScore = (cleanFaceCount.toFloat() / 10f).coerceIn(0f, 1f) * 0.20f

        val similarityScore = avgSimilarity * 0.30f

        return soloPhotoScore + largeFaceScore + cleanFaceScore + similarityScore
    }

    private fun generateWarnings(
        soloPhotoCount: Int,
        largeFaceCount: Int,
        cleanFaceCount: Int,
        qualityTier: ClusterQualityTier,
        avgSimilarity: Float
    ): List<String> {
        val warnings = mutableListOf<String>()

        when (qualityTier) {
            ClusterQualityTier.POOR -> {
                warnings.add("⚠️ POOR QUALITY - This cluster may contain multiple people!")
                warnings.add("Do NOT train on this cluster - it will create a bad model.")

                if (avgSimilarity < 0.70f) {
                    warnings.add("Low internal similarity (${(avgSimilarity * 100).toInt()}%) suggests mixed identities.")
                }
            }
            ClusterQualityTier.GOOD -> {
                warnings.add("⚠️ Review outlier faces before training")

                if (cleanFaceCount < 10) {
                    warnings.add("Consider adding more high-quality photos for better results.")
                }
            }
            ClusterQualityTier.EXCELLENT -> {
                // No warnings
            }
        }

        if (soloPhotoCount < MIN_SOLO_PHOTOS) {
            warnings.add("Need at least $MIN_SOLO_PHOTOS solo photos (have $soloPhotoCount)")
        }

        if (largeFaceCount < 6) {
            warnings.add("Only $largeFaceCount photos with large/clear faces (prefer 10+)")
            warnings.add("Tip: Use close-up photos where the face is clearly visible")
        }

        if (cleanFaceCount < 6) {
            warnings.add("After removing outliers: only $cleanFaceCount clean faces (need 6+)")
        }

        if (qualityTier == ClusterQualityTier.EXCELLENT) {
            warnings.add("✅ Excellent quality! This cluster is ready for training.")
            warnings.add("High-quality photos with consistent facial features detected.")
        }

        return warnings
    }
}

data class ClusterQualityResult(
    val originalFaceCount: Int,
    val soloPhotoCount: Int,
    val largeFaceCount: Int,
    val cleanFaceCount: Int,
    val avgInternalSimilarity: Float,
    val outlierFaces: List<DetectedFaceWithEmbedding>,
    val cleanFaces: List<DetectedFaceWithEmbedding>,
    val qualityScore: Float,
    val qualityTier: ClusterQualityTier,
    val canTrain: Boolean,
    val warnings: List<String>
) {
    fun getSummary(): String = when (qualityTier) {
        ClusterQualityTier.EXCELLENT ->
            "Excellent quality cluster with $cleanFaceCount high-quality photos ready for training."
        ClusterQualityTier.GOOD ->
            "Good quality cluster with $cleanFaceCount usable photos. Review outliers before training."
        ClusterQualityTier.POOR ->
            "Poor quality cluster. May contain multiple people or low-quality photos. Add more photos or split cluster."
    }
}

enum class ClusterQualityTier {
    EXCELLENT,  // 85%+
    GOOD,       // 75-84%
    POOR        // <75%
}