"""
Overlay engine for rendering dynamic overlays on video content
NOTE: This is legacy code - the new PyQt6 implementation uses QWebEngineView for overlays
"""

import time
import logging
from typing import Dict, Any, Optional, List, Tuple
from pathlib import Path
from PyQt6.QtCore import Qt, QRect, QPoint, QTimer, QPropertyAnimation, QEasingCurve
from PyQt6.QtGui import (
    QPainter, QPen, QBrush, QColor, QFont, QPixmap, QFontMetrics,
    QLinearGradient, QRadialGradient, QPolygon
)
from PyQt6.QtWidgets import QGraphicsEffect

logger = logging.getLogger(__name__)


class OverlayElement:
    """Base class for overlay elements"""
    
    def __init__(self, element_id: str, element_type: str, config: Dict[str, Any]):
        self.id = element_id
        self.type = element_type
        self.config = config
        self.visible = config.get('visible', True)
        self.opacity = config.get('opacity', 1.0)
        self.x = config.get('x', 0)
        self.y = config.get('y', 0)
        self.width = config.get('width', 100)
        self.height = config.get('height', 50)
        self.z_index = config.get('z_index', 0)
        
        # Animation properties
        self.animation_config = config.get('animation', {})
        self.animation_start_time = None
        
    def get_rect(self) -> QRect:
        """Get element rectangle"""
        return QRect(int(self.x), int(self.y), int(self.width), int(self.height))
    
    def update_animation(self, elapsed_time: float):
        """Update animation state"""
        if not self.animation_config:
            return
        
        animation_type = self.animation_config.get('type')
        if animation_type == 'scroll':
            self._update_scroll_animation(elapsed_time)
        elif animation_type == 'fade':
            self._update_fade_animation(elapsed_time)
        elif animation_type == 'bounce':
            self._update_bounce_animation(elapsed_time)
    
    def _update_scroll_animation(self, elapsed_time: float):
        """Update scrolling text animation"""
        speed = self.animation_config.get('speed', 100)  # pixels per second
        direction = self.animation_config.get('direction', 'left')
        
        if direction == 'left':
            self.x -= speed * elapsed_time / 1000.0
        elif direction == 'right':
            self.x += speed * elapsed_time / 1000.0
        elif direction == 'up':
            self.y -= speed * elapsed_time / 1000.0
        elif direction == 'down':
            self.y += speed * elapsed_time / 1000.0
    
    def _update_fade_animation(self, elapsed_time: float):
        """Update fade animation"""
        duration = self.animation_config.get('duration', 2000)  # ms
        fade_type = self.animation_config.get('fade_type', 'in')
        
        if self.animation_start_time is None:
            self.animation_start_time = elapsed_time
        
        progress = (elapsed_time - self.animation_start_time) / duration
        progress = max(0.0, min(1.0, progress))
        
        if fade_type == 'in':
            self.opacity = progress
        elif fade_type == 'out':
            self.opacity = 1.0 - progress
        elif fade_type == 'in_out':
            if progress <= 0.5:
                self.opacity = progress * 2
            else:
                self.opacity = (1.0 - progress) * 2
    
    def _update_bounce_animation(self, elapsed_time: float):
        """Update bounce animation"""
        import math
        
        frequency = self.animation_config.get('frequency', 1.0)  # Hz
        amplitude = self.animation_config.get('amplitude', 10)  # pixels
        
        offset = amplitude * math.sin(2 * math.pi * frequency * elapsed_time / 1000.0)
        self.y = self.config.get('y', 0) + offset
    
    def render(self, painter: QPainter, canvas_rect: QRect):
        """Render element (to be implemented by subclasses)"""
        pass


