import asyncio
import time
import aiohttp
import requests
from urllib.parse import urlparse, parse_qs
from typing import Optional, Dict
from config.logger import setup_logging
from core.utils.cache.manager import cache_manager
from core.utils.cache.config import CacheType

TAG = __name__
logger = setup_logging()


class VoiceprintProvider:
    """声纹识别服务提供者"""
    
    def __init__(self, config: dict):
        self.original_url = config.get("url", "")
        self.speakers = config.get("speakers", [])
        self.speaker_map = self._parse_speakers()
        # 声纹识别相似度阈值，默认0.4
        self.similarity_threshold = float(config.get("similarity_threshold", 0.4))
        
        # 解析API地址和密钥
        self.api_url = None
        self.api_key = None
        self.speaker_ids = []
        
        if not self.original_url:
            logger.bind(tag=TAG).warning("声纹识别URL未配置，声纹识别将被禁用")
            self.enabled = False
        else:
            # 解析URL和key
            parsed_url = urlparse(self.original_url)
            base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
            
            # 从查询参数中提取key
            query_params = parse_qs(parsed_url.query)
            self.api_key = query_params.get('key', [''])[0]
            
            if not self.api_key:
                logger.bind(tag=TAG).error("URL中未找到key参数，声纹识别将被禁用")
                self.enabled = False
            else:
                # 构造identify接口地址
                self.api_url = f"{base_url}/voiceprint/identify"
                
                # 提取speaker_ids
                for speaker_str in self.speakers:
                    try:
                        parts = speaker_str.split(",", 2)
                        if len(parts) >= 1:
                            speaker_id = parts[0].strip()
                            self.speaker_ids.append(speaker_id)
                    except Exception:
                        continue
                
                # 检查是否有有效的说话人配置
                if not self.speaker_ids:
                    logger.bind(tag=TAG).warning("未配置有效的说话人，声纹识别将被禁用")
                    self.enabled = False
                else:
                    # 进行健康检查，验证服务器是否可用
                    if self._check_server_health():
                        self.enabled = True
                        logger.bind(tag=TAG).info(f"声纹识别已启用: API={self.api_url}, 说话人={len(self.speaker_ids)}个, 相似度阈值={self.similarity_threshold}")
                    else:
                        self.enabled = False
                        logger.bind(tag=TAG).warning(f"声纹识别服务器不可用，声纹识别已禁用: {self.api_url}")
    
    def _parse_speakers(self) -> Dict[str, Dict[str, str]]:
        """解析说话人配置"""
        speaker_map = {}
        for speaker_str in self.speakers:
            try:
                parts = speaker_str.split(",", 2)
                if len(parts) >= 3:
                    speaker_id, name, description = parts[0].strip(), parts[1].strip(), parts[2].strip()
                    speaker_map[speaker_id] = {
                        "name": name,
                        "description": description
                    }
            except Exception as e:
                logger.bind(tag=TAG).warning(f"解析说话人配置失败: {speaker_str}, 错误: {e}")
        return speaker_map
    
    def _check_server_health(self) -> bool:
        """检查声纹识别服务器健康状态"""
        if not self.api_url or not self.api_key:
            return False
    
        cache_key = f"{self.api_url}:{self.api_key}"
        
        # 检查缓存
        cached_result = cache_manager.get(CacheType.VOICEPRINT_HEALTH, cache_key)
        if cached_result is not None:
            logger.bind(tag=TAG).debug(f"使用缓存的健康状态: {cached_result}")
            return cached_result
        
        # 缓存过期或不存在
        logger.bind(tag=TAG).info("执行声纹服务器健康检查")
        
        try:
            # 健康检查URL
            parsed_url = urlparse(self.api_url)
            health_url = f"{parsed_url.scheme}://{parsed_url.netloc}/voiceprint/health?key={self.api_key}"
            
            # 发送健康检查请求
            response = requests.get(health_url, timeout=3)
            
            if response.status_code == 200:
                result = response.json()
                if result.get("status") == "healthy":
                    logger.bind(tag=TAG).info("声纹识别服务器健康检查通过")
                    is_healthy = True
                else:
                    logger.bind(tag=TAG).warning(f"声纹识别服务器状态异常: {result}")
                    is_healthy = False
            else:
                logger.bind(tag=TAG).warning(f"声纹识别服务器健康检查失败: HTTP {response.status_code}")
                is_healthy = False
                
        except requests.exceptions.ConnectTimeout:
            logger.bind(tag=TAG).warning("声纹识别服务器连接超时")
            is_healthy = False
        except requests.exceptions.ConnectionError:
            logger.bind(tag=TAG).warning("声纹识别服务器连接被拒绝")
            is_healthy = False
        except Exception as e:
            logger.bind(tag=TAG).warning(f"声纹识别服务器健康检查异常: {e}")
            is_healthy = False
        
        # 使用全局缓存管理器缓存结果
        cache_manager.set(CacheType.VOICEPRINT_HEALTH, cache_key, is_healthy)
        logger.bind(tag=TAG).info(f"健康检查结果已缓存: {is_healthy}")
        
        return is_healthy
    
    async def identify_speaker(self, audio_data: bytes, session_id: str) -> Optional[str]:
        """识别说话人"""
        if not self.enabled or not self.api_url or not self.api_key:
            logger.bind(tag=TAG).debug("声纹识别功能已禁用或未配置，跳过识别")
            return None
            
        try:
            api_start_time = time.monotonic()
            
            # 准备请求头
            headers = {
                'Authorization': f'Bearer {self.api_key}',
                'Accept': 'application/json'
            }
            
            # 准备multipart/form-data数据
            data = aiohttp.FormData()
            data.add_field('speaker_ids', ','.join(self.speaker_ids))
            data.add_field('file', audio_data, filename='audio.wav', content_type='audio/wav')
            
            timeout = aiohttp.ClientTimeout(total=10)
            
            # 网络请求
            async with aiohttp.ClientSession(timeout=timeout) as session:
                async with session.post(self.api_url, headers=headers, data=data) as response:
                    
                    if response.status == 200:
                        result = await response.json()
                        speaker_id = result.get("speaker_id")
                        score = result.get("score", 0)
                        total_elapsed_time = time.monotonic() - api_start_time
                        
                        logger.bind(tag=TAG).info(f"声纹识别耗时: {total_elapsed_time:.3f}s")
                        
                        # 相似度阈值检查
                        if score < self.similarity_threshold:
                            logger.bind(tag=TAG).warning(f"声纹识别相似度{score:.3f}低于阈值{self.similarity_threshold}")
                            return "未知说话人"
                        
                        if speaker_id and speaker_id in self.speaker_map:
                            result_name = self.speaker_map[speaker_id]["name"]
                            logger.bind(tag=TAG).info(f"声纹识别成功: {result_name} (相似度: {score:.3f})")
                            return result_name
                        else:
                            logger.bind(tag=TAG).warning(f"未识别的说话人ID: {speaker_id}")
                            return "未知说话人"
                    else:
                        logger.bind(tag=TAG).error(f"声纹识别API错误: HTTP {response.status}")
                        return None
                        
        except asyncio.TimeoutError:
            elapsed = time.monotonic() - api_start_time
            logger.bind(tag=TAG).error(f"声纹识别超时: {elapsed:.3f}s")
            return None
        except Exception as e:
            elapsed = time.monotonic() - api_start_time
            logger.bind(tag=TAG).error(f"声纹识别失败: {e}")
            return None

