"""
Exercise form analysis using pose landmarks
"""

from typing import Optional, Dict, List, Tuple
from pose_detector import PoseDetector
import numpy as np


class FormAnalyzer:
    """
    Analyzes exercise form and counts reps
    """
    
    def __init__(self, exercise_config: dict):
        """
        Args:
            exercise_config: Configuration dict for the exercise
        """
        self.config = exercise_config
        self.rep_count = 0
        self.in_rep = False
        self.cooldown_counter = 0
        self.form_issues = []
        self.form_score = 1.0
        
    def reset(self):
        """Reset counter and state"""
        self.rep_count = 0
        self.in_rep = False
        self.cooldown_counter = 0
        self.form_issues = []
        self.form_score = 1.0


class SquatAnalyzer(FormAnalyzer):
    """
    Squat form analysis
    
    Checks:
    - Hip angle at bottom
    - Knee angle at bottom
    - Back angle from vertical
    - Depth (hip below knee)
    """
    
    def analyze(self, detector: PoseDetector) -> Dict:
        """
        Analyze squat form
        
        Returns:
            dict with keys: rep_count, form_score, form_issues, angles
        """
        self.form_issues = []
        angles = {}
        
        # Calculate angles
        left_hip_angle = detector.calculate_angle('LEFT_SHOULDER', 'LEFT_HIP', 'LEFT_KNEE')
        right_hip_angle = detector.calculate_angle('RIGHT_SHOULDER', 'RIGHT_HIP', 'RIGHT_KNEE')
        
        left_knee_angle = detector.calculate_angle('LEFT_HIP', 'LEFT_KNEE', 'LEFT_ANKLE')
        right_knee_angle = detector.calculate_angle('RIGHT_HIP', 'RIGHT_KNEE', 'RIGHT_ANKLE')
        
        # Use average of both sides
        hip_angle = None
        knee_angle = None
        
        if left_hip_angle and right_hip_angle:
            hip_angle = (left_hip_angle + right_hip_angle) / 2
            angles['hip'] = hip_angle
            
        if left_knee_angle and right_knee_angle:
            knee_angle = (left_knee_angle + right_knee_angle) / 2
            angles['knee'] = knee_angle
        
        # Check depth (hip below knee)
        left_hip = detector.get_landmark('LEFT_HIP')
        left_knee = detector.get_landmark('LEFT_KNEE')
        
        at_bottom = False
        if hip_angle and knee_angle:
            # Consider "at bottom" if hip angle is small (squat position)
            if hip_angle < 100:  # Arbitrary threshold for "squatting"
                at_bottom = True
        
        # Rep counting logic
        if self.cooldown_counter > 0:
            self.cooldown_counter -= 1
        
        if at_bottom and not self.in_rep and self.cooldown_counter == 0:
            # Entering squat position
            self.in_rep = True
            
            # Check form at bottom
            config = self.config
            
            if hip_angle:
                hip_range = config['bottom_hip_angle_range']
                if not (hip_range[0] <= hip_angle <= hip_range[1]):
                    self.form_issues.append(f"Hip angle {hip_angle:.0f}° (target {hip_range[0]}-{hip_range[1]}°)")
            
            if knee_angle:
                knee_range = config['knee_angle_at_bottom_range']
                if not (knee_range[0] <= knee_angle <= knee_range[1]):
                    self.form_issues.append(f"Knee angle {knee_angle:.0f}° (target {knee_range[0]}-{knee_range[1]}°)")
            
            # Check if actually going deep enough
            if left_hip and left_knee:
                if left_hip[1] > left_knee[1] * 0.95:  # Hip should be below knee
                    self.form_issues.append("Not deep enough - hips above knees")
        
        elif not at_bottom and self.in_rep:
            # Exiting squat position (standing up) - count rep
            self.in_rep = False
            self.rep_count += 1
            self.cooldown_counter = self.config['rep_cooldown_frames']
        
        # Calculate form score
        self.form_score = 1.0 - (len(self.form_issues) * 0.2)
        self.form_score = max(0.0, min(1.0, self.form_score))
        
        return {
            'exercise': 'squat',
            'rep_count': self.rep_count,
            'form_score': self.form_score,
            'form_issues': self.form_issues.copy(),
            'angles': angles,
            'in_rep': self.in_rep
        }


