import os
import subprocess

from loguru import logger
from pydub import AudioSegment


def check_ffmpeg():
    """检查FFmpeg是否已安装"""
    try:
        subprocess.run(['ffmpeg', '-version'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        return True
    except FileNotFoundError:
        return False


def merge_audio_files(total_duration: float, list_script: list, output_audio_path: str):
    """
    合并音频文件
    
    Args:
        task_id: 任务ID
        total_duration: 总时长
        list_script: 完整脚本信息，包含duration时长和audio路径
    
    Returns:
        str: 合并后的音频文件路径
    """
    # 检查FFmpeg是否安装
    if not check_ffmpeg():
        logger.error("FFmpeg未安装，无法合并音频文件")
        return None

    def _parse_time_to_seconds(value: str) -> float:
        if not value:
            return 0.0
        if ',' in value:
            hms, ms = value.split(',', 1)
            milliseconds = int(ms)
        else:
            hms = value
            milliseconds = 0
        h, m, s = map(int, hms.split(':'))
        return h * 3600 + m * 60 + s + milliseconds / 1000.0

    def _parse_edited_start_seconds(time_range: str):
        if not time_range or '-' not in time_range:
            return None
        start_part = time_range.split('-', 1)[0]
        return _parse_time_to_seconds(start_part)

    def _parse_edited_end_seconds(time_range: str):
        if not time_range or '-' not in time_range:
            return None
        end_part = time_range.split('-', 1)[1]
        return _parse_time_to_seconds(end_part)

    estimated_duration = float(total_duration or 0)
    for segment in list_script:
        start_seconds = _parse_edited_start_seconds(segment.get('editedTimeRange', ''))
        end_seconds = _parse_edited_end_seconds(segment.get('editedTimeRange', ''))
        duration = float(segment.get('duration', 0) or 0)
        if start_seconds is not None and end_seconds is not None:
            estimated_duration = max(estimated_duration, end_seconds)
        elif start_seconds is not None:
            estimated_duration = max(estimated_duration, start_seconds + duration)

    # 创建一个空的音频片段（避免基准过短导致末尾被截断）
    final_audio = AudioSegment.silent(duration=int(round(estimated_duration * 1000)))

    # 回退位置（旧逻辑）：当 editedTimeRange 缺失时使用 duration 累加
    current_position = 0.0  # 秒

    # 遍历脚本中的每个片段
    for segment in list_script:
        try:
            # 获取片段时长（秒）
            duration = float(segment.get('duration', 0) or 0)
            start_seconds = _parse_edited_start_seconds(segment.get('editedTimeRange', ''))
            if start_seconds is None:
                start_seconds = current_position

            # 检查audio字段是否为空
            if segment['audio'] and os.path.exists(segment['audio']):
                # 加载TTS音频文件
                tts_audio = AudioSegment.from_file(segment['audio'])

                # 将TTS音频添加到最终音频
                final_audio = final_audio.overlay(tts_audio, position=int(round(start_seconds * 1000)))
            else:
                # audio为空，不添加音频，仅保留间隔
                logger.info(f"片段 {segment.get('timestamp', '')} 没有音频文件，保留 {duration} 秒的间隔")

            # 更新下一个片段的开始位置
            current_position = max(current_position + duration, start_seconds + duration)

        except Exception as e:
            logger.error(f"处理音频片段时出错: {str(e)}")
            # 即使处理失败，也要更新位置，确保后续片段位置正确
            if 'duration' in segment:
                current_position += float(segment['duration'] or 0)
            continue

    # 保存合并后的音频文件
    output_audio_path = os.path.join(output_audio_path, "merger_audio.mp3")
    final_audio.export(output_audio_path, format="mp3")
    logger.info(f"合并后的音频文件已保存: {output_audio_path}")

    return output_audio_path


def time_to_seconds(time_str):
    """
    将时间字符串转换为秒数，支持多种格式：
    1. 'HH:MM:SS,mmm' (时:分:秒,毫秒)
    2. 'MM:SS,mmm' (分:秒,毫秒)
    3. 'SS,mmm' (秒,毫秒)
    """
    try:
        # 处理毫秒部分
        if ',' in time_str:
            time_part, ms_part = time_str.split(',')
            ms = float(ms_part) / 1000
        else:
            time_part = time_str
            ms = 0

        # 分割时间部分
        parts = time_part.split(':')

        if len(parts) == 3:  # HH:MM:SS
            h, m, s = map(int, parts)
            seconds = h * 3600 + m * 60 + s
        elif len(parts) == 2:  # MM:SS
            m, s = map(int, parts)
            seconds = m * 60 + s
        else:  # SS
            seconds = int(parts[0])

        return seconds + ms
    except (ValueError, IndexError) as e:
        logger.error(f"Error parsing time {time_str}: {str(e)}")
        return 0.0


def extract_timestamp(filename):
    """
    从文件名中提取开始和结束时间戳
    例如: "audio_00_06,500-00_24,800.mp3" -> (6.5, 24.8)
    """
    try:
        # 从文件名中提取时间部分
        time_part = filename.split('_', 1)[1].split('.')[0]  # 获取 "00_06,500-00_24,800" 部分
        start_time, end_time = time_part.split('-')  # 分割成开始和结束时间

        # 将下划线格式转换回冒号格式
        start_time = start_time.replace('_', ':')
        end_time = end_time.replace('_', ':')

        # 将时间戳转换为秒
        start_seconds = time_to_seconds(start_time)
        end_seconds = time_to_seconds(end_time)

        return start_seconds, end_seconds
    except Exception as e:
        logger.error(f"Error extracting timestamp from {filename}: {str(e)}")
        return 0.0, 0.0


if __name__ == "__main__":
    # 示例用法
    total_duration = 90

    video_script = [
        {'picture': '【解说】好的，各位，欢迎回到我的频道！《庆余年 2》刚开播就给了我们一个王炸！范闲在北齐"死"了？这怎么可能！',
         'timestamp': '00:00:00-00:00:26',
         'narration': '好的各位，欢迎回到我的频道！《庆余年 2》刚开播就给了我们一个王炸！范闲在北齐"死"了？这怎么可能！上集片尾那个巨大的悬念，这一集就立刻揭晓了！范闲假死归来，他面临的第一个，也是最大的难关，就是如何面对他最敬爱的，同时也是最可怕的那个人——庆帝！',
         'OST': 0, 'duration': 26,
         'audio': '/Users/apple/Desktop/home/NarratoAI/storage/tasks/qyn2-2-demo/audio_00_00_00-00_01_15.mp3'},
        {'picture': '【解说】上一集我们看到，范闲在北齐遭遇了惊天变故，生死不明！', 'timestamp': '00:01:15-00:01:29',
         'narration': '但我们都知道，他绝不可能就这么轻易退场！第二集一开场，范闲就已经秘密回到了京都。他的生死传闻，可不像我们想象中那样只是小范围流传，而是…',
         'OST': 0, 'duration': 14,
         'audio': '/Users/apple/Desktop/home/NarratoAI/storage/tasks/qyn2-2-demo/audio_00_01_15-00_04_40.mp3'},
        {'picture': '画面切到王启年小心翼翼地向范闲汇报。', 'timestamp': '00:04:41-00:04:58',
         'narration': '我发现大人的死讯不光是在民间,在官场上也它传开了,所以呢,所以啊,可不是什么好事,将来您跟陛下怎么交代,这可是欺君之罪',
         'OST': 1, 'duration': 17,
         'audio': ''},
        {'picture': '【解说】"欺君之罪"！在封建王朝，这可是抄家灭族的大罪！搁一般人，肯定脚底抹油溜之大吉了。',
         'timestamp': '00:04:58-00:05:20',
         'narration': '"欺君之罪"！在封建王朝，这可是抄家灭族的大罪！搁一般人，肯定脚底抹油溜之大吉了。但范闲是谁啊？他偏要反其道而行之！他竟然决定，直接去见庆帝！冒着天大的风险，用"假死"这个事实去赌庆帝的态度！',
         'OST': 0, 'duration': 22,
         'audio': '/Users/apple/Desktop/home/NarratoAI/storage/tasks/qyn2-2-demo/audio_00_04_58-00_05_45.mp3'},
        {'picture': '【解说】但想见庆帝，哪有那么容易？范闲艺高人胆大，竟然选择了最激进的方式——闯宫！',
         'timestamp': '00:05:45-00:05:53',
         'narration': '但想见庆帝，哪有那么容易？范闲艺高人胆大，竟然选择了最激进的方式——闯宫！',
         'OST': 0, 'duration': 8,
         'audio': '/Users/apple/Desktop/home/NarratoAI/storage/tasks/qyn2-2-demo/audio_00_05_45-00_06_00.mp3'},
        {'picture': '画面切换到范闲蒙面闯入皇宫，被侍卫包围的场景。', 'timestamp': '00:06:00-00:06:03',
         'narration': '抓刺客',
         'OST': 1, 'duration': 3,
         'audio': ''}]

    output_file = merge_audio_files(total_duration, video_script)
    print(output_file)
