"""
媒体库管理服务
管理视频素材，包括上传、转场镜头分割、标签管理等
"""

import json
import uuid
import os
import hashlib
import shutil
from pathlib import Path
from typing import List, Dict, Optional, Tuple
from dataclasses import dataclass, asdict
from datetime import datetime

from python_core.config import settings
from python_core.utils.logger import logger
from python_core.utils.jsonrpc import create_response_handler

# 依赖管理器 - 遵循SOLID原则
class DependencyManager:
    """依赖管理器 - 单一职责原则(SRP): 只负责管理依赖"""

    def __init__(self):
        self._dependencies = {}
        self._initialize_dependencies()

    def _initialize_dependencies(self):
        """初始化所有依赖"""
        self._check_opencv_dependency()
        self._check_scenedetect_dependency()

    def _check_opencv_dependency(self):
        """检查OpenCV依赖"""
        try:
            import cv2
            import numpy as np
            self._dependencies['opencv'] = {
                'available': True,
                'modules': {'cv2': cv2, 'numpy': np},
                'version': cv2.__version__
            }
            logger.info(f"OpenCV {cv2.__version__} is available")
        except ImportError as e:
            self._dependencies['opencv'] = {
                'available': False,
                'error': str(e),
                'modules': {}
            }
            logger.warning("OpenCV not available. Install opencv-python for full functionality.")

    def _check_scenedetect_dependency(self):
        """检查PySceneDetect依赖"""
        try:
            from scenedetect import VideoManager, SceneManager
            from scenedetect.detectors import ContentDetector
            import scenedetect

            self._dependencies['scenedetect'] = {
                'available': True,
                'modules': {
                    'VideoManager': VideoManager,
                    'SceneManager': SceneManager,
                    'ContentDetector': ContentDetector
                },
                'version': scenedetect.__version__
            }
            logger.info(f"PySceneDetect {scenedetect.__version__} is available for advanced scene detection")
        except ImportError as e:
            self._dependencies['scenedetect'] = {
                'available': False,
                'error': str(e),
                'modules': {}
            }
            logger.warning("PySceneDetect not available. Install scenedetect for better scene detection.")

    def is_available(self, dependency_name: str) -> bool:
        """检查依赖是否可用 - 开闭原则(OCP): 对扩展开放"""
        return self._dependencies.get(dependency_name, {}).get('available', False)

    def get_module(self, dependency_name: str, module_name: str):
        """获取依赖模块"""
        if not self.is_available(dependency_name):
            raise ImportError(f"Dependency '{dependency_name}' is not available")

        modules = self._dependencies[dependency_name]['modules']
        if module_name not in modules:
            raise ImportError(f"Module '{module_name}' not found in dependency '{dependency_name}'")

        return modules[module_name]

    def get_dependency_info(self, dependency_name: str) -> dict:
        """获取依赖信息"""
        return self._dependencies.get(dependency_name, {})

    def get_all_dependencies(self) -> dict:
        """获取所有依赖信息"""
        return self._dependencies.copy()

# 抽象接口 - 接口隔离原则(ISP): 定义专门的接口
from abc import ABC, abstractmethod

class VideoInfoExtractor(ABC):
    """视频信息提取器接口"""

    @abstractmethod
    def extract_video_info(self, file_path: str) -> Dict:
        """提取视频信息"""
        pass

class SceneDetector(ABC):
    """场景检测器接口"""

    @abstractmethod
    def detect_scenes(self, file_path: str, threshold: float = 30.0) -> List[float]:
        """检测场景变化点"""
        pass

class VideoProcessor(ABC):
    """视频处理器接口"""

    @abstractmethod
    def split_video(self, input_path: str, output_dir: str, scene_times: List[float]) -> List[str]:
        """分割视频"""
        pass

class ThumbnailGenerator(ABC):
    """缩略图生成器接口"""

    @abstractmethod
    def generate_thumbnail(self, video_path: str, timestamp: float, output_path: str) -> bool:
        """生成视频缩略图"""
        pass

class VideoSegmentCreator(ABC):
    """视频片段创建器接口"""

    @abstractmethod
    def create_segments_from_scenes(self, video_path: str, scene_changes: List[float],
                                  original_video_id: str, tags: List[str] = None) -> List['VideoSegment']:
        """根据场景变化创建视频片段"""
        pass

class FileHashCalculator(ABC):
    """文件哈希计算器接口"""

    @abstractmethod
    def calculate_hash(self, file_path: str) -> str:
        """计算文件哈希值"""
        pass

# 具体实现类 - 单一职责原则(SRP): 每个类只负责一个功能

class FFProbeVideoInfoExtractor(VideoInfoExtractor):
    """使用FFProbe提取视频信息"""

    def extract_video_info(self, file_path: str) -> Dict:
        """使用ffprobe获取准确的视频信息"""
        file_size = os.path.getsize(file_path)

        try:
            import subprocess
            import json

            cmd = [
                'ffprobe',
                '-v', 'quiet',
                '-print_format', 'json',
                '-show_format',
                '-show_streams',
                file_path
            ]

            result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)

            if result.returncode == 0:
                probe_data = json.loads(result.stdout)

                video_stream = None
                for stream in probe_data.get('streams', []):
                    if stream.get('codec_type') == 'video':
                        video_stream = stream
                        break

                if video_stream:
                    duration = float(probe_data.get('format', {}).get('duration', 0))
                    width = int(video_stream.get('width', 0))
                    height = int(video_stream.get('height', 0))

                    fps_str = video_stream.get('r_frame_rate', '0/1')
                    if '/' in fps_str:
                        num, den = fps_str.split('/')
                        fps = float(num) / float(den) if float(den) != 0 else 0
                    else:
                        fps = float(fps_str)

                    frame_count = int(duration * fps) if fps > 0 else 0

                    logger.info(f"ffprobe video info: duration={duration:.2f}s, fps={fps:.2f}, resolution={width}x{height}")

                    return {
                        'duration': duration,
                        'width': width,
                        'height': height,
                        'fps': fps,
                        'frame_count': frame_count,
                        'file_size': file_size,
                        'codec': video_stream.get('codec_name', 'unknown')
                    }

        except Exception as e:
            logger.warning(f"ffprobe failed: {e}")
            raise

        raise Exception("Failed to extract video info with ffprobe")