class PushupAnalyzer(FormAnalyzer):
    """
    Push-up form analysis
    
    Checks:
    - Elbow angle at bottom
    - Body alignment (straight line from shoulders to ankles)
    - Depth
    """
    
    def analyze(self, detector: PoseDetector) -> Dict:
        """
        Analyze push-up form
        """
        self.form_issues = []
        angles = {}
        
        # Calculate elbow angle
        left_elbow = detector.calculate_angle('LEFT_SHOULDER', 'LEFT_ELBOW', 'LEFT_WRIST')
        right_elbow = detector.calculate_angle('RIGHT_SHOULDER', 'RIGHT_ELBOW', 'RIGHT_WRIST')
        
        elbow_angle = None
        if left_elbow and right_elbow:
            elbow_angle = (left_elbow + right_elbow) / 2
            angles['elbow'] = elbow_angle
        
        # Check body alignment (shoulder-hip-ankle should be straight)
        left_shoulder = detector.get_landmark('LEFT_SHOULDER')
        left_hip = detector.get_landmark('LEFT_HIP')
        left_ankle = detector.get_landmark('LEFT_ANKLE')
        
        body_straight = False
        if all([left_shoulder, left_hip, left_ankle]):
            # Calculate deviation from straight line
            # (Simplified - could use more sophisticated geometry)
            mid_y = (left_shoulder[1] + left_ankle[1]) / 2
            deviation = abs(left_hip[1] - mid_y)
            
            max_deviation = self.config['body_alignment_max_deviation']
            if deviation > max_deviation:
                self.form_issues.append(f"Body not aligned - sag/pike detected")
            else:
                body_straight = True
        
        # Rep counting
        at_bottom = False
        if elbow_angle:
            # At bottom when elbows are bent
            if elbow_angle < 110:
                at_bottom = True
        
        if self.cooldown_counter > 0:
            self.cooldown_counter -= 1
        
        if at_bottom and not self.in_rep and self.cooldown_counter == 0:
            self.in_rep = True
            
            # Check form at bottom
            config = self.config
            if elbow_angle:
                elbow_range = config['elbow_angle_at_bottom_range']
                if not (elbow_range[0] <= elbow_angle <= elbow_range[1]):
                    self.form_issues.append(f"Elbow angle {elbow_angle:.0f}° (target {elbow_range[0]}-{elbow_range[1]}°)")
        
        elif not at_bottom and self.in_rep:
            self.in_rep = False
            self.rep_count += 1
            self.cooldown_counter = self.config['rep_cooldown_frames']
        
        self.form_score = 1.0 - (len(self.form_issues) * 0.25)
        self.form_score = max(0.0, min(1.0, self.form_score))
        
        return {
            'exercise': 'pushup',
            'rep_count': self.rep_count,
            'form_score': self.form_score,
            'form_issues': self.form_issues.copy(),
            'angles': angles,
            'in_rep': self.in_rep
        }


class DeadliftAnalyzer(FormAnalyzer):
    """
    Deadlift form analysis
    
    Checks:
    - Hip hinge angle
    - Back straightness
    - Lockout position
    """
    
    def analyze(self, detector: PoseDetector) -> Dict:
        """
        Analyze deadlift form
        """
        self.form_issues = []
        angles = {}
        
        # Hip hinge angle
        left_hip_angle = detector.calculate_angle('LEFT_SHOULDER', 'LEFT_HIP', 'LEFT_KNEE')
        right_hip_angle = detector.calculate_angle('RIGHT_SHOULDER', 'RIGHT_HIP', 'RIGHT_KNEE')
        
        hip_angle = None
        if left_hip_angle and right_hip_angle:
            hip_angle = (left_hip_angle + right_hip_angle) / 2
            angles['hip'] = hip_angle
        
        # Back angle from vertical
        back_angle = detector.calculate_angle_from_vertical('LEFT_HIP', 'LEFT_SHOULDER')
        if back_angle:
            angles['back'] = back_angle
        
        # Rep counting
        at_bottom = False
        at_top = False
        
        if hip_angle:
            if hip_angle < 100:  # Hinged position
                at_bottom = True
            elif hip_angle > 160:  # Locked out
                at_top = True
        
        if self.cooldown_counter > 0:
            self.cooldown_counter -= 1
        
        if at_bottom and not self.in_rep and self.cooldown_counter == 0:
            self.in_rep = True
            
            # Check form at bottom
            if back_angle:
                # Back should be relatively straight (not too horizontal)
                if back_angle > 60:  # Too horizontal
                    self.form_issues.append("Back angle too horizontal - risk of rounding")
        
        elif at_top and self.in_rep:
            self.in_rep = False
            self.rep_count += 1
            self.cooldown_counter = self.config['rep_cooldown_frames']
            
            # Check lockout
            if hip_angle:
                lockout_range = self.config['lockout_hip_angle_range']
                if not (lockout_range[0] <= hip_angle <= lockout_range[1]):
                    self.form_issues.append("Incomplete lockout")
        
        self.form_score = 1.0 - (len(self.form_issues) * 0.25)
        self.form_score = max(0.0, min(1.0, self.form_score))
        
        return {
            'exercise': 'deadlift',
            'rep_count': self.rep_count,
            'form_score': self.form_score,
            'form_issues': self.form_issues.copy(),
            'angles': angles,
            'in_rep': self.in_rep
        }


def create_analyzer(exercise_name: str, exercise_config: dict) -> FormAnalyzer:
    """
    Factory function to create appropriate analyzer
    
    Args:
        exercise_name: 'squat', 'pushup', 'deadlift', etc.
        exercise_config: Configuration dict for the exercise
        
    Returns:
        FormAnalyzer instance
    """
    analyzers = {
        'squat': SquatAnalyzer,
        'pushup': PushupAnalyzer,
        'deadlift': DeadliftAnalyzer
    }
    
    analyzer_class = analyzers.get(exercise_name.lower())
    if not analyzer_class:
        raise ValueError(f"Unknown exercise: {exercise_name}")
    
    return analyzer_class(exercise_config)
