import cv2
import os
from moviepy.editor import VideoFileClip
import numpy as np
from concurrent.futures import ThreadPoolExecutor, as_completed
import threading

class VideoProcessor:
    """视频处理器：分割视频、提取关键帧等（支持多线程并发）"""
    
    def __init__(self):
        self.segment_duration = 30  # 每段30秒
        self.max_workers = 3  # 视频处理I/O密集，使用3个线程避免资源竞争
        self.segments_lock = threading.Lock()  # 保护 segments 列表
        self.timeout = 120  # 每个片段超时时间（秒）
    
    def process(self, video_path, process_id, timestamps=None):
        """处理视频文件
        
        Args:
            video_path: 视频文件路径
            process_id: 处理ID
            timestamps: 语义分段时间戳（可选），格式为字典
        """
        try:
            results = {
                'segments': [],
                'keyframes': [],
                'duration': 0,
                'fps': 0,
                'resolution': None
            }
            
            # 使用moviepy获取视频信息
            video = VideoFileClip(video_path)
            duration = video.duration
            fps = video.fps
            resolution = video.size
            
            results['duration'] = duration
            results['fps'] = fps
            results['resolution'] = resolution
            
            # 根据是否有时间戳选择不同的分割方式
            if timestamps:
                # 基于语义的智能分割
                segments = self._split_video_by_timestamps(video, process_id, timestamps)
            else:
                # 固定时长分割
                segments = self._split_video(video, process_id, duration)
            
            results['segments'] = segments
            
            # 提取关键帧
            keyframes = self._extract_keyframes(video_path, process_id, duration)
            results['keyframes'] = keyframes
            
            video.close()
            
            return results
            
        except Exception as e:
            print(f"视频处理错误: {str(e)}")
            raise
    
    def _split_video_by_timestamps(self, video, process_id, timestamps):
        """根据语义时间戳分割视频（支持多线程并发）"""
        segments = []
        output_dir = os.path.join('processed', 'videos', process_id)
        os.makedirs(output_dir, exist_ok=True)

        # 获取视频路径和总时长
        video_path = video.filename
        try:
            video_duration = video.duration
            print(f"   📹 视频总时长: {video_duration:.2f}秒")
        except Exception as e:
            print(f"   ⚠️  无法获取视频时长: {str(e)}")
            video_duration = None

        print(f"   🚀 使用多线程并发切割（{self.max_workers} 个线程）")

        # 准备任务列表
        tasks = []
        for idx, (clip_name, data) in enumerate(timestamps.items()):
            start_time = data['start']
            end_time = data['end']
            content = data['content']

            # 检查时间范围是否有效
            if video_duration:
                if start_time >= video_duration:
                    print(f"   ⚠️  片段 {idx+1}: 起始时间({start_time}s)超出视频长度，跳过")
                    continue
                if end_time > video_duration:
                    print(f"   ⚠️  片段 {idx+1}: 结束时间调整为 {video_duration}s")
                    end_time = video_duration

            if end_time <= start_time:
                print(f"   ❌ 片段 {idx+1}: 时间范围无效，跳过")
                continue

            tasks.append((idx, clip_name, start_time, end_time, content, video_path, output_dir, process_id))

        print(f"   📋 准备 {len(tasks)} 个视频切割任务")

        # 使用线程池并发处理（带超时机制）
        with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
            future_to_task = {
                executor.submit(self._process_single_segment, task): task
                for task in tasks
            }

            completed = 0
            for future in as_completed(future_to_task, timeout=self.timeout * len(tasks)):
                completed += 1
                task = future_to_task[future]
                idx, clip_name = task[0], task[1]

                try:
                    # 为每个任务设置超时
                    result = future.result(timeout=self.timeout)
                    if result:
                        # 使用锁保护共享列表
                        with self.segments_lock:
                            segments.append(result)
                        print(f"   ✅ [{completed}/{len(tasks)}] {clip_name}: 切割成功")
                    else:
                        print(f"   ⚠️  [{completed}/{len(tasks)}] {clip_name}: 切割失败")
                except TimeoutError:
                    print(f"   ⏱️  [{completed}/{len(tasks)}] {clip_name}: 超时（>{self.timeout}秒），跳过")
                except Exception as e:
                    print(f"   ❌ [{completed}/{len(tasks)}] {clip_name}: 错误 - {str(e)}")

        print(f"\n   ✅ 视频切割完成，成功生成 {len(segments)}/{len(timestamps)} 个片段")
        return segments

    def _process_single_segment(self, task):
        """
        处理单个视频片段的切割（线程安全，带资源清理）

        Args:
            task: (idx, clip_name, start_time, end_time, content, video_path, output_dir, process_id)

        Returns:
            dict: 片段信息 或 None
        """
        idx, clip_name, start_time, end_time, content, video_path, output_dir, process_id = task

        video = None
        segment_clip = None

        try:
            # 每个线程创建独立的 VideoFileClip 实例（线程安全）
            video = VideoFileClip(video_path)

            # 提取内容的前20个中文字符作为文件名
            safe_content = ''.join(c for c in content[:20] if '\u4e00' <= c <= '\u9fff')
            if not safe_content:
                safe_content = f"segment_{idx+1}"

            segment_filename = f"{clip_name}_{safe_content}.mp4"
            segment_path = os.path.join(output_dir, segment_filename)

            # 切割视频片段
            segment_clip = video.subclip(start_time, end_time)

            if segment_clip is None:
                return None

            # 检查是否有音频轨道
            has_audio = segment_clip.audio is not None

            # 写入视频文件，根据音频情况调整参数
            try:
                if has_audio:
                    # 有音频，正常保存
                    segment_clip.write_videofile(
                        segment_path,
                        codec='libx264',
                        audio_codec='aac',
                        verbose=False,
                        logger=None,
                        temp_audiofile=os.path.join(output_dir, f'temp_audio_{clip_name}_{os.getpid()}.m4a'),
                        remove_temp=True,
                        preset='ultrafast',
                        threads=1  # 每个片段使用1线程（避免资源竞争）
                    )
                else:
                    # 无音频，只保存视频
                    segment_clip.write_videofile(
                        segment_path,
                        codec='libx264',
                        audio=False,
                        verbose=False,
                        logger=None,
                        preset='ultrafast',
                        threads=1
                    )
            except AttributeError:
                # 如果音频处理出错，尝试不带音频保存
                segment_clip_no_audio = segment_clip.without_audio()
                segment_clip_no_audio.write_videofile(
                    segment_path,
                    codec='libx264',
                    audio=False,
                    verbose=False,
                    logger=None,
                    preset='ultrafast',
                    threads=1
                )
                segment_clip_no_audio.close()

            result = {
                'index': idx + 1,
                'start_time': start_time,
                'end_time': end_time,
                'duration': end_time - start_time,
                'content': content,
                'path': f'/processed/videos/{process_id}/{segment_filename}',
                'filename': segment_filename
            }

            return result

        except Exception as e:
            return None

        finally:
            # 确保资源被释放（无论是否出错）
            try:
                if segment_clip is not None:
                    segment_clip.close()
            except:
                pass

            try:
                if video is not None:
                    video.close()
            except:
                pass

    def _split_video(self, video, process_id, duration):
        segments = []
        num_segments = int(duration // self.segment_duration) + 1

        output_dir = os.path.join('processed', 'videos', process_id)
        os.makedirs(output_dir, exist_ok=True)

        for i in range(num_segments):
            start_time = i * self.segment_duration
            end_time = min((i + 1) * self.segment_duration, duration)


        segment_filename = f"segment_{i+1}.mp4"
        segment_path = os.path.join(output_dir, segment_filename)

        # 切割视频片段
        segment_clip = video.subclip(start_time, end_time)

        # 更安全的视频写入方式，避免音频处理错误
        try:
            # 检查是否有音频轨道
            has_audio = segment_clip.audio is not None and segment_clip.audio.reader is not None

            if has_audio:
                # 尝试包含音频的写入
                segment_clip.write_videofile(
                    segment_path,
                    codec='libx264',
                    audio_codec='aac',
                    verbose=False,
                    logger=None
                )
            else:
                # 无音频直接写入
                segment_clip.write_videofile(
                    segment_path,
                    codec='libx264',
                    audio=False,
                    verbose=False,
                    logger=None
                )
        except Exception as e:
            # 如果上述方法都失败，则尝试不包含音频的方式
            print(f"   ⚠️  视频处理出现问题，尝试无音频模式: {str(e)}")
            try:
                segment_clip.write_videofile(
                    segment_path,
                    codec='libx264',
                    audio=False,
                    verbose=False,
                    logger=None
                )
            except Exception as e2:
                print(f"   ❌ 视频保存失败: {str(e2)}")
                raise e2
        finally:
            segment_clip.close()

        segments.append({
            'index': i + 1,
            'start_time': start_time,
            'end_time': end_time,
            'duration': end_time - start_time,
            'path': f'/processed/videos/{process_id}/{segment_filename}',
            'filename': segment_filename
        })

        return segments

    
    def _extract_keyframes(self, video_path, process_id, duration, num_frames=10):
        """提取关键帧"""
        keyframes = []
        output_dir = os.path.join('processed', 'images', process_id, 'keyframes')
        os.makedirs(output_dir, exist_ok=True)
        
        cap = cv2.VideoCapture(video_path)
        total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
        
        # 均匀提取关键帧
        frame_indices = np.linspace(0, total_frames - 1, num_frames, dtype=int)
        
        for idx, frame_idx in enumerate(frame_indices):
            cap.set(cv2.CAP_PROP_POS_FRAMES, frame_idx)
            ret, frame = cap.read()
            
            if ret:
                frame_filename = f"keyframe_{idx+1}.jpg"
                frame_path = os.path.join(output_dir, frame_filename)
                cv2.imwrite(frame_path, frame)
                
                timestamp = frame_idx / cap.get(cv2.CAP_PROP_FPS)
                
                keyframes.append({
                    'index': idx + 1,
                    'frame_number': int(frame_idx),
                    'timestamp': timestamp,
                    'path': f'/processed/images/{process_id}/keyframes/{frame_filename}',
                    'filename': frame_filename
                })
        
        cap.release()
        return keyframes