class OpenCVVideoInfoExtractor(VideoInfoExtractor):
    """使用OpenCV提取视频信息"""

    def __init__(self, dependency_manager: DependencyManager):
        self.dependency_manager = dependency_manager

    def extract_video_info(self, file_path: str) -> Dict:
        """使用OpenCV获取视频信息"""
        if not self.dependency_manager.is_available('opencv'):
            raise Exception("OpenCV not available")

        cv2 = self.dependency_manager.get_module('opencv', 'cv2')
        file_size = os.path.getsize(file_path)

        try:
            cap = cv2.VideoCapture(file_path)

            fps = cap.get(cv2.CAP_PROP_FPS)
            frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
            width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
            height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
            duration = frame_count / fps if fps > 0 else 0

            cap.release()

            logger.info(f"OpenCV video info: duration={duration:.2f}s, fps={fps:.2f}, resolution={width}x{height}")

            return {
                'duration': duration,
                'width': width,
                'height': height,
                'fps': fps,
                'frame_count': frame_count,
                'file_size': file_size,
                'codec': 'unknown'
            }
        except Exception as e:
            logger.error(f"Failed to get video info with OpenCV: {e}")
            raise

class PySceneDetectSceneDetector(SceneDetector):
    """使用PySceneDetect进行场景检测"""

    def __init__(self, dependency_manager: DependencyManager):
        self.dependency_manager = dependency_manager

    def detect_scenes(self, file_path: str, threshold: float = 30.0) -> List[float]:
        """使用PySceneDetect检测场景变化点"""
        if not self.dependency_manager.is_available('scenedetect'):
            raise Exception("PySceneDetect not available")

        VideoManager = self.dependency_manager.get_module('scenedetect', 'VideoManager')
        SceneManager = self.dependency_manager.get_module('scenedetect', 'SceneManager')
        ContentDetector = self.dependency_manager.get_module('scenedetect', 'ContentDetector')

        try:
            video_manager = VideoManager([file_path])
            scene_manager = SceneManager()
            scene_manager.add_detector(ContentDetector(threshold=threshold))

            video_manager.start()
            scene_manager.detect_scenes(frame_source=video_manager)

            scene_list = scene_manager.get_scene_list()

            scene_changes = [0.0]
            for scene in scene_list:
                start_time = scene[0].get_seconds()
                end_time = scene[1].get_seconds()

                if start_time > 0 and start_time not in scene_changes:
                    scene_changes.append(start_time)

                if end_time not in scene_changes:
                    scene_changes.append(end_time)

            # 如果没有检测到场景变化，添加视频结束时间
            if len(scene_changes) == 1:  # 只有开始时间0.0
                # 获取视频时长
                video_duration = video_manager.get_duration().get_seconds()
                if video_duration > 0:
                    scene_changes.append(video_duration)
                    logger.info(f"No scenes detected, using full video duration: {video_duration:.2f}s")

            scene_changes = sorted(list(set(scene_changes)))
            video_manager.release()

            logger.info(f"PySceneDetect found {len(scene_changes)-1} scene changes in video")
            logger.debug(f"Scene change timestamps: {scene_changes}")

            return scene_changes

        except Exception as e:
            logger.error(f"PySceneDetect failed: {e}")
            raise

class OpenCVSceneDetector(SceneDetector):
    """使用OpenCV进行场景检测"""

    def __init__(self, dependency_manager: DependencyManager):
        self.dependency_manager = dependency_manager

    def detect_scenes(self, file_path: str, threshold: float = 30.0) -> List[float]:
        """使用OpenCV检测场景变化点"""
        if not self.dependency_manager.is_available('opencv'):
            raise Exception("OpenCV not available")

        cv2 = self.dependency_manager.get_module('opencv', 'cv2')
        np = self.dependency_manager.get_module('opencv', 'numpy')

        try:
            cap = cv2.VideoCapture(file_path)
            fps = cap.get(cv2.CAP_PROP_FPS)

            if fps <= 0:
                cap.release()
                logger.warning(f"Invalid fps ({fps}) for video {file_path}")
                return [0.0]

            scene_changes = [0.0]
            prev_frame = None
            frame_count = 0
            frame_skip = max(1, int(fps / 2))

            while True:
                ret, frame = cap.read()
                if not ret:
                    break

                if frame_count % frame_skip == 0:
                    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                    gray = cv2.resize(gray, (320, 240))

                    if prev_frame is not None:
                        diff = cv2.absdiff(prev_frame, gray)
                        mean_diff = np.mean(diff)

                        if mean_diff > threshold:
                            timestamp = frame_count / fps
                            if not scene_changes or timestamp - scene_changes[-1] > 1.0:
                                scene_changes.append(timestamp)
                                logger.debug(f"Scene change detected at {timestamp:.2f}s (diff: {mean_diff:.2f})")

                    prev_frame = gray

                frame_count += 1

            cap.release()

            duration = frame_count / fps if fps > 0 else 0
            if duration > 0 and (not scene_changes or duration - scene_changes[-1] > 0.5):
                scene_changes.append(duration)

            logger.info(f"OpenCV detected {len(scene_changes)-1} scene changes in video")
            return scene_changes

        except Exception as e:
            logger.error(f"Failed to detect scene changes with OpenCV: {e}")
            raise

