"""
MP3 → Opus 预转码服务

将 MP3 音频转为 Opus 帧列表（JSON + base64），供 hw_service_go 直接下载播放，
跳过实时 ffmpeg 转码，大幅降低首帧延迟和 CPU 消耗。

Opus 参数与 hw_service_go 保持一致：16kHz, 单声道, 60ms/帧
"""
import base64
import json
import logging
import subprocess

import opuslib

logger = logging.getLogger(__name__)

SAMPLE_RATE = 16000
CHANNELS = 1
FRAME_DURATION_MS = 60
FRAME_SIZE = SAMPLE_RATE * FRAME_DURATION_MS // 1000  # 960 samples
BYTES_PER_FRAME = FRAME_SIZE * 2  # 16bit = 2 bytes per sample


def convert_mp3_to_opus_json(mp3_bytes: bytes) -> str:
    """
    将 MP3 音频数据转码为 Opus 帧 JSON。

    流程: MP3 bytes → ffmpeg(PCM 16kHz mono s16le) → opuslib(60ms Opus 帧)

    Returns:
        JSON 字符串，包含 base64 编码的 Opus 帧列表
    """
    # 1. ffmpeg: MP3 → PCM (16kHz, mono, signed 16-bit little-endian)
    proc = subprocess.run(
        [
            'ffmpeg', '-nostdin', '-loglevel', 'error',
            '-i', 'pipe:0',
            '-ar', str(SAMPLE_RATE),
            '-ac', str(CHANNELS),
            '-f', 's16le',
            'pipe:1',
        ],
        input=mp3_bytes,
        capture_output=True,
        timeout=120,
    )
    if proc.returncode != 0:
        stderr = proc.stderr.decode(errors='replace')
        raise RuntimeError(f'ffmpeg 转码失败: {stderr}')

    pcm = proc.stdout
    if len(pcm) < BYTES_PER_FRAME:
        raise RuntimeError(f'PCM 数据过短: {len(pcm)} bytes')

    # 2. Opus 编码：逐帧编码
    encoder = opuslib.Encoder(SAMPLE_RATE, CHANNELS, 'audio')
    frames = []
    for offset in range(0, len(pcm) - BYTES_PER_FRAME + 1, BYTES_PER_FRAME):
        chunk = pcm[offset:offset + BYTES_PER_FRAME]
        opus_frame = encoder.encode(chunk, FRAME_SIZE)
        frames.append(base64.b64encode(opus_frame).decode('ascii'))

    logger.info(f'Opus 预转码完成: {len(frames)} 帧, '
                f'约 {len(frames) * FRAME_DURATION_MS / 1000:.1f}s 音频')

    return json.dumps({
        'sample_rate': SAMPLE_RATE,
        'channels': CHANNELS,
        'frame_duration_ms': FRAME_DURATION_MS,
        'frames': frames,
    }, separators=(',', ':'))  # 紧凑格式，减少体积
