"""
Face Analysis Service - Ported from legacy system.
Provides face detection, smile detection, and light analysis using computer vision.
"""

import asyncio
import os
from typing import Any

import cv2
import numpy as np

from app.core.logging import get_logger
from app.services.analysis_utils import AnalysisUtils
from app.utils.light_utils import categorize_light
from app.utils.video_utils import foreach_frame

logger = get_logger("face_analysis_service")


class FaceAnalysisService:
    """
    Face Analysis Service - Ported from legacy system.
    Provides face detection, smile detection, and light analysis.
    """

    def __init__(self):
        """Initialize the face analysis service."""
        self.face_cascade = None
        self.smile_cascade = None
        self.faces_detected = 0
        self.smiles_detected = 0
        self.light_level = 0.0
        self.light_category = "unknown"

        # Initialize OpenCV cascades
        self._init_cascades()

    def _init_cascades(self):
        """Initialize Haar cascade classifiers."""
        try:
            # Face detection cascade
            face_path = self._get_model_path("frontface_default.xml")
            if os.path.exists(face_path):
                self.face_cascade = cv2.CascadeClassifier(face_path)
                logger.info("Face cascade loaded successfully")
            else:
                logger.warning("Face cascade not found")

            # Smile detection cascade
            smile_path = self._get_model_path("haarcascade_smile.xml")
            if os.path.exists(smile_path):
                self.smile_cascade = cv2.CascadeClassifier(smile_path)
                logger.info("Smile cascade loaded successfully")
            else:
                logger.warning("Smile cascade not found")

        except Exception as e:
            logger.error(f"Failed to initialize face analysis cascades: {e}")
            self.face_cascade = None
            self.smile_cascade = None

    def _get_model_path(self, model_name: str) -> str:
        """Get the path to a model file using shared helper."""
        return AnalysisUtils.get_resource_path("models", model_name)

    def detect_faces(self, frame: np.ndarray) -> list[tuple[int, int, int, int]]:
        """
        Detect faces in a frame using Haar cascade.

        Args:
            frame: Input frame (BGR format)

        Returns:
            List of face rectangles (x, y, w, h)
        """
        if self.face_cascade is None:
            return []

        try:
            # Convert to grayscale for face detection
            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

            # Detect faces
            faces = self.face_cascade.detectMultiScale(
                gray, scaleFactor=1.3, minNeighbors=5
            )

            return faces.tolist() if len(faces) > 0 else []

        except Exception as e:
            logger.warning(f"Face detection failed: {e}")
            return []

    def detect_smiles(
        self, frame: np.ndarray, face_rect: tuple[int, int, int, int]
    ) -> list[tuple[int, int, int, int]]:
        """
        Detect smiles within a face region.

        Args:
            frame: Input frame (BGR format)
            face_rect: Face rectangle (x, y, w, h)

        Returns:
            List of smile rectangles (x, y, w, h)
        """
        if self.smile_cascade is None:
            return []

        try:
            x, y, w, h = face_rect

            # Extract face region
            face_roi = frame[y : y + h, x : x + w]

            # Convert to grayscale
            gray_roi = cv2.cvtColor(face_roi, cv2.COLOR_BGR2GRAY)

            # Detect smiles within the face region
            smiles = self.smile_cascade.detectMultiScale(
                gray_roi, scaleFactor=1.6, minNeighbors=20
            )

            # Convert coordinates back to original frame
            smiles_in_frame = []
            for sx, sy, sw, sh in smiles:
                smiles_in_frame.append((x + sx, y + sy, sw, sh))

            return smiles_in_frame

        except Exception as e:
            logger.warning(f"Smile detection failed: {e}")
            return []

    def analyze_light_level(self, frame: np.ndarray) -> dict[str, Any]:
        """
        Analyze light level in a frame using HSV color space.

        Args:
            frame: Input frame (BGR format)

        Returns:
            Dictionary with light analysis results
        """
        try:
            # Convert to HSV color space
            hsv_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

            # Extract Value channel (brightness)
            _, _, v_channel = cv2.split(hsv_frame)

            # Calculate mean brightness
            mean_brightness = cv2.mean(v_channel)[0]

            # Categorize light level (shared thresholds)
            light_category = categorize_light(mean_brightness)

            return {
                "light_value": round(mean_brightness, 2),
                "light_category": light_category,
                "mean_brightness": mean_brightness,
            }

        except Exception as e:
            logger.warning(f"Light analysis failed: {e}")
            return {
                "light_value": 0.0,
                "light_category": "Unknown",
                "mean_brightness": 0.0,
            }

    def analyze_frame(self, frame: np.ndarray) -> dict[str, Any]:
        """
        Analyze a single frame for faces, smiles, and light level.

        Args:
            frame: Input frame (BGR format)

        Returns:
            Dictionary with analysis results
        """
        try:
            # Detect faces
            faces = self.detect_faces(frame)

            # Initialize results
            analysis_result = {
                "faces_detected": len(faces),
                "smiles_detected": 0,
                "light_analysis": self.analyze_light_level(frame),
            }

            # Process each detected face
            for face_rect in faces:
                # Detect smiles within the face
                smiles = self.detect_smiles(frame, face_rect)
                analysis_result["smiles_detected"] += len(smiles)

                # Draw rectangles on frame for visualization
                x, y, w, h = face_rect
                cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)

                # Draw smile rectangles
                for smile_rect in smiles:
                    sx, sy, sw, sh = smile_rect
                    cv2.rectangle(frame, (sx, sy), (sx + sw, sy + sh), (0, 0, 255), 2)

            return analysis_result

        except Exception as e:
            logger.error(f"Frame analysis failed: {e}")
            return {
                "faces_detected": 0,
                "smiles_detected": 0,
                "light_analysis": {
                    "light_value": 0.0,
                    "light_category": "Error",
                    "mean_brightness": 0.0,
                },
            }

    def analyze_video_frames(
        self, video_path: str, sample_rate: int = 1
    ) -> dict[str, Any]:
        """
        Analyze video frames for face and smile detection.

        Args:
            video_path: Path to video file
            sample_rate: Process every Nth frame (1 = every frame)

        Returns:
            Dictionary with video analysis results
        """
        try:
            total_faces = 0
            total_smiles = 0
            light_values: list[float] = []

            def on_frame(_idx: int, frame):
                nonlocal total_faces, total_smiles, light_values
                frame_result = self.analyze_frame(frame)
                total_faces += frame_result["faces_detected"]
                total_smiles += frame_result["smiles_detected"]
                light_values.append(frame_result["light_analysis"]["light_value"])

            stats = foreach_frame(video_path, on_frame, sample_rate=sample_rate)

            # Calculate averages and percentages
            avg_light_value = np.mean(light_values) if light_values else 0.0
            processed_frames = stats["processed_frames"] or 0
            face_detection_rate = (
                (total_faces / processed_frames) if processed_frames > 0 else 0.0
            )
            smile_detection_rate = (
                (total_smiles / processed_frames) if processed_frames > 0 else 0.0
            )

            # Categorize overall light level
            overall_light_category = categorize_light(avg_light_value)

            return {
                "total_frames": stats["total_frames"],
                "processed_frames": processed_frames,
                "fps": stats["fps"],
                "face_detection": {
                    "total_faces_detected": total_faces,
                    "faces_per_frame": face_detection_rate,
                    "face_detection_percentage": round(face_detection_rate * 100, 2),
                },
                "smile_detection": {
                    "total_smiles_detected": total_smiles,
                    "smiles_per_frame": smile_detection_rate,
                    "smile_detection_percentage": round(smile_detection_rate * 100, 2),
                },
                "light_analysis": {
                    "average_light_value": round(avg_light_value, 2),
                    "light_category": overall_light_category,
                    "light_values_range": {
                        "min": round(min(light_values), 2) if light_values else 0.0,
                        "max": round(max(light_values), 2) if light_values else 0.0,
                    },
                },
                "processing_info": {
                    "sample_rate": sample_rate,
                    "processing_efficiency": round(
                        (processed_frames / (stats["total_frames"] or 1)) * 100, 2
                    ),
                },
            }

        except Exception as e:
            logger.error(f"Video analysis failed: {e}")
            return {"error": str(e), "total_frames": 0, "processed_frames": 0}

    def get_analysis_summary(self) -> dict[str, Any]:
        """Get a summary of the face analysis."""
        return {
            "faces_detected": self.faces_detected,
            "smiles_detected": self.smiles_detected,
            "light_level": self.light_level,
            "light_category": self.light_category,
        }

    async def analyze_video_face(self, video_path: str) -> dict[str, Any]:
        """
        Analyze face detection and smile detection for an entire video file.

        Args:
            video_path: Path to the video file

        Returns:
            Dict containing face analysis results
        """
        try:
            # Use shared frame iterator for consistency
            from app.utils.video_utils import foreach_frame

            smile_count = 0
            face_count = 0
            error_frames = 0

            def on_frame(_idx: int, frame):
                nonlocal smile_count, face_count, error_frames
                try:
                    frame_result = self.analyze_frame(frame)
                    if frame_result.get("faces_detected", 0) > 0:
                        face_count += 1
                        if frame_result.get("smiles_detected", 0) > 0:
                            smile_count += 1
                except Exception:
                    error_frames += 1

            stats = await asyncio.to_thread(foreach_frame, video_path, on_frame)
            total_frames = stats["total_frames"]
            processed_frames = stats["processed_frames"]

            smile_percentage = (smile_count / total_frames) * 100 if total_frames else 0

            logger.info(
                f"Face analysis completed: {processed_frames} processed frames, {error_frames} errors"
            )

            return {
                "smile_count": smile_count,
                "smile_percentage": round(smile_percentage, 2),
                "face_count": face_count,
                "total_frames": total_frames,
                "processed_frames": processed_frames,
                "error_frames": error_frames,
            }

        except Exception as e:
            logger.error(f"Video face analysis failed: {e}")
            return {
                "smile_count": 0,
                "smile_percentage": 0,
                "face_count": 0,
                "total_frames": 0,
                "processed_frames": 0,
                "error_frames": 0,
            }

    async def analyze_video_lighting(self, video_path: str) -> dict[str, Any]:
        """
        Analyze lighting conditions for an entire video file.

        Args:
            video_path: Path to the video file

        Returns:
            Dict containing lighting analysis results
        """
        try:
            # Use shared frame iterator for consistency
            from app.utils.video_utils import foreach_frame

            light_values = []
            error_frames = 0

            def on_frame(_idx: int, frame):
                nonlocal light_values, error_frames
                try:
                    light_result = self.analyze_light_level(frame)
                    light_values.append(light_result.get("light_value", 0))
                except Exception:
                    error_frames += 1

            stats = await asyncio.to_thread(foreach_frame, video_path, on_frame)
            total_frames = stats["total_frames"]
            total_processed = stats["processed_frames"]

            # Calculate average light value and reuse shared categorization
            if light_values:
                avg_light_value = sum(light_values) / len(light_values)
                light_category = categorize_light(avg_light_value)
            else:
                avg_light_value = 0
                light_category = "Unknown"

            logger.info(
                f"Lighting analysis completed: {total_processed} processed frames, {error_frames} errors"
            )

            return {
                "light_value": round(avg_light_value, 2),
                "light_category": light_category,
                "total_frames": total_frames,
                "processed_frames": total_processed,
                "error_frames": error_frames,
            }

        except Exception as e:
            logger.error(f"Video lighting analysis failed: {e}")
            return {
                "light_value": 0,
                "light_category": "Error",
                "total_frames": 0,
                "processed_frames": 0,
                "error_frames": 0,
            }
