import os
import logging
import subprocess
import tempfile
import shutil
from typing import Optional
import hashlib

from dbmodel import Instance, Series, Study
from utils import getImage, toImage
from PIL import Image
from config import config

# Configure logger
LOGGER = logging.getLogger('sampacs')

async def generate_series_video(series_uid: str, study_uid: str, request) -> Optional[str]:
    """
    Generate video preview for a DICOM series on-demand.

    Args:
        series_uid: Series Instance UID
        study_uid: Study Instance UID

    Returns:
        Path to generated video file or None if failed
    """
    try:
        LOGGER.info(f"Starting on-demand video generation for series {series_uid}")

        # Get study information
        study = Study.get_or_none(Study.uid == study_uid)
        if not study:
            LOGGER.error(f"Study {study_uid} not found")
            return None

        study_date = study.date.strftime("%Y%m")

        # Get series information
        series = Series.get_or_none(Series.uid == series_uid)
        if not series:
            LOGGER.error(f"Series {series_uid} not found")
            return None

        # Check if series belongs to study
        if series.study_id != study_uid:
            LOGGER.error(f"Series {series_uid} does not belong to study {study_uid}")
            return None

        # Get all instances in the series
        instances = (Instance
                    .select()
                    .where(Instance.series == series_uid)
                    .order_by(Instance.number))

        if not instances:
            LOGGER.warning(f"No instances found for series {series_uid}")
            return None

        if len(instances) < 2:
            LOGGER.warning(f"Not enough instances ({len(instances)}) for video generation")
            return None

        # Create temporary directory for JPEG frames
        temp_dir = tempfile.mkdtemp(prefix=f"video_{series_uid}_")

        try:
            # Generate JPEG frames for each instance
            jpeg_files = []
            frames_generated = 0

            for i, instance in enumerate(instances):
                try:
                    # Check if client is still connected before processing next instance
                    if hasattr(request, 'is_disconnected') and await request.is_disconnected():
                        LOGGER.info(f"Client disconnected, cancelling video generation for series {series_uid}")
                        return None

                    # Get DICOM dataset
                    ds = getImage(instance.uid, study_date, study.nodeCode)
                    if ds and hasattr(ds, 'PixelData') and ds.PixelData:
                        # Convert to image
                        image = toImage(ds, 0)
                        if image:
                            # Resize image to reduce video size (max dimension 176px)
                            width, height = image.size
                            if width > height:
                                new_width = 176
                                new_height = int(height * 176 / width)
                            else:
                                new_height = 176
                                new_width = int(width * 176 / height)

                            resized_image = image.resize((new_width, new_height), Image.Resampling.LANCZOS)

                            # Save as JPEG frame
                            jpeg_path = os.path.join(temp_dir, f"frame_{i:06d}.jpg")
                            resized_image.save(jpeg_path, format='JPEG', quality=80)
                            jpeg_files.append(jpeg_path)
                            frames_generated += 1
                except Exception as e:
                    LOGGER.warning(f"Error processing instance {instance.uid}: {e}")
                    continue

            if frames_generated < 2:
                LOGGER.warning(f"Not enough frames ({frames_generated}) for video generation")
                return None

            # Check if client is still connected before starting FFmpeg
            if hasattr(request, 'is_disconnected') and await request.is_disconnected():
                LOGGER.info(f"Client disconnected before FFmpeg start, cancelling video generation for series {series_uid}")
                return None

            # Create video cache directory
            cache_dir = os.path.join(config.storageDir, study_date, 'video_cache')
            os.makedirs(cache_dir, exist_ok=True)

            # Generate unique filename based on series UID and instance count
            video_hash = hashlib.md5(f"{series_uid}_{frames_generated}".encode()).hexdigest()[:8]
            output_path = os.path.join(cache_dir, f"{series_uid}_{video_hash}.webm")

            # Check if video already exists in cache
            if os.path.exists(output_path):
                LOGGER.info(f"Using cached video: {output_path}")
                return output_path

            # Calculate framerate for 3-second video duration
            target_duration = 3  # seconds
            framerate = max(1, frames_generated / target_duration)  # At least 1 fps

            # Generate video using ffmpeg
            ffmpeg_cmd = [
                'ffmpeg', '-y',  # Overwrite output file
                '-framerate', str(framerate),  # Dynamic framerate for 3-second video
                '-i', os.path.join(temp_dir, 'frame_%06d.jpg'),
                '-c:v', 'libvpx-vp9',  # VP9 codec for WebM
                '-pix_fmt', 'yuv420p',
                '-crf', '40',  # Quality setting
                '-b:v', '0',  # Variable bitrate
                '-an',  # No audio
                '-vf', 'scale=176:-1',
                output_path
            ]

            LOGGER.info(f"Running ffmpeg command for {frames_generated} frames")

            result = subprocess.run(
                ffmpeg_cmd,
                capture_output=True,
                text=True,
                timeout=300  # 5 minute timeout
            )

            if result.returncode == 0:
                if os.path.exists(output_path):
                    file_size = os.path.getsize(output_path)
                    LOGGER.info(f"Video generated successfully: {output_path} ({file_size} bytes)")
                    return output_path
                else:
                    LOGGER.error("FFmpeg completed but output file not found")
            else:
                LOGGER.error(f"FFmpeg failed with return code {result.returncode}")
                if result.stderr:
                    LOGGER.error(f"FFmpeg stderr: {result.stderr[:500]}...")

        finally:
            # Clean up temporary directory
            try:
                shutil.rmtree(temp_dir)
            except Exception as e:
                LOGGER.warning(f"Error cleaning up temp directory {temp_dir}: {e}")

    except subprocess.TimeoutExpired:
        LOGGER.error(f"Video generation timeout for series {series_uid}")
    except Exception as e:
        LOGGER.error(f"Unexpected error generating video for series {series_uid}: {e}")

    return None

def cleanup_old_videos(max_age_hours: int = 24):
    """
    Clean up old video cache files.

    Args:
        max_age_hours: Maximum age of cache files in hours
    """
    try:
        import time
        current_time = time.time()
        max_age_seconds = max_age_hours * 3600

        # Find all video cache directories
        for root, dirs, files in os.walk(config.storageDir):
            if 'video_cache' in root:
                for file in files:
                    if file.endswith('.webm'):
                        file_path = os.path.join(root, file)
                        file_age = current_time - os.path.getctime(file_path)

                        if file_age > max_age_seconds:
                            try:
                                os.remove(file_path)
                                LOGGER.info(f"Cleaned up old video cache: {file_path}")
                            except Exception as e:
                                LOGGER.warning(f"Failed to clean up {file_path}: {e}")

    except Exception as e:
        LOGGER.error(f"Error cleaning up video cache: {e}")