# 工厂类 - 依赖倒置原则(DIP): 依赖抽象而不是具体实现
class VideoProcessorFactory:
    """视频处理器工厂 - 单一职责原则(SRP): 只负责创建对象"""

    def __init__(self, dependency_manager: DependencyManager):
        self.dependency_manager = dependency_manager

    def create_video_info_extractor(self) -> VideoInfoExtractor:
        """创建视频信息提取器 - 开闭原则(OCP): 对扩展开放"""
        # 优先使用FFProbe，回退到OpenCV
        try:
            return FFProbeVideoInfoExtractor()
        except Exception:
            if self.dependency_manager.is_available('opencv'):
                return OpenCVVideoInfoExtractor(self.dependency_manager)
            else:
                raise Exception("No video info extractor available")

    def create_scene_detector(self) -> SceneDetector:
        """创建场景检测器"""
        # 优先使用PySceneDetect，回退到OpenCV
        if self.dependency_manager.is_available('scenedetect'):
            try:
                return PySceneDetectSceneDetector(self.dependency_manager)
            except Exception:
                pass

        if self.dependency_manager.is_available('opencv'):
            return OpenCVSceneDetector(self.dependency_manager)
        else:
            raise Exception("No scene detector available")

    def create_thumbnail_generator(self) -> ThumbnailGenerator:
        """创建缩略图生成器"""
        if self.dependency_manager.is_available('opencv'):
            return OpenCVThumbnailGenerator(self.dependency_manager)
        else:
            raise Exception("No thumbnail generator available")

    def create_hash_calculator(self) -> FileHashCalculator:
        """创建哈希计算器"""
        return MD5FileHashCalculator()

    def create_video_segment_creator(self) -> VideoSegmentCreator:
        """创建视频片段创建器"""
        hash_calculator = self.create_hash_calculator()
        if self.dependency_manager.is_available('opencv'):
            return OpenCVVideoSegmentCreator(self.dependency_manager, hash_calculator)
        else:
            raise Exception("No video segment creator available")

# 具体实现类继续

class OpenCVThumbnailGenerator(ThumbnailGenerator):
    """使用OpenCV生成缩略图"""

    def __init__(self, dependency_manager: DependencyManager):
        self.dependency_manager = dependency_manager

    def generate_thumbnail(self, video_path: str, timestamp: float, output_path: str) -> bool:
        """生成视频缩略图"""
        if not self.dependency_manager.is_available('opencv'):
            return False

        cv2 = self.dependency_manager.get_module('opencv', 'cv2')

        try:
            cap = cv2.VideoCapture(video_path)
            fps = cap.get(cv2.CAP_PROP_FPS)

            if fps <= 0:
                cap.release()
                return False

            # 跳转到指定时间
            frame_number = int(timestamp * fps)
            cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number)

            ret, frame = cap.read()
            cap.release()

            if ret:
                # 保存缩略图
                success = cv2.imwrite(output_path, frame)
                logger.info(f"Thumbnail generated: {output_path}")
                return success
            else:
                logger.error(f"Failed to read frame at {timestamp}s")
                return False

        except Exception as e:
            logger.error(f"Failed to generate thumbnail: {e}")
            return False

class MD5FileHashCalculator(FileHashCalculator):
    """使用MD5计算文件哈希"""

    def calculate_hash(self, file_path: str) -> str:
        """计算文件MD5哈希值"""
        import hashlib

        try:
            hash_md5 = hashlib.md5()
            with open(file_path, "rb") as f:
                for chunk in iter(lambda: f.read(4096), b""):
                    hash_md5.update(chunk)
            return hash_md5.hexdigest()
        except Exception as e:
            logger.error(f"Failed to calculate MD5 hash: {e}")
            return ""

