"""
==================================================
Smart Attendance AI
Recognizer Service
==================================================
Compares an unknown face embedding against
registered student embeddings.
==================================================
"""

import time

from typing import List

from config.settings import settings

from core.image_data import ImageData
from core.student_embedding import StudentEmbedding
from core.recognition_result import RecognitionResult

from services.inference_service import InferenceService
from services.embedding_service import EmbeddingService
from services.similarity_service import SimilarityService

from utils.exceptions import (
    NoFaceDetectedException,
    MultipleFacesDetectedException
)


class RecognizerService:

    # ==========================================
    # Recognize Student
    # ==========================================

    @classmethod
    def recognize(
        cls,
        image: ImageData,
        students: List[StudentEmbedding]
    ) -> RecognitionResult:
        """
        Compare an unknown face against all
        registered student embeddings.
        """

        start_time = time.perf_counter()

        # --------------------------------------
        # Detect Face
        # --------------------------------------

        faces = InferenceService.infer(
            image
        )

        if len(faces) == 0:

            raise NoFaceDetectedException()

        if len(faces) > 1:

            raise MultipleFacesDetectedException()

        # --------------------------------------
        # Generate Face Embedding
        # --------------------------------------

        embedding_result = (
            EmbeddingService.extract(
                faces
            )[0]
        )

        unknown_embedding = (
            embedding_result.embedding
        )

        # --------------------------------------
        # Compare Against Students
        # --------------------------------------

        best_student = None

        best_similarity = -1.0

        for student in students:

            similarity = (
                SimilarityService.cosine_similarity(
                    unknown_embedding,
                    student.embedding
                )
            )

            if similarity > best_similarity:

                best_similarity = similarity

                best_student = student

        # --------------------------------------
        # Similarity Threshold
        # --------------------------------------

        matched = (
            best_similarity >=
            settings.SIMILARITY_THRESHOLD
        )

        # --------------------------------------
        # Processing Time
        # --------------------------------------

        processing_time_ms = int(
            (
                time.perf_counter()
                - start_time
            ) * 1000
        )

        # --------------------------------------
        # Recognition Result
        # --------------------------------------

        return RecognitionResult(

            student_id=(
                best_student.student_id
                if matched and best_student
                else None
            ),

            matched=matched,

            similarity=best_similarity,

            embedding=unknown_embedding,

            confidence=embedding_result.confidence,

            bbox=embedding_result.bbox,

            face_width=embedding_result.face_width,

            face_height=embedding_result.face_height,

            landmarks=embedding_result.landmarks,

            processing_time_ms=processing_time_ms

        )