package com.example.studentfaceregistry.face

import com.example.studentfaceregistry.data.Student
import kotlin.math.sqrt

/**
 * 人脸比对器
 * 支持多种相似度计算方式和自适应阈值
 */
class FaceMatcher(
    private val euclideanThreshold: Float = 1.0f,
    private val cosineThreshold: Float = 0.6f
) {
    /**
     * 查找最匹配的学生（使用余弦相似度）
     * @param embedding 待识别的人脸特征
     * @param students 已注册的学生列表
     * @return 识别结果
     */
    fun findNearest(embedding: FloatArray, students: List<Student>): RecognitionResult {
        if (students.isEmpty()) {
            return RecognitionResult(null, Float.MAX_VALUE, 0f, MatchType.NO_DATA)
        }

        var nearest: Student? = null
        var nearestEuclidean = Float.MAX_VALUE
        var nearestCosine = -Float.MAX_VALUE
        var secondNearestEuclidean = Float.MAX_VALUE

        for (student in students) {
            // 对该学生存储的所有特征（主特征 + 多角度补充特征）取最高相似度
            var bestCosine = -Float.MAX_VALUE
            for (candidate in student.allEmbeddings()) {
                val cosineSim = cosineSimilarity(embedding, candidate)
                if (cosineSim > bestCosine) bestCosine = cosineSim
            }
            val cosineSim = bestCosine
            val euclideanDist = euclideanDistanceFromCosine(cosineSim)

            if (euclideanDist < nearestEuclidean) {
                secondNearestEuclidean = nearestEuclidean
                nearest = student
                nearestEuclidean = euclideanDist
                nearestCosine = cosineSim
            } else if (euclideanDist < secondNearestEuclidean) {
                secondNearestEuclidean = euclideanDist
            }
        }

        // 判断匹配类型
        val matchType = determineMatchType(nearestEuclidean, nearestCosine, secondNearestEuclidean)

        return if (nearest != null && (matchType == MatchType.MATCH || matchType == MatchType.UNSURE)) {
            RecognitionResult(nearest, nearestEuclidean, nearestCosine, matchType)
        } else {
            RecognitionResult(null, nearestEuclidean, nearestCosine, matchType)
        }
    }

    /**
     * 计算欧氏距离
     */
    private fun euclideanDistance(a: FloatArray, b: FloatArray): Float {
        var sum = 0f
        val len = minOf(a.size, b.size)
        for (i in 0 until len) {
            val diff = a[i] - b[i]
            sum += diff * diff
        }
        return sqrt(sum)
    }

    private fun Student.allEmbeddings(): List<FloatArray> = listOf(embedding) + extraEmbeddings

    /**
     * 对已 L2 归一化向量，通过余弦相似度推导欧氏距离，避免重复遍历
     */
    private fun euclideanDistanceFromCosine(cosineSimilarity: Float): Float {
        return sqrt((2f - 2f * cosineSimilarity).coerceAtLeast(0f))
    }

    /**
     * 计算余弦相似度
     * 对于已 L2 归一化的向量，余弦相似度等于点积
     */
    private fun cosineSimilarity(a: FloatArray, b: FloatArray): Float {
        var dotProduct = 0f
        val len = minOf(a.size, b.size)
        for (i in 0 until len) {
            dotProduct += a[i] * b[i]
        }
        // 限制在 [-1, 1] 范围内，避免浮点误差
        return dotProduct.coerceIn(-1f, 1f)
    }

    /**
     * 判断匹配类型
     */
    private fun determineMatchType(
        euclideanDist: Float,
        cosineSim: Float,
        secondNearestDist: Float
    ): MatchType {
        val euclideanMatch = euclideanDist <= euclideanThreshold
        val cosineMatch = cosineSim >= cosineThreshold
        val gap = secondNearestDist - euclideanDist
        val isClearlyBest = gap >= 0.12f

        return when {
            euclideanMatch && cosineMatch -> {
                MatchType.MATCH
            }
            (cosineMatch && isClearlyBest) || (euclideanMatch && isClearlyBest) -> {
                MatchType.MATCH
            }
            euclideanMatch || cosineMatch || cosineSim >= (cosineThreshold - 0.05f) -> {
                MatchType.UNSURE
            }
            else -> {
                MatchType.NO_MATCH
            }
        }
    }

    /**
     * 根据一组距离自适应调整阈值
     * 基于已注册学生两两之间的（异类）距离分布给出保守建议：
     * 阈值取异类距离分布的低分位，保证绝大多数异类对被正确拒绝。
     * 注意：本方法没有同人距离数据，只能给出"上界"参考，
     * 现场采集同人数据后可进一步放宽。
     */
    fun suggestThresholds(students: List<Student>): SuggestedThresholds {
        if (students.size < 3) {
            return SuggestedThresholds(euclideanThreshold, cosineThreshold)
        }

        // 计算所有学生两两之间的距离（采样，上限 200 对）
        val distances = mutableListOf<Float>()
        val sampleSize = minOf(students.size, 50)
        val step = maxOf(1, students.size / sampleSize)

        for (i in students.indices step step) {
            for (j in (i + 1) until students.size step step) {
                distances.add(euclideanDistance(students[i].embedding, students[j].embedding))
            }
            if (distances.size >= 200) break
        }

        if (distances.isEmpty()) {
            return SuggestedThresholds(euclideanThreshold, cosineThreshold)
        }

        distances.sort()

        // 使用低分位异类距离作为欧氏阈值上界：仅约 10% 的异类对会低于该距离
        val p10Index = (distances.size * 0.1).toInt().coerceIn(0, distances.size - 1)
        val p05Index = (distances.size * 0.05).toInt().coerceIn(0, distances.size - 1)
        val suggestedEuclidean = distances[p10Index]

        return SuggestedThresholds(
            euclidean = suggestedEuclidean,
            // 对 L2 归一化向量：cos = 1 - d^2 / 2，用更保守的 p05 距离推导余弦阈值
            cosine = 1f - (distances[p05Index] * distances[p05Index]) / 2f
        )
    }
}

/**
 * 匹配类型
 */
enum class MatchType {
    MATCH,      // 明确匹配
    UNSURE,     // 可能匹配，但不确定
    NO_MATCH,   // 不匹配
    NO_DATA     // 没有数据
}

/**
 * 建议的阈值
 */
data class SuggestedThresholds(
    val euclidean: Float,
    val cosine: Float
)
