package com.placeholder.sherpai2.domain.clustering

import com.google.mlkit.vision.face.Face
import com.google.mlkit.vision.face.FaceLandmark
import kotlin.math.abs
import kotlin.math.pow
import kotlin.math.sqrt

/**
 * FaceQualityFilter - Quality filtering for face detection
 *
 * PURPOSE:
 * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 * Two modes with different strictness:
 * 1. Discovery: RELAXED (we want to find people, be permissive)
 * 2. Scanning: MINIMAL (only reject obvious garbage)
 *
 * FILTERS OUT:
 * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 * ✅ Ghost faces (no eyes detected)
 * ✅ Tiny faces (< 10% of image)
 * ✅ Extreme angles (> 45°)
 * ⚠️ Side profiles (both eyes required)
 *
 * ALLOWS:
 * ✅ Moderate angles (up to 45°)
 * ✅ Faces without tracking ID (not reliable)
 * ✅ Faces without nose (some angles don't show nose)
 */
object FaceQualityFilter {

    /**
     * Age group estimation for filtering (child vs adult detection)
     */
    enum class AgeGroup { CHILD, ADULT, UNCERTAIN }

    /**
     * Estimate whether a face belongs to a child or adult based on facial proportions.
     *
     * Uses two heuristics:
     * 1. Eye position ratio - Children have larger foreheads, so eyes are lower (~45% from top)
     *    Adults have eyes at ~35% from top
     * 2. Face roundness (width/height ratio) - Children: ~0.85-1.0, Adults: ~0.7-0.85
     *
     * @return AgeGroup.CHILD, AgeGroup.ADULT, or AgeGroup.UNCERTAIN
     */
    fun estimateAgeGroup(face: Face, imageWidth: Int, imageHeight: Int): AgeGroup {
        val leftEye = face.getLandmark(FaceLandmark.LEFT_EYE)
        val rightEye = face.getLandmark(FaceLandmark.RIGHT_EYE)

        if (leftEye == null || rightEye == null) {
            return AgeGroup.UNCERTAIN
        }

        // Eye-to-face height ratio (where eyes sit relative to face top)
        val faceHeight = face.boundingBox.height().toFloat()
        val faceTop = face.boundingBox.top.toFloat()
        val eyeY = (leftEye.position.y + rightEye.position.y) / 2
        val eyePositionRatio = (eyeY - faceTop) / faceHeight

        // Children: eyes at ~45% from top (larger forehead proportionally)
        // Adults: eyes at ~35% from top
        // Score: higher = more child-like

        // Face roundness (width/height)
        val faceWidth = face.boundingBox.width().toFloat()
        val faceRatio = faceWidth / faceHeight
        // Children: ratio ~0.85-1.0 (rounder faces)
        // Adults: ratio ~0.7-0.85 (longer/narrower faces)

        var childScore = 0

        // Eye position scoring
        if (eyePositionRatio > 0.45f) childScore += 2  // Strong child signal
        else if (eyePositionRatio > 0.42f) childScore += 1  // Mild child signal
        else if (eyePositionRatio < 0.35f) childScore -= 1  // Adult signal

        // Face roundness scoring
        if (faceRatio > 0.90f) childScore += 2  // Very round = child
        else if (faceRatio > 0.82f) childScore += 1  // Somewhat round
        else if (faceRatio < 0.75f) childScore -= 1  // Long face = adult

        return when {
            childScore >= 3 -> AgeGroup.CHILD
            childScore <= 0 -> AgeGroup.ADULT
            else -> AgeGroup.UNCERTAIN
        }
    }

    /**
     * Validate face for Discovery/Clustering
     *
     * RELAXED thresholds - we want to find people, not reject everything
     */
    fun validateForDiscovery(
        face: Face,
        imageWidth: Int,
        imageHeight: Int
    ): FaceQualityValidation {
        val issues = mutableListOf<String>()

        // ===== CHECK 1: Eye Detection (CRITICAL) =====
        val leftEye = face.getLandmark(FaceLandmark.LEFT_EYE)
        val rightEye = face.getLandmark(FaceLandmark.RIGHT_EYE)

        if (leftEye == null || rightEye == null) {
            issues.add("Missing eye landmarks")
            return FaceQualityValidation(false, issues, 0f)
        }

        // ===== CHECK 2: Head Pose (RELAXED - 45°) =====
        val headEulerAngleY = face.headEulerAngleY
        val headEulerAngleZ = face.headEulerAngleZ
        val headEulerAngleX = face.headEulerAngleX

        if (abs(headEulerAngleY) > 45f) {
            issues.add("Head turned too far")
        }

        if (abs(headEulerAngleZ) > 45f) {
            issues.add("Head tilted too much")
        }

        if (abs(headEulerAngleX) > 40f) {
            issues.add("Head angle too extreme")
        }

        // ===== CHECK 3: Face Size (RELAXED - 10%) =====
        val faceWidthRatio = face.boundingBox.width() / imageWidth.toFloat()
        val faceHeightRatio = face.boundingBox.height() / imageHeight.toFloat()

        if (faceWidthRatio < 0.10f) {
            issues.add("Face too small")
        }

        if (faceHeightRatio < 0.10f) {
            issues.add("Face too small")
        }

        // ===== CHECK 4: Eye Distance (OPTIONAL) =====
        if (leftEye != null && rightEye != null) {
            val eyeDistance = sqrt(
                (rightEye.position.x - leftEye.position.x).toDouble().pow(2.0) +
                        (rightEye.position.y - leftEye.position.y).toDouble().pow(2.0)
            ).toFloat()

            val eyeDistanceRatio = eyeDistance / face.boundingBox.width()
            if (eyeDistanceRatio < 0.15f || eyeDistanceRatio > 0.65f) {
                issues.add("Abnormal eye spacing")
            }
        }

        // ===== CONFIDENCE SCORE =====
        val poseScore = 1f - (abs(headEulerAngleY) + abs(headEulerAngleZ) + abs(headEulerAngleX)) / 270f
        val sizeScore = (faceWidthRatio + faceHeightRatio) / 2f
        val nose = face.getLandmark(FaceLandmark.NOSE_BASE)
        val landmarkScore = if (nose != null) 1f else 0.8f

        val confidenceScore = (poseScore * 0.4f + sizeScore * 0.3f + landmarkScore * 0.3f).coerceIn(0f, 1f)

        // ===== VERDICT (RELAXED - 0.5 threshold) =====
        val isValid = issues.isEmpty() && confidenceScore >= 0.5f

        return FaceQualityValidation(isValid, issues, confidenceScore)
    }

    /**
     * Quick check for scanning phase (permissive)
     */
    fun validateForScanning(
        face: Face,
        imageWidth: Int,
        imageHeight: Int
    ): Boolean {
        val leftEye = face.getLandmark(FaceLandmark.LEFT_EYE)
        val rightEye = face.getLandmark(FaceLandmark.RIGHT_EYE)

        if (leftEye == null && rightEye == null) {
            return false
        }

        val faceWidthRatio = face.boundingBox.width() / imageWidth.toFloat()
        if (faceWidthRatio < 0.08f) {
            return false
        }

        return true
    }
}

data class FaceQualityValidation(
    val isValid: Boolean,
    val issues: List<String>,
    val confidenceScore: Float
) {
    val passesStrictValidation: Boolean get() = isValid && confidenceScore >= 0.7f
    val passesModerateValidation: Boolean get() = isValid && confidenceScore >= 0.5f
}