class RectangleElement(OverlayElement):
    """Rectangle overlay element"""
    
    def __init__(self, element_id: str, config: Dict[str, Any]):
        super().__init__(element_id, 'rectangle', config)
        self.color = QColor(config.get('color', '#000000'))
        self.border_color = QColor(config.get('border_color', '#ffffff'))
        self.border_width = config.get('border_width', 0)
        self.corner_radius = config.get('corner_radius', 0)
    
    def render(self, painter: QPainter, canvas_rect: QRect):
        """Render rectangle"""
        if not self.visible or self.opacity <= 0:
            return
        
        # Set opacity
        painter.setOpacity(self.opacity)
        
        rect = self.get_rect()
        
        # Fill
        if self.color.alpha() > 0:
            brush = QBrush(self.color)
            painter.setBrush(brush)
        else:
            painter.setBrush(Qt.NoBrush)
        
        # Border
        if self.border_width > 0:
            pen = QPen(self.border_color, self.border_width)
            painter.setPen(pen)
        else:
            painter.setPen(Qt.NoPen)
        
        # Draw
        if self.corner_radius > 0:
            painter.drawRoundedRect(rect, self.corner_radius, self.corner_radius)
        else:
            painter.drawRect(rect)


class TextElement(OverlayElement):
    """Text overlay element"""
    
    def __init__(self, element_id: str, config: Dict[str, Any]):
        super().__init__(element_id, 'text', config)
        self.text = config.get('text', '')
        self.font_family = config.get('font_family', 'Arial')
        self.font_size = config.get('font_size', 16)
        self.font_weight = config.get('font_weight', 'normal')
        self.color = QColor(config.get('color', '#ffffff'))
        self.background_color = QColor(config.get('background_color', 'transparent'))
        self.alignment = config.get('alignment', 'left')
        self.word_wrap = config.get('word_wrap', False)
        
        # Scrolling text properties
        self.scroll_position = 0
        self.text_width = 0
    
    def set_text(self, text: str):
        """Update text content"""
        self.text = text
        self.scroll_position = 0  # Reset scroll position
    
    def render(self, painter: QPainter, canvas_rect: QRect):
        """Render text"""
        if not self.visible or self.opacity <= 0 or not self.text:
            return
        
        # Set opacity
        painter.setOpacity(self.opacity)
        
        # Setup font
        font = QFont(self.font_family, self.font_size)
        if self.font_weight == 'bold':
            font.setBold(True)
        painter.setFont(font)
        
        # Get text metrics
        metrics = QFontMetrics(font)
        self.text_width = metrics.boundingRect(self.text).width()
        
        rect = self.get_rect()
        
        # Background
        if self.background_color.alpha() > 0:
            painter.fillRect(rect, self.background_color)
        
        # Text color
        painter.setPen(self.color)
        
        # Handle scrolling animation
        if self.animation_config.get('type') == 'scroll':
            self._render_scrolling_text(painter, rect, metrics)
        else:
            self._render_static_text(painter, rect)
    
    def _render_scrolling_text(self, painter: QPainter, rect: QRect, metrics: QFontMetrics):
        """Render scrolling text"""
        if self.text_width <= rect.width():
            # Text fits, no scrolling needed
            self._render_static_text(painter, rect)
            return
        
        # Calculate scroll position
        direction = self.animation_config.get('direction', 'left')
        
        if direction == 'left':
            # Reset position when text fully scrolled out
            if self.x < -self.text_width:
                self.x = rect.right()
            
            # Draw text at current position
            text_rect = QRect(int(self.x), rect.y(), self.text_width, rect.height())
        else:
            text_rect = rect
        
        # Draw text
        alignment = Qt.AlignVCenter
        if self.alignment == 'center':
            alignment |= Qt.AlignHCenter
        elif self.alignment == 'right':
            alignment |= Qt.AlignRight
        else:
            alignment |= Qt.AlignLeft
        
        painter.drawText(text_rect, alignment, self.text)
    
    def _render_static_text(self, painter: QPainter, rect: QRect):
        """Render static text"""
        # Text alignment
        alignment = Qt.AlignVCenter
        if self.alignment == 'center':
            alignment |= Qt.AlignHCenter
        elif self.alignment == 'right':
            alignment |= Qt.AlignRight
        else:
            alignment |= Qt.AlignLeft
        
        if self.word_wrap:
            alignment |= Qt.TextWordWrap
        
        painter.drawText(rect, alignment, self.text)


