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) {
            val cosineSim = cosineSimilarity(embedding, student.embedding)
            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)
        }
    }

    /**
     * 查找所有可能的匹配（返回多个候选）
     */
    fun findAllMatches(
        embedding: FloatArray,
        students: List<Student>,
        topK: Int = 3
    ): List<MatchCandidate> {
        val candidates = students.map { student ->
            val cosineSim = cosineSimilarity(embedding, student.embedding)
            MatchCandidate(
                student = student,
                euclideanDistance = euclideanDistanceFromCosine(cosineSim),
                cosineSimilarity = cosineSim
            )
        }

        return candidates.sortedBy { it.euclideanDistance }.take(topK)
    }

    /**
     * 计算欧氏距离
     */
    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)
    }

    /**
     * 对已 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)
        }

        // 计算所有学生两两之间的距离（采样）
        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()

        // 建议使用较小百分位作为阈值（假设同一个人多次录入的距离较小）
        val p10Index = (distances.size * 0.1).toInt().coerceIn(0, distances.size - 1)
        val p20Index = (distances.size * 0.2).toInt().coerceIn(0, distances.size - 1)

        return SuggestedThresholds(
            euclidean = distances[p10Index],
            cosine = 1f - distances[p20Index] * 0.5f
        )
    }
}

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

/**
 * 匹配候选
 */
data class MatchCandidate(
    val student: Student,
    val euclideanDistance: Float,
    val cosineSimilarity: Float
)

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