class OpenCVVideoSegmentCreator(VideoSegmentCreator):
    """使用OpenCV创建视频片段"""

    def __init__(self, dependency_manager: DependencyManager, hash_calculator: FileHashCalculator):
        self.dependency_manager = dependency_manager
        self.hash_calculator = hash_calculator
        self.segments_dir = settings.temp_dir / "video_segments"
        self.segments_dir.mkdir(parents=True, exist_ok=True)

    def create_segments_from_scenes(self, video_path: str, scene_changes: List[float],
                                  original_video_id: str, tags: List[str] = None) -> List['VideoSegment']:
        """根据场景变化创建视频片段"""
        if tags is None:
            tags = []

        # 为分镜片段处理标签：去掉"原始"，添加"分镜"
        segment_tags = [tag for tag in tags if tag != "原始"]
        if "分镜" not in segment_tags:
            segment_tags.append("分镜")

        if not self.dependency_manager.is_available('opencv'):
            logger.warning("OpenCV not available, creating single segment")
            return self._create_single_segment(video_path, original_video_id, segment_tags)

        return self._create_multiple_segments(video_path, scene_changes, original_video_id, segment_tags)

    def _create_single_segment(self, video_path: str, original_video_id: str, tags: List[str]) -> List['VideoSegment']:
        """创建单个片段（当OpenCV不可用时）"""
        import shutil
        import uuid
        from datetime import datetime

        # 获取视频信息（这里需要使用依赖注入的方式）
        video_info = {'duration': 0.0, 'width': 0, 'height': 0, 'fps': 0.0, 'file_size': 0}

        segment_id = str(uuid.uuid4())
        segment_filename = f"{segment_id}.mp4"
        segment_path = self.segments_dir / segment_filename

        # 复制整个视频作为单个片段
        shutil.copy2(video_path, segment_path)

        now = datetime.now().isoformat()
        segment = VideoSegment(
            id=segment_id,
            original_video_id=original_video_id,
            segment_index=0,
            filename=segment_filename,
            file_path=str(segment_path),
            md5_hash=self.hash_calculator.calculate_hash(str(segment_path)),
            file_size=video_info['file_size'],
            duration=video_info['duration'],
            width=video_info['width'],
            height=video_info['height'],
            fps=video_info['fps'],
            format='mp4',
            start_time=0.0,
            end_time=video_info['duration'],
            tags=tags.copy(),
            use_count=0,
            created_at=now,
            updated_at=now
        )

        return [segment]

    def _create_multiple_segments(self, video_path: str, scene_changes: List[float],
                                original_video_id: str, tags: List[str]) -> List['VideoSegment']:
        """创建多个片段（使用OpenCV分割）"""
        if not self.dependency_manager.is_available('opencv'):
            logger.warning("OpenCV not available for video splitting")
            return self._create_single_segment(video_path, original_video_id, tags)

        cv2 = self.dependency_manager.get_module('opencv', 'cv2')
        segments = []

        try:
            # 获取视频信息
            cap = cv2.VideoCapture(video_path)
            fps = cap.get(cv2.CAP_PROP_FPS)
            width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
            height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

            if fps <= 0:
                cap.release()
                logger.warning("Invalid FPS, creating single segment")
                return self._create_single_segment(video_path, original_video_id, tags)

            # 设置视频编码器
            fourcc = cv2.VideoWriter_fourcc(*'mp4v')

            for i in range(len(scene_changes) - 1):
                start_time = scene_changes[i]
                end_time = scene_changes[i + 1]
                duration = end_time - start_time

                # 跳过太短的片段（小于1秒）
                if duration < 1.0:
                    logger.debug(f"Skipping short segment: {duration:.2f}s")
                    continue

                segment_id = str(uuid.uuid4())
                segment_filename = f"{segment_id}.mp4"
                segment_path = self.segments_dir / segment_filename

                # 创建视频写入器
                out = cv2.VideoWriter(str(segment_path), fourcc, fps, (width, height))

                # 跳转到开始帧
                start_frame = int(start_time * fps)
                end_frame = int(end_time * fps)

                cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame)

                frame_count = 0
                target_frames = end_frame - start_frame

                while frame_count < target_frames:
                    ret, frame = cap.read()
                    if not ret:
                        logger.warning(f"Failed to read frame {frame_count}/{target_frames}")
                        break

                    out.write(frame)
                    frame_count += 1

                out.release()

                # 检查文件是否创建成功
                if not segment_path.exists() or segment_path.stat().st_size == 0:
                    logger.error(f"Failed to create segment: {segment_path}")
                    continue

                # 创建片段记录
                from datetime import datetime
                now = datetime.now().isoformat()

                # 创建VideoSegment对象
                segment = VideoSegment(
                    id=segment_id,
                    original_video_id=original_video_id,
                    segment_index=i,
                    filename=segment_filename,
                    file_path=str(segment_path),
                    md5_hash=self.hash_calculator.calculate_hash(str(segment_path)),
                    file_size=segment_path.stat().st_size,
                    duration=duration,
                    width=width,
                    height=height,
                    fps=fps,
                    format='mp4',
                    start_time=start_time,
                    end_time=end_time,
                    tags=tags.copy(),
                    use_count=0,
                    created_at=now,
                    updated_at=now
                )

                segments.append(segment)
                logger.info(f"Created segment {i}: {start_time:.2f}s - {end_time:.2f}s ({duration:.2f}s)")

            cap.release()
            logger.info(f"Successfully created {len(segments)} video segments")

        except Exception as e:
            logger.error(f"Failed to create multiple segments: {e}")
            return self._create_single_segment(video_path, original_video_id, tags)

        return segments

# 全局依赖管理器实例
dependency_manager = DependencyManager()
video_processor_factory = VideoProcessorFactory(dependency_manager)

# 向后兼容的全局变量 - 里氏替换原则(LSP): 保持接口兼容
VIDEO_LIBS_AVAILABLE = dependency_manager.is_available('opencv')
SCENEDETECT_AVAILABLE = dependency_manager.is_available('scenedetect')


@dataclass
class VideoSegment:
    """视频片段数据结构"""
    id: str
    original_video_id: str
    segment_index: int
    filename: str
    file_path: str
    md5_hash: str
    file_size: int
    duration: float
    width: int
    height: int
    fps: float
    format: str
    start_time: float
    end_time: float
    tags: List[str]
    use_count: int
    thumbnail_path: Optional[str] = None
    scene_score: Optional[float] = None  # 场景变化分数
    motion_score: Optional[float] = None  # 运动强度分数
    brightness: Optional[float] = None
    contrast: Optional[float] = None
    created_at: str = ""
    updated_at: str = ""
    is_active: bool = True


@dataclass
class OriginalVideo:
    """原始视频数据结构"""
    id: str
    filename: str
    file_path: str
    md5_hash: str
    file_size: int
    duration: float
    width: int
    height: int
    fps: float
    format: str
    segment_count: int
    tags: List[str]
    created_at: str = ""
    updated_at: str = ""
    is_active: bool = True


