"""
语音识别服务

支持：
- 阿里云百炼 DashScope ASR（推荐）
- OpenAI Whisper API
"""
import logging
import os
import tempfile
import threading
import time
from pathlib import Path
from typing import Optional

import dashscope
from dashscope.audio.asr import Recognition, RecognitionCallback, RecognitionResult
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.config import settings
from ai_platform.models import LLMProvider

logger = logging.getLogger(__name__)


class SpeechService:
    """
    语音识别服务
    
    支持多种语音识别后端：
    - 阿里云百炼 DashScope（默认，推荐）
    - OpenAI Whisper API
    """
    
    def __init__(self, db: Optional[AsyncSession] = None):
        self._db = db
        # 延迟解析：优先从数据库获取，初始化时先从 settings 读取作为兜底（settings 已自动从环境变量读取）
        self.dashscope_api_key = getattr(settings, 'DASHSCOPE_API_KEY', None)
        self._resolved = False
    
    async def _resolve_dashscope_api_key(self):
        """
        异步解析 DashScope API Key
        
        优先级：
        1. 数据库中 qwen 类型提供商的 API Key
        2. settings / 环境变量中的 DASHSCOPE_API_KEY（兜底）
        """
        if self._resolved:
            return
        self._resolved = True
        
        if not self._db:
            return
        
        try:
            result = await self._db.execute(
                select(LLMProvider).where(
                    LLMProvider.provider_type == 'qwen',
                    LLMProvider.is_active == True,
                    LLMProvider.is_deleted == False
                )
            )
            provider = result.scalar_one_or_none()
            if provider and provider.api_key:
                self.dashscope_api_key = provider.api_key
                logger.info(f"Speech: Using API key from database provider: {provider.name}")
        except Exception as e:
            logger.debug(f"Speech: Failed to get provider from database: {e}")
    
    def transcribe(
        self,
        audio_file,
        language: str = 'zh',
        provider: str = 'dashscope',
    ) -> dict:
        """
        语音转文字
        
        Args:
            audio_file: 音频文件（Django UploadedFile 或文件路径或 bytes）
            language: 语言代码，如 'zh', 'en'
            provider: 服务提供商，'dashscope' 或 'openai'
            
        Returns:
            {
                'success': bool,
                'text': str,
                'duration': float,
                'error': str,
            }
        """
        if provider == 'dashscope':
            return self._transcribe_dashscope(audio_file, language)
        elif provider == 'openai':
            return self._transcribe_openai(audio_file, language)
        else:
            return {
                'success': False,
                'text': '',
                'error': f'不支持的提供商: {provider}',
            }
    
    def _transcribe_dashscope(self, audio_file, language: str) -> dict:
        """
        使用阿里云百炼 DashScope 进行语音识别
        
        使用 fun-asr-realtime 模型，支持多种音频格式
        """
        try:
            if not self.dashscope_api_key:
                return {
                    'success': False,
                    'text': '',
                    'error': '未配置 DASHSCOPE_API_KEY',
                }
            
            # 设置 API Key
            dashscope.api_key = self.dashscope_api_key
            dashscope.base_websocket_api_url = 'wss://dashscope.aliyuncs.com/api-ws/v1/inference'
            
            # 获取音频数据和格式
            audio_data, audio_format, sample_rate = self._prepare_audio(audio_file)
            
            if not audio_data:
                return {
                    'success': False,
                    'text': '',
                    'error': '无法读取音频文件',
                }
            
            # 使用同步方式收集识别结果
            result_text = []
            error_message = []
            completed = threading.Event()
            
            class SyncCallback(RecognitionCallback):
                def on_complete(self) -> None:
                    completed.set()
                
                def on_error(self, result: RecognitionResult) -> None:
                    error_message.append(result.message if hasattr(result, 'message') else str(result))
                    completed.set()
                
                def on_event(self, result: RecognitionResult) -> None:
                    sentence = result.get_sentence()
                    if 'text' in sentence and RecognitionResult.is_sentence_end(sentence):
                        result_text.append(sentence['text'])
            
            callback = SyncCallback()
            
            # 创建识别实例
            recognition = Recognition(
                model='paraformer-realtime-v2',  # 使用 paraformer 模型，效果更好
                format=audio_format,
                sample_rate=sample_rate,
                callback=callback,
            )
            
            # 开始识别
            recognition.start()
            
            # 分块发送音频数据
            chunk_size = 3200  # 每次发送 3200 字节
            offset = 0
            while offset < len(audio_data):
                chunk = audio_data[offset:offset + chunk_size]
                recognition.send_audio_frame(chunk)
                offset += chunk_size
                time.sleep(0.05)  # 稍微延迟，模拟实时流
            
            # 停止识别
            recognition.stop()
            
            # 等待完成（最多 30 秒）
            completed.wait(timeout=30)
            
            if error_message:
                return {
                    'success': False,
                    'text': '',
                    'error': error_message[0],
                }
            
            final_text = ''.join(result_text)
            
            return {
                'success': True,
                'text': final_text,
                'duration': len(audio_data) / (sample_rate * 2),  # 估算时长
                'error': '',
            }
            
        except Exception as e:
            logger.exception(f'阿里云语音识别失败: {e}')
            return {
                'success': False,
                'text': '',
                'error': str(e),
            }
    
    def _prepare_audio(self, audio_file) -> tuple:
        """
        准备音频数据
        
        前端已直接录制 WAV 格式（16kHz, 16bit, 单声道），无需转换
        阿里云 DashScope ASR 支持的格式：pcm, wav, mp3, opus, speex, aac, amr
        
        Returns:
            (audio_data: bytes, format: str, sample_rate: int)
        """
        audio_data = None
        audio_format = 'wav'
        sample_rate = 16000
        
        if isinstance(audio_file, bytes):
            audio_data = audio_file
        elif hasattr(audio_file, 'read'):
            # Django UploadedFile
            audio_data = audio_file.read()
            file_name = getattr(audio_file, 'name', 'audio.wav').lower()
            
            # 根据文件名判断格式
            if file_name.endswith('.mp3'):
                audio_format = 'mp3'
            elif file_name.endswith('.pcm'):
                audio_format = 'pcm'
            elif file_name.endswith('.opus'):
                audio_format = 'opus'
            else:
                audio_format = 'wav'
        elif isinstance(audio_file, (str, Path)):
            # 文件路径
            file_path = Path(audio_file)
            with open(file_path, 'rb') as f:
                audio_data = f.read()
            
            suffix = file_path.suffix.lower()
            if suffix == '.mp3':
                audio_format = 'mp3'
            elif suffix == '.pcm':
                audio_format = 'pcm'
            elif suffix == '.opus':
                audio_format = 'opus'
            else:
                audio_format = 'wav'
        
        return audio_data, audio_format, sample_rate
    
    def _transcribe_openai(self, audio_file, language: str, provider_config: dict = None) -> dict:
        """使用 OpenAI Whisper API 进行语音识别"""
        try:
            import openai
            
            if not provider_config:
                return {
                    'success': False,
                    'text': '',
                    'error': '未配置 OpenAI 提供商',
                }
            
            # 创建客户端
            client = openai.OpenAI(
                api_key=provider_config.get('api_key', ''),
                base_url=provider_config.get('api_base') or None,
            )
            
            # 处理文件
            if isinstance(audio_file, bytes):
                # bytes 数据，写入临时文件
                with tempfile.NamedTemporaryFile(
                    suffix='.wav',
                    delete=False,
                ) as tmp:
                    tmp.write(audio_file)
                    tmp_path = tmp.name
                
                try:
                    with open(tmp_path, 'rb') as f:
                        response = client.audio.transcriptions.create(
                            model='whisper-1',
                            file=f,
                            language=language,
                            response_format='verbose_json',
                        )
                finally:
                    Path(tmp_path).unlink(missing_ok=True)
            elif hasattr(audio_file, 'read'):
                # UploadedFile
                file_content = audio_file.read()
                file_name = getattr(audio_file, 'name', 'audio.webm')
                
                # 写入临时文件
                with tempfile.NamedTemporaryFile(
                    suffix=Path(file_name).suffix or '.webm',
                    delete=False,
                ) as tmp:
                    tmp.write(file_content)
                    tmp_path = tmp.name
                
                try:
                    with open(tmp_path, 'rb') as f:
                        response = client.audio.transcriptions.create(
                            model='whisper-1',
                            file=f,
                            language=language,
                            response_format='verbose_json',
                        )
                finally:
                    Path(tmp_path).unlink(missing_ok=True)
            else:
                with open(audio_file, 'rb') as f:
                    response = client.audio.transcriptions.create(
                        model='whisper-1',
                        file=f,
                        language=language,
                        response_format='verbose_json',
                    )
            
            return {
                'success': True,
                'text': response.text,
                'duration': getattr(response, 'duration', 0),
                'error': '',
            }
            
        except Exception as e:
            logger.exception(f'OpenAI 语音识别失败: {e}')
            return {
                'success': False,
                'text': '',
                'error': str(e),
            }
    
    def text_to_speech(
        self,
        text: str,
        voice: str = 'sambert-zhichu-v1',
        provider: str = 'dashscope',
    ) -> dict:
        """
        文字转语音（TTS）
        
        Args:
            text: 要转换的文字
            voice: 声音类型
            provider: 服务提供商
            
        Returns:
            {
                'success': bool,
                'audio_data': bytes,
                'content_type': str,
                'error': str,
            }
        """
        if provider == 'dashscope':
            return self._tts_dashscope(text, voice)
        elif provider == 'openai':
            return self._tts_openai(text, voice)
        else:
            return {
                'success': False,
                'audio_data': b'',
                'error': f'不支持的提供商: {provider}',
            }
    
    def _tts_dashscope(self, text: str, voice: str) -> dict:
        """使用阿里云百炼 TTS"""
        try:
            from dashscope.audio.tts import SpeechSynthesizer
            
            if not self.dashscope_api_key:
                return {
                    'success': False,
                    'audio_data': b'',
                    'error': '未配置 DASHSCOPE_API_KEY',
                }
            
            dashscope.api_key = self.dashscope_api_key
            
            # 调用 TTS
            result = SpeechSynthesizer.call(
                model=voice,  # sambert-zhichu-v1, sambert-zhimiao-emo-v1 等
                text=text,
                sample_rate=16000,
                format='wav',
            )
            
            if result.get_audio_data():
                return {
                    'success': True,
                    'audio_data': result.get_audio_data(),
                    'content_type': 'audio/wav',
                    'error': '',
                }
            else:
                return {
                    'success': False,
                    'audio_data': b'',
                    'error': '语音合成失败',
                }
            
        except Exception as e:
            logger.exception(f'阿里云 TTS 失败: {e}')
            return {
                'success': False,
                'audio_data': b'',
                'error': str(e),
            }
    
    def _tts_openai(self, text: str, voice: str, provider_config: dict = None) -> dict:
        """使用 OpenAI TTS API"""
        try:
            import openai
            
            if not provider_config:
                return {
                    'success': False,
                    'audio_data': b'',
                    'error': '未配置 OpenAI 提供商',
                }
            
            client = openai.OpenAI(
                api_key=provider_config.get('api_key', ''),
                base_url=provider_config.get('api_base') or None,
            )
            
            response = client.audio.speech.create(
                model='tts-1',
                voice=voice,  # alloy, echo, fable, onyx, nova, shimmer
                input=text,
            )
            
            return {
                'success': True,
                'audio_data': response.content,
                'content_type': 'audio/mpeg',
                'error': '',
            }
            
        except Exception as e:
            logger.exception(f'OpenAI TTS 失败: {e}')
            return {
                'success': False,
                'audio_data': b'',
                'error': str(e),
            }
    
    async def get_openai_provider_config(self) -> dict:
        """异步获取OpenAI提供商配置"""
        if not self._db:
            return {}
        
        result = await self._db.execute(
            select(LLMProvider).where(
                LLMProvider.provider_type == 'openai',
                LLMProvider.is_active == True,
                LLMProvider.is_deleted == False
            )
        )
        provider = result.scalar_one_or_none()
        if not provider:
            return {}
        
        return {
            'api_key': provider.api_key,
            'api_base': provider.api_base,
        }