class ImageElement(OverlayElement):
    """Image overlay element"""
    
    def __init__(self, element_id: str, config: Dict[str, Any]):
        super().__init__(element_id, 'image', config)
        self.source = config.get('source', '')
        self.fit = config.get('fit', 'contain')  # contain, cover, fill, scale-down
        self.pixmap: Optional[QPixmap] = None
        self._load_image()
    
    def _load_image(self):
        """Load image from source"""
        try:
            if self.source:
                # Handle both absolute and relative paths
                if Path(self.source).is_absolute():
                    image_path = Path(self.source)
                else:
                    # Relative to project root
                    project_root = Path(__file__).parent.parent.parent
                    image_path = project_root / self.source
                
                if image_path.exists():
                    self.pixmap = QPixmap(str(image_path))
                    logger.debug(f"Loaded image: {image_path}")
                else:
                    logger.warning(f"Image not found: {image_path}")
        except Exception as e:
            logger.error(f"Failed to load image {self.source}: {e}")
    
    def set_source(self, source: str):
        """Update image source"""
        self.source = source
        self._load_image()
    
    def render(self, painter: QPainter, canvas_rect: QRect):
        """Render image"""
        if not self.visible or self.opacity <= 0 or not self.pixmap:
            return
        
        # Set opacity
        painter.setOpacity(self.opacity)
        
        rect = self.get_rect()
        
        # Scale pixmap based on fit mode
        scaled_pixmap = self._scale_pixmap(self.pixmap, rect)
        
        # Calculate position for centering if needed
        if self.fit in ['contain', 'scale-down']:
            x_offset = (rect.width() - scaled_pixmap.width()) // 2
            y_offset = (rect.height() - scaled_pixmap.height()) // 2
            draw_pos = QPoint(rect.x() + x_offset, rect.y() + y_offset)
        else:
            draw_pos = rect.topLeft()
        
        painter.drawPixmap(draw_pos, scaled_pixmap)
    
    def _scale_pixmap(self, pixmap: QPixmap, target_rect: QRect) -> QPixmap:
        """Scale pixmap according to fit mode"""
        if self.fit == 'fill':
            return pixmap.scaled(target_rect.size(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
        elif self.fit == 'cover':
            return pixmap.scaled(target_rect.size(), Qt.KeepAspectRatioByExpanding, Qt.SmoothTransformation)
        elif self.fit == 'contain':
            return pixmap.scaled(target_rect.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
        elif self.fit == 'scale-down':
            if pixmap.width() <= target_rect.width() and pixmap.height() <= target_rect.height():
                return pixmap  # No scaling needed
            else:
                return pixmap.scaled(target_rect.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
        else:
            return pixmap


class OverlayRenderer:
    """Manages rendering of overlay elements"""
    
    def __init__(self):
        self.elements: Dict[str, OverlayElement] = {}
        self.start_time = time.time() * 1000  # milliseconds
        self.last_update_time = self.start_time
    
    def add_element(self, element: OverlayElement):
        """Add overlay element"""
        self.elements[element.id] = element
    
    def remove_element(self, element_id: str):
        """Remove overlay element"""
        if element_id in self.elements:
            del self.elements[element_id]
    
    def get_element(self, element_id: str) -> Optional[OverlayElement]:
        """Get overlay element by ID"""
        return self.elements.get(element_id)
    
    def clear_elements(self):
        """Clear all elements"""
        self.elements.clear()
    
    def update_element_data(self, element_id: str, data: Dict[str, Any]):
        """Update element data"""
        element = self.get_element(element_id)
        if element:
            if element.type == 'text' and 'text' in data:
                element.set_text(data['text'])
            elif element.type == 'image' and 'source' in data:
                element.set_source(data['source'])
            
            # Update other properties
            for key, value in data.items():
                if hasattr(element, key):
                    setattr(element, key, value)
    
    def render(self, painter: QPainter, canvas_rect: QRect):
        """Render all overlay elements"""
        current_time = time.time() * 1000
        elapsed_time = current_time - self.last_update_time
        self.last_update_time = current_time
        
        # Sort elements by z-index
        sorted_elements = sorted(self.elements.values(), key=lambda e: e.z_index)
        
        # Render elements
        for element in sorted_elements:
            try:
                # Update animations
                element.update_animation(current_time - self.start_time)
                
                # Render element
                element.render(painter, canvas_rect)
                
            except Exception as e:
                logger.error(f"Failed to render element {element.id}: {e}")


class OverlayEngine:
    """Main overlay engine"""
    
    def __init__(self):
        self.renderer = OverlayRenderer()
        self.template_config: Optional[Dict[str, Any]] = None
        self.overlay_data: Dict[str, Any] = {}
        self.playback_position = 0
        self.playback_duration = 0
    
    def load_template(self, template_config: Dict[str, Any], overlay_data: Dict[str, Any] = None):
        """Load overlay template"""
        try:
            self.template_config = template_config
            self.overlay_data = overlay_data or {}
            
            # Clear existing elements
            self.renderer.clear_elements()
            
            # Create elements from template
            elements_config = template_config.get('elements', [])
            
            for element_config in elements_config:
                element = self._create_element(element_config)
                if element:
                    self.renderer.add_element(element)
            
            # Apply overlay data
            self._apply_overlay_data()
            
            logger.info(f"Loaded template with {len(elements_config)} elements")
            
        except Exception as e:
            logger.error(f"Failed to load template: {e}")
    
    def _create_element(self, config: Dict[str, Any]) -> Optional[OverlayElement]:
        """Create overlay element from configuration"""
        element_type = config.get('type')
        element_id = config.get('id')
        
        if not element_type or not element_id:
            logger.warning("Element missing type or id")
            return None
        
        try:
            if element_type == 'rectangle':
                return RectangleElement(element_id, config)
            elif element_type == 'text':
                return TextElement(element_id, config)
            elif element_type == 'image':
                return ImageElement(element_id, config)
            else:
                logger.warning(f"Unknown element type: {element_type}")
                return None
                
        except Exception as e:
            logger.error(f"Failed to create element {element_id}: {e}")
            return None
    
    def update_overlay_data(self, overlay_data: Dict[str, Any]):
        """Update overlay data"""
        self.overlay_data.update(overlay_data)
        self._apply_overlay_data()
    
    def _apply_overlay_data(self):
        """Apply overlay data to elements"""
        try:
            for element_id, data in self.overlay_data.items():
                self.renderer.update_element_data(element_id, data)
                
        except Exception as e:
            logger.error(f"Failed to apply overlay data: {e}")
    
    def update_playback_position(self, position: int, duration: int):
        """Update video playback position"""
        self.playback_position = position
        self.playback_duration = duration
    
    def render(self, painter: QPainter, canvas_rect: QRect):
        """Render overlay"""
        try:
            self.renderer.render(painter, canvas_rect)
        except Exception as e:
            logger.error(f"Overlay render failed: {e}")
    
    def get_element_by_id(self, element_id: str) -> Optional[OverlayElement]:
        """Get element by ID"""
        return self.renderer.get_element(element_id)
    
    def set_element_visibility(self, element_id: str, visible: bool):
        """Set element visibility"""
        element = self.get_element_by_id(element_id)
        if element:
            element.visible = visible
    
    def set_element_text(self, element_id: str, text: str):
        """Set text content for text element"""
        element = self.get_element_by_id(element_id)
        if element and element.type == 'text':
            element.set_text(text)
    
    def set_element_image(self, element_id: str, image_source: str):
        """Set image source for image element"""
        element = self.get_element_by_id(element_id)
        if element and element.type == 'image':
            element.set_source(image_source)