class MediaManager:
    """媒体库管理器

    遵循SOLID原则:
    - SRP: 只负责媒体文件的管理和协调
    - OCP: 通过依赖注入支持扩展
    - LSP: 可以替换不同的处理器实现
    - ISP: 使用专门的接口
    - DIP: 依赖抽象接口而不是具体实现
    """

    def __init__(self,
                 dependency_manager: DependencyManager = None,
                 video_info_extractor: VideoInfoExtractor = None,
                 scene_detector: SceneDetector = None,
                 thumbnail_generator: ThumbnailGenerator = None,
                 hash_calculator: FileHashCalculator = None,
                 video_segment_creator: VideoSegmentCreator = None):
        """
        初始化媒体管理器

        Args:
            dependency_manager: 依赖管理器
            video_info_extractor: 视频信息提取器
            scene_detector: 场景检测器
            thumbnail_generator: 缩略图生成器
            hash_calculator: 哈希计算器
            video_segment_creator: 视频片段创建器
        """
        self.cache_dir = settings.temp_dir / "cache"
        self.cache_dir.mkdir(parents=True, exist_ok=True)

        # 数据文件
        self.segments_file = self.cache_dir / "video_segments.json"
        self.original_videos_file = self.cache_dir / "original_videos.json"

        # 依赖注入 - 依赖倒置原则(DIP)
        self.dependency_manager = dependency_manager or globals()['dependency_manager']
        self.factory = VideoProcessorFactory(self.dependency_manager)

        # 延迟初始化处理器 - 单一职责原则(SRP)
        self._video_info_extractor = video_info_extractor
        self._scene_detector = scene_detector
        self._thumbnail_generator = thumbnail_generator
        self._hash_calculator = hash_calculator
        self._video_segment_creator = video_segment_creator

        # 初始化目录
        self._initialize_directories()

        # 加载数据
        self.video_segments = self._load_video_segments()
        self.original_videos = self._load_original_videos()

    def _initialize_directories(self):
        """初始化目录结构 - 单一职责原则(SRP)"""
        self.video_storage_dir = settings.temp_dir / "video_storage"
        self.video_storage_dir.mkdir(parents=True, exist_ok=True)

        self.segments_dir = settings.temp_dir / "video_segments"
        self.segments_dir.mkdir(parents=True, exist_ok=True)

        self.thumbnails_dir = settings.temp_dir / "video_thumbnails"
        self.thumbnails_dir.mkdir(parents=True, exist_ok=True)

    @property
    def video_info_extractor(self) -> VideoInfoExtractor:
        """获取视频信息提取器 - 懒加载"""
        if self._video_info_extractor is None:
            self._video_info_extractor = self.factory.create_video_info_extractor()
        return self._video_info_extractor

    @property
    def scene_detector(self) -> SceneDetector:
        """获取场景检测器 - 懒加载"""
        if self._scene_detector is None:
            self._scene_detector = self.factory.create_scene_detector()
        return self._scene_detector

    @property
    def thumbnail_generator(self) -> ThumbnailGenerator:
        """获取缩略图生成器 - 懒加载"""
        if self._thumbnail_generator is None:
            self._thumbnail_generator = self.factory.create_thumbnail_generator()
        return self._thumbnail_generator

    @property
    def hash_calculator(self) -> FileHashCalculator:
        """获取哈希计算器 - 懒加载"""
        if self._hash_calculator is None:
            self._hash_calculator = self.factory.create_hash_calculator()
        return self._hash_calculator

    @property
    def video_segment_creator(self) -> VideoSegmentCreator:
        """获取视频片段创建器 - 懒加载"""
        if self._video_segment_creator is None:
            self._video_segment_creator = self.factory.create_video_segment_creator()
        return self._video_segment_creator

    def _initialize_directories(self):
        """初始化目录结构 - 单一职责原则(SRP)"""
        self.video_storage_dir = settings.temp_dir / "video_storage"
        self.video_storage_dir.mkdir(parents=True, exist_ok=True)

        self.segments_dir = settings.temp_dir / "video_segments"
        self.segments_dir.mkdir(parents=True, exist_ok=True)

        self.thumbnails_dir = settings.temp_dir / "video_thumbnails"
        self.thumbnails_dir.mkdir(parents=True, exist_ok=True)
    
    def _load_video_segments(self) -> List[VideoSegment]:
        """加载视频片段数据"""
        if self.segments_file.exists():
            try:
                with open(self.segments_file, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                    return [VideoSegment(**item) for item in data]
            except Exception as e:
                logger.error(f"Failed to load video segments: {e}")
                return []
        else:
            return []
    
    def _load_original_videos(self) -> List[OriginalVideo]:
        """加载原始视频数据"""
        if self.original_videos_file.exists():
            try:
                with open(self.original_videos_file, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                    return [OriginalVideo(**item) for item in data]
            except Exception as e:
                logger.error(f"Failed to load original videos: {e}")
                return []
        else:
            return []
    
    def _save_video_segments(self, segments: List[VideoSegment] = None):
        """保存视频片段数据"""
        if segments is None:
            segments = self.video_segments
        
        try:
            data = [asdict(segment) for segment in segments]
            with open(self.segments_file, 'w', encoding='utf-8') as f:
                json.dump(data, f, ensure_ascii=False, indent=2)
            logger.info(f"Video segments saved to {self.segments_file}")
        except Exception as e:
            logger.error(f"Failed to save video segments: {e}")
            raise
    
    def _save_original_videos(self, videos: List[OriginalVideo] = None):
        """保存原始视频数据"""
        if videos is None:
            videos = self.original_videos
        
        try:
            data = [asdict(video) for video in videos]
            with open(self.original_videos_file, 'w', encoding='utf-8') as f:
                json.dump(data, f, ensure_ascii=False, indent=2)
            logger.info(f"Original videos saved to {self.original_videos_file}")
        except Exception as e:
            logger.error(f"Failed to save original videos: {e}")
            raise
    
    def _calculate_md5(self, file_path: str) -> str:
        """计算文件MD5哈希值"""
        hash_md5 = hashlib.md5()
        with open(file_path, "rb") as f:
            for chunk in iter(lambda: f.read(4096), b""):
                hash_md5.update(chunk)
        return hash_md5.hexdigest()
    
    def _get_video_info(self, file_path: str) -> Dict:
        """获取视频基本信息 - 使用依赖注入的提取器"""
        try:
            return self.video_info_extractor.extract_video_info(file_path)
        except Exception as e:
            logger.error(f"Failed to get video info: {e}")
            # 返回基本信息作为后备
            file_size = os.path.getsize(file_path) if os.path.exists(file_path) else 0
            return {
                'duration': 0.0,
                'width': 0,
                'height': 0,
                'fps': 0.0,
                'frame_count': 0,
                'file_size': file_size,
                'codec': 'unknown'
            }
    
    def _detect_scene_changes(self, file_path: str, threshold: float = 30.0) -> List[float]:
        """检测场景变化点 - 使用依赖注入的检测器"""
        try:
            return self.scene_detector.detect_scenes(file_path, threshold)
        except Exception as e:
            logger.error(f"Scene detection failed: {e}")
            # 返回基本的开始和结束时间作为后备
            try:
                video_info = self._get_video_info(file_path)
                duration = video_info.get('duration', 0)
                return [0.0, duration] if duration > 0 else [0.0]
            except:
                return [0.0]


    
    def _generate_thumbnail(self, video_path: str, timestamp: float, output_path: str) -> bool:
        """生成视频缩略图 - 使用依赖注入的生成器"""
        try:
            return self.thumbnail_generator.generate_thumbnail(video_path, timestamp, output_path)
        except Exception as e:
            logger.error(f"Failed to generate thumbnail: {e}")
            return False

    def _calculate_md5(self, file_path: str) -> str:
        """计算文件MD5哈希值 - 使用依赖注入的计算器"""
        try:
            return self.hash_calculator.calculate_hash(file_path)
        except Exception as e:
            logger.error(f"Failed to calculate hash: {e}")
            return ""

    def _split_video_by_scenes(self, video_path: str, scene_changes: List[float],
                              original_video_id: str, tags: List[str] = None) -> List[VideoSegment]:
        """根据场景变化分割视频 - 使用依赖注入的创建器"""
        try:
            return self.video_segment_creator.create_segments_from_scenes(
                video_path, scene_changes, original_video_id, tags
            )
        except Exception as e:
            logger.error(f"Failed to split video using segment creator: {e}")
            # 返回空列表作为后备
            return []



    def get_video_by_md5(self, md5_hash: str) -> Optional[Dict]:
        """根据MD5获取原始视频"""
        for video in self.original_videos:
            if video.md5_hash == md5_hash and video.is_active:
                return asdict(video)
        return None

    def get_segment_by_md5(self, md5_hash: str) -> Optional[Dict]:
        """根据MD5获取视频片段"""
        for segment in self.video_segments:
            if segment.md5_hash == md5_hash and segment.is_active:
                return asdict(segment)
        return None

    def get_all_segments(self) -> List[Dict]:
        """获取所有视频片段"""
        return [asdict(segment) for segment in self.video_segments if segment.is_active]

    def get_all_original_videos(self) -> List[Dict]:
        """获取所有原始视频"""
        return [asdict(video) for video in self.original_videos if video.is_active]

    def get_segments_by_video_id(self, video_id: str) -> List[Dict]:
        """获取指定原始视频的所有片段"""
        segments = []
        for segment in self.video_segments:
            if segment.original_video_id == video_id and segment.is_active:
                segments.append(asdict(segment))
        return sorted(segments, key=lambda x: x['segment_index'])

    def upload_video_file(self, source_path: str, filename: str = None, tags: List[str] = None) -> Dict:
        """上传单个视频文件并分割成片段"""
        if not os.path.exists(source_path):
            raise FileNotFoundError(f"Source file not found: {source_path}")

        if tags is None:
            tags = []

        # 计算MD5
        md5_hash = self._calculate_md5(source_path)

        # 检查是否已存在相同MD5的视频
        existing = self.get_video_by_md5(md5_hash)
        if existing:
            logger.info(f"Video with MD5 {md5_hash} already exists")
            existing_segments = self.get_segments_by_video_id(existing['id'])

            # 如果已存在的视频没有分镜头，重新生成分镜头
            if not existing_segments:
                logger.info(f"Existing video has no segments, regenerating segments for video {existing['id']}")

                # 获取存储的视频文件路径
                stored_video_path = existing['file_path']

                # 检测场景变化
                scene_changes = self._detect_scene_changes(stored_video_path)
                logger.info(f"Detected {len(scene_changes)} scene changes for existing video")

                # 为分镜片段处理标签：去掉"原始"，添加"分镜"
                segment_tags = [tag for tag in tags if tag != "原始"] if tags else []
                if "分镜" not in segment_tags:
                    segment_tags.append("分镜")

                # 分割视频成片段
                segments = self._split_video_by_scenes(stored_video_path, scene_changes, existing['id'], segment_tags)

                # 保存新生成的片段
                self.video_segments.extend(segments)
                self._save_video_segments()

                # 更新原始视频的segment_count
                for i, video in enumerate(self.original_videos):
                    if video.id == existing['id']:
                        video.segment_count = len(segments)
                        video.updated_at = datetime.now().isoformat()
                        self.original_videos[i] = video
                        break

                self._save_original_videos()

                logger.info(f"Regenerated {len(segments)} segments for existing video {existing['id']}")

                return {
                    'original_video': existing,
                    'segments': [asdict(segment) if hasattr(segment, '__dict__') else segment for segment in segments],
                    'is_duplicate': True,
                    'segments_regenerated': True
                }
            else:
                return {
                    'original_video': existing,
                    'segments': existing_segments,
                    'is_duplicate': True,
                    'segments_regenerated': False
                }

        # 生成新的视频ID和文件名
        video_id = str(uuid.uuid4())
        if filename is None:
            filename = os.path.basename(source_path)

        # 获取文件扩展名
        file_ext = os.path.splitext(filename)[1].lower()
        stored_filename = f"{video_id}{file_ext}"
        stored_path = self.video_storage_dir / stored_filename

        # 复制文件到存储目录
        shutil.copy2(source_path, stored_path)

        # 获取视频基本信息
        video_info = self._get_video_info(str(stored_path))

        # 检测场景变化
        scene_changes = self._detect_scene_changes(str(stored_path))

        # 创建原始视频记录
        now = datetime.now().isoformat()
        original_video = OriginalVideo(
            id=video_id,
            filename=filename,
            file_path=str(stored_path),
            md5_hash=md5_hash,
            file_size=video_info['file_size'],
            duration=video_info['duration'],
            width=video_info['width'],
            height=video_info['height'],
            fps=video_info['fps'],
            format=file_ext[1:] if file_ext else 'unknown',
            segment_count=len(scene_changes) - 1,
            tags=tags,
            created_at=now,
            updated_at=now
        )
        
        # 分割视频成片段，传递标签给片段（去掉"原始"，添加"分镜"）
        segment_tags = [tag for tag in tags if tag != "原始"]
        if "分镜" not in segment_tags:
            segment_tags.append("分镜")
        segments = self._split_video_by_scenes(str(stored_path), scene_changes, video_id, segment_tags)
        
        # 保存数据
        self.original_videos.append(original_video)
        self.video_segments.extend(segments)

        self._save_original_videos()
        self._save_video_segments()

        logger.info(f"Uploaded video: {filename} (MD5: {md5_hash}, {len(segments)} segments)")

        return {
            'original_video': asdict(original_video),
            'segments': [asdict(segment) for segment in segments],
            'is_duplicate': False
        }

    def batch_upload_video_files(self, source_directory: str, tags: List[str] = None) -> Dict:
        """批量上传视频文件"""
        if not os.path.exists(source_directory):
            raise FileNotFoundError(f"Source directory not found: {source_directory}")

        if tags is None:
            tags = []

        # 支持的视频格式
        video_extensions = {'.mp4', '.avi', '.mov', '.mkv', '.wmv', '.flv', '.webm', '.m4v'}

        results = {
            'total_files': 0,
            'uploaded_files': 0,
            'skipped_files': 0,
            'failed_files': 0,
            'total_segments': 0,
            'uploaded_list': [],
            'skipped_list': [],
            'failed_list': []
        }

        # 遍历目录中的所有文件
        for root, dirs, files in os.walk(source_directory):
            for file in files:
                file_path = os.path.join(root, file)
                file_ext = os.path.splitext(file)[1].lower()

                # 检查是否为视频文件
                if file_ext not in video_extensions:
                    continue

                results['total_files'] += 1

                try:
                    # 尝试上传文件
                    result = self.upload_video_file(file_path, file, tags)

                    if result['is_duplicate']:
                        results['skipped_files'] += 1
                        results['skipped_list'].append({
                            'filename': file,
                            'reason': 'Already exists (same MD5)'
                        })
                    else:
                        results['uploaded_files'] += 1
                        results['total_segments'] += len(result['segments'])
                        results['uploaded_list'].append(result)

                except Exception as e:
                    results['failed_files'] += 1
                    results['failed_list'].append({
                        'filename': file,
                        'error': str(e)
                    })
                    logger.error(f"Failed to upload {file}: {e}")

        logger.info(f"Batch upload completed: {results['uploaded_files']} uploaded, "
                   f"{results['skipped_files']} skipped, {results['failed_files']} failed, "
                   f"{results['total_segments']} total segments created")

        return results

    def add_segment_tags(self, segment_id: str, tags: List[str]) -> bool:
        """为视频片段添加标签"""
        for i, segment in enumerate(self.video_segments):
            if segment.id == segment_id and segment.is_active:
                # 合并标签，去重
                existing_tags = set(segment.tags)
                new_tags = existing_tags.union(set(tags))
                segment.tags = list(new_tags)
                segment.updated_at = datetime.now().isoformat()

                self.video_segments[i] = segment
                self._save_video_segments()

                logger.info(f"Added tags {tags} to segment {segment_id}")
                return True
        return False

    def remove_segment_tags(self, segment_id: str, tags: List[str]) -> bool:
        """移除视频片段的标签"""
        for i, segment in enumerate(self.video_segments):
            if segment.id == segment_id and segment.is_active:
                # 移除指定标签
                existing_tags = set(segment.tags)
                remaining_tags = existing_tags - set(tags)
                segment.tags = list(remaining_tags)
                segment.updated_at = datetime.now().isoformat()

                self.video_segments[i] = segment
                self._save_video_segments()

                logger.info(f"Removed tags {tags} from segment {segment_id}")
                return True
        return False

    def increment_segment_usage(self, segment_id: str) -> bool:
        """增加视频片段使用次数"""
        for i, segment in enumerate(self.video_segments):
            if segment.id == segment_id and segment.is_active:
                segment.use_count += 1
                segment.updated_at = datetime.now().isoformat()

                self.video_segments[i] = segment
                self._save_video_segments()

                logger.info(f"Incremented usage count for segment {segment_id} to {segment.use_count}")
                return True
        return False

    def get_segments_by_tags(self, tags: List[str], match_all: bool = False) -> List[Dict]:
        """根据标签搜索视频片段"""
        results = []
        tag_set = set(tags)

        for segment in self.video_segments:
            if not segment.is_active:
                continue

            segment_tags = set(segment.tags)

            if match_all:
                # 必须包含所有标签
                if tag_set.issubset(segment_tags):
                    results.append(asdict(segment))
            else:
                # 包含任意一个标签
                if tag_set.intersection(segment_tags):
                    results.append(asdict(segment))

        return results

    def get_popular_segments(self, limit: int = 10) -> List[Dict]:
        """获取最常用的视频片段"""
        active_segments = [segment for segment in self.video_segments if segment.is_active]
        sorted_segments = sorted(active_segments, key=lambda x: x.use_count, reverse=True)

        return [asdict(segment) for segment in sorted_segments[:limit]]

    def search_segments(self, keyword: str) -> List[Dict]:
        """搜索视频片段"""
        keyword = keyword.lower()
        results = []

        for segment in self.video_segments:
            if not segment.is_active:
                continue

            # 搜索文件名和标签
            if (keyword in segment.filename.lower() or
                any(keyword in tag.lower() for tag in segment.tags)):
                results.append(asdict(segment))

        return results

    def delete_segment(self, segment_id: str) -> bool:
        """删除视频片段"""
        for i, segment in enumerate(self.video_segments):
            if segment.id == segment_id:
                # 删除物理文件
                try:
                    if os.path.exists(segment.file_path):
                        os.remove(segment.file_path)

                    # 删除缩略图
                    if segment.thumbnail_path and os.path.exists(segment.thumbnail_path):
                        os.remove(segment.thumbnail_path)
                except Exception as e:
                    logger.error(f"Failed to delete physical files: {e}")

                # 从列表中移除
                deleted_segment = self.video_segments.pop(i)
                self._save_video_segments()

                logger.info(f"Deleted segment: {segment_id} - {deleted_segment.filename}")
                return True
        return False

    def delete_original_video(self, video_id: str) -> bool:
        """删除原始视频及其所有片段"""
        # 先删除所有相关片段
        segments_to_delete = [s for s in self.video_segments if s.original_video_id == video_id]
        for segment in segments_to_delete:
            self.delete_segment(segment.id)

        # 删除原始视频
        for i, video in enumerate(self.original_videos):
            if video.id == video_id:
                # 删除物理文件
                try:
                    if os.path.exists(video.file_path):
                        os.remove(video.file_path)
                except Exception as e:
                    logger.error(f"Failed to delete video file: {e}")

                # 从列表中移除
                deleted_video = self.original_videos.pop(i)
                self._save_original_videos()

                logger.info(f"Deleted original video: {video_id} - {deleted_video.filename}")
                return True
        return False


# 全局实例 - 延迟初始化
_global_media_manager = None

def get_media_manager() -> MediaManager:
    """获取全局MediaManager实例 - 懒加载"""
    global _global_media_manager
    if _global_media_manager is None:
        _global_media_manager = MediaManager()
    return _global_media_manager


def main():
    """命令行接口 - 使用JSON-RPC协议"""
    import sys
    import json
    from python_core.utils.jsonrpc import create_response_handler

    # 创建响应处理器
    rpc = create_response_handler()

    if len(sys.argv) < 2:
        rpc.error("INVALID_REQUEST", "No command specified")
        return

    command = sys.argv[1]

    try:
        # 获取全局MediaManager实例
        media_manager = get_media_manager()

        if command == "get_all_segments":
            segments = media_manager.get_all_segments()
            rpc.success(segments)

        elif command == "get_all_original_videos":
            videos = media_manager.get_all_original_videos()
            rpc.success(videos)

        elif command == "get_segments_by_video_id":
            if len(sys.argv) < 3:
                rpc.error("INVALID_REQUEST", "Video ID required")
                return
            video_id = sys.argv[2]
            segments = media_manager.get_segments_by_video_id(video_id)
            rpc.success(segments)

        elif command == "upload_video_file":
            if len(sys.argv) < 3:
                rpc.error("INVALID_REQUEST", "Source path required")
                return
            source_path = sys.argv[2]
            filename = sys.argv[3] if len(sys.argv) > 3 else None
            tags = json.loads(sys.argv[4]) if len(sys.argv) > 4 else []
            result = media_manager.upload_video_file(source_path, filename, tags)
            rpc.success(result)

        elif command == "batch_upload_video_files":
            if len(sys.argv) < 3:
                rpc.error("INVALID_REQUEST", "Source directory required")
                return
            source_directory = sys.argv[2]
            tags = json.loads(sys.argv[3]) if len(sys.argv) > 3 else []
            result = media_manager.batch_upload_video_files(source_directory, tags)
            rpc.success(result)

        elif command == "add_segment_tags":
            if len(sys.argv) < 4:
                rpc.error("INVALID_REQUEST", "Segment ID and tags required")
                return
            segment_id = sys.argv[2]
            tags = json.loads(sys.argv[3])
            success = media_manager.add_segment_tags(segment_id, tags)
            rpc.success(success)

        elif command == "remove_segment_tags":
            if len(sys.argv) < 4:
                rpc.error("INVALID_REQUEST", "Segment ID and tags required")
                return
            segment_id = sys.argv[2]
            tags = json.loads(sys.argv[3])
            success = media_manager.remove_segment_tags(segment_id, tags)
            rpc.success(success)

        elif command == "increment_segment_usage":
            if len(sys.argv) < 3:
                rpc.error("INVALID_REQUEST", "Segment ID required")
                return
            segment_id = sys.argv[2]
            success = media_manager.increment_segment_usage(segment_id)
            rpc.success(success)

        elif command == "get_segments_by_tags":
            if len(sys.argv) < 3:
                rpc.error("INVALID_REQUEST", "Tags required")
                return
            tags = json.loads(sys.argv[2])
            match_all = json.loads(sys.argv[3]) if len(sys.argv) > 3 else False
            segments = media_manager.get_segments_by_tags(tags, match_all)
            rpc.success(segments)

        elif command == "get_popular_segments":
            limit = int(sys.argv[2]) if len(sys.argv) > 2 else 10
            segments = media_manager.get_popular_segments(limit)
            rpc.success(segments)

        elif command == "search_segments":
            if len(sys.argv) < 3:
                rpc.error("INVALID_REQUEST", "Search keyword required")
                return
            keyword = sys.argv[2]
            results = media_manager.search_segments(keyword)
            rpc.success(results)

        elif command == "delete_segment":
            if len(sys.argv) < 3:
                rpc.error("INVALID_REQUEST", "Segment ID required")
                return
            segment_id = sys.argv[2]
            success = media_manager.delete_segment(segment_id)
            rpc.success(success)

        elif command == "delete_original_video":
            if len(sys.argv) < 3:
                rpc.error("INVALID_REQUEST", "Video ID required")
                return
            video_id = sys.argv[2]
            success = media_manager.delete_original_video(video_id)
            rpc.success(success)

        else:
            rpc.error("INVALID_REQUEST", f"Unknown command: {command}")

    except Exception as e:
        logger.error(f"Command execution failed: {e}")
        rpc.error("INTERNAL_ERROR", str(e))


if __name__ == "__main__":
    main()
