"""
矢量化服务模块 - 使用统一异常处理机制
"""

import aiohttp
import time
import urllib3
from typing import Callable, Optional, Dict, Any
import logging
import os
import asyncio
from pathlib import Path

# 导入基础服务类
from utils.service_base import (
    BaseService, PollingMixin, ServiceError, ServiceTimeoutError, 
    ServiceNetworkError, RetryConfig, TimeoutConfig
)

# 禁用SSL警告
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

logger = logging.getLogger(__name__)


class VectorizerServiceError(ServiceError):
    """矢量化服务特定异常"""
    pass


class VectorizerService(BaseService, PollingMixin):
    """矢量化服务类 - 继承统一异常处理机制"""
    
 #   def __init__(self, base_url: str = "https://frp-dad.com:50529"):
    def __init__(self, base_url: str = "https://127.0.0.1:8090"):
        # 配置重试和超时
        retry_config = RetryConfig(
            max_retries=3,
            base_delay=2.0,
            max_delay=30.0
        )
        
        timeout_config = TimeoutConfig(
            connection_timeout=60.0,
            read_timeout=240.0,
            total_timeout=1200.0
        )
        
        super().__init__(
            name="VectorizerService",
            base_url=base_url,
            retry_config=retry_config,
            timeout_config=timeout_config
        )

    async def image_to_eps(self, 
                          image_path: str, 
                          save_eps_path: Optional[str] = None, 
                          timeout: int = 1200, 
                          poll_interval: float = 2.0, 
                          status_callback: Optional[Callable[[str, dict], None]] = None) -> str:
        """
        将图片转换为EPS矢量文件
        
        Args:
            image_path: 输入图片路径
            save_eps_path: 输出EPS文件路径（可选）
            timeout: 最大等待时间（秒）
            poll_interval: 轮询间隔（秒）
            status_callback: 状态回调函数
            
        Returns:
            str: EPS文件保存路径
            
        Raises:
            VectorizerServiceError: 矢量化服务异常
            ServiceTimeoutError: 超时异常
            ServiceNetworkError: 网络异常
        """
        # 验证输入文件
        if not os.path.exists(image_path):
            raise VectorizerServiceError(f"输入图片文件不存在: {image_path}")
        
        # 设置输出路径
        if save_eps_path is None:
            save_eps_path = os.path.splitext(image_path)[0] + '.eps'
        
        # 确保输出目录存在
        output_dir = os.path.dirname(save_eps_path)
        if output_dir:
            os.makedirs(output_dir, exist_ok=True)
        
        try:
            # 1. 上传图片
            task_id = await self.execute_with_retry(
                self._upload_image,
                image_path,
                status_callback,
                error_context=" - 上传图片"
            )
            
            # 2. 轮询等待处理完成
            await self.execute_with_retry(
                self._wait_for_processing,
                task_id,
                timeout,
                poll_interval,
                status_callback,
                error_context=" - 等待处理完成"
            )
            
            # 3. 下载结果文件
            await self.execute_with_retry(
                self._download_result,
                task_id,
                save_eps_path,
                status_callback,
                error_context=" - 下载结果文件"
            )
            
            # 验证输出文件
            if not os.path.exists(save_eps_path) or os.path.getsize(save_eps_path) == 0:
                raise VectorizerServiceError(f"输出文件创建失败或为空: {save_eps_path}")
            
            self.logger.info(f"矢量化转换成功: {image_path} -> {save_eps_path}")
            
            if status_callback:
                status_callback('finished', {
                    'message': '转换完成！',
                    'input_path': image_path,
                    'output_path': save_eps_path
                })
            
            return save_eps_path
            
        except (ServiceTimeoutError, ServiceNetworkError, VectorizerServiceError):
            # 直接传递这些异常
            if status_callback:
                status_callback('error', {'message': '处理失败，请稍后重试'})
            raise
        except Exception as e:
            error_msg = f"矢量化转换失败: {str(e)}"
            self.logger.error(error_msg)
            if status_callback:
                status_callback('error', {'message': error_msg})
            raise VectorizerServiceError(error_msg)

    async def _upload_image(self, 
                           image_path: str, 
                           status_callback: Optional[Callable[[str, dict], None]] = None) -> str:
        """上传图片到矢量化服务"""
        if status_callback:
            status_callback('uploading', {'message': '正在上传图片...', 'image_path': image_path})
        
        async with await self.create_http_session() as session:
            with open(image_path, 'rb') as f:
                data = aiohttp.FormData()
                data.add_field('file', f, filename=os.path.basename(image_path))
                
                async with session.post(f"{self.base_url}/add_task", data=data, ssl=False) as resp:
                    if resp.status != 200:
                        error_text = await resp.text()
                        raise VectorizerServiceError(f"上传失败 - HTTP {resp.status}: {error_text}")
                    
                    response_data = await resp.json()
                    
                    if response_data.get('code') != 0:
                        error_msg = response_data.get('message', '未知错误')
                        raise VectorizerServiceError(f"上传失败: {error_msg}")
                    
                    task_id = response_data.get('id') or response_data.get('taskid')
                    if not task_id:
                        raise VectorizerServiceError("上传失败，未获取到任务ID")
                    
                    self.logger.info(f"图片上传成功，任务ID: {task_id}")
                    
                    if status_callback:
                        status_callback('uploaded', {
                            'message': '图片上传成功，开始处理...',
                            'taskid': task_id
                        })
                    
                    return task_id

    async def _wait_for_processing(self, 
                                  task_id: str, 
                                  timeout: int, 
                                  poll_interval: float,
                                  status_callback: Optional[Callable[[str, dict], None]] = None):
        """等待处理完成"""
        start_time = time.time()
        poll_count = 0
        consecutive_failures = 0
        max_consecutive_failures = 5
        
        async with await self.create_http_session() as session:
            while True:
                poll_count += 1
                elapsed_time = time.time() - start_time
                
                # 检查超时
                if elapsed_time > timeout:
                    await self.cleanup_failed_task(task_id)
                    raise ServiceTimeoutError(f"处理超时 (超过{timeout}秒) - 任务ID: {task_id}")
                
                if status_callback:
                    progress = min(90, int(90 * elapsed_time / timeout))
                    status_callback('processing', {
                        'message': f'正在处理中... (第{poll_count}次检查)',
                        'taskid': task_id,
                        'elapsed_time': elapsed_time,
                        'poll_count': poll_count,
                        'progress': progress
                    })
                
                try:
                    async with session.get(f"{self.base_url}/try_get", 
                                         params={'taskid': task_id}, 
                                         ssl=False) as resp:
                        if resp.status != 200:
                            consecutive_failures += 1
                            error_text = await resp.text()
                            self.logger.warning(f"状态查询失败 - HTTP {resp.status}: {error_text}")
                            
                            if consecutive_failures >= max_consecutive_failures:
                                await self.cleanup_failed_task(task_id)
                                raise VectorizerServiceError(f"连续{consecutive_failures}次状态查询失败")
                            
                            await asyncio.sleep(poll_interval * 2)  # 失败时等待更长时间
                            continue
                        
                        # 重置失败计数
                        consecutive_failures = 0
                        
                        result = await resp.json()
                        
                        if result.get('code') == 0:
                            # 处理完成
                            self.logger.info(f"任务处理完成 - 任务ID: {task_id}, 耗时: {elapsed_time:.2f}秒")
                            if status_callback:
                                status_callback('completed', {
                                    'message': '处理完成，准备下载...',
                                    'taskid': task_id,
                                    'total_time': elapsed_time,
                                    'poll_count': poll_count
                                })
                            return
                        
                        elif result.get('code') == -1:
                            # 处理失败
                            error_msg = result.get('message', '处理失败')
                            await self.cleanup_failed_task(task_id)
                            raise VectorizerServiceError(f"服务器处理失败: {error_msg}")
                        
                        # 其他状态码，继续等待
                        self.logger.debug(f"任务处理中 - 任务ID: {task_id}, 状态码: {result.get('code')}")
                
                except VectorizerServiceError:
                    # 直接传递服务异常
                    raise
                except Exception as e:
                    consecutive_failures += 1
                    self.logger.warning(f"轮询检查异常 (第{consecutive_failures}次): {str(e)}")
                    
                    if consecutive_failures >= max_consecutive_failures:
                        await self.cleanup_failed_task(task_id)
                        raise VectorizerServiceError(f"连续{consecutive_failures}次轮询异常: {str(e)}")
                
                await asyncio.sleep(poll_interval)

    async def _download_result(self, 
                              task_id: str, 
                              save_path: str,
                              status_callback: Optional[Callable[[str, dict], None]] = None):
        """下载处理结果"""
        if status_callback:
            status_callback('downloading', {'message': '正在下载EPS文件...', 'taskid': task_id})
        
        async with await self.create_http_session() as session:
            async with session.get(f"{self.base_url}/get_image", 
                                 params={'taskid': task_id}, 
                                 ssl=False) as resp:
                if resp.status != 200:
                    error_text = await resp.text()
                    raise VectorizerServiceError(f"下载失败 - HTTP {resp.status}: {error_text}")
                
                # 检查内容长度
                content_length = resp.headers.get('Content-Length')
                if content_length and int(content_length) == 0:
                    raise VectorizerServiceError("下载的文件为空")
                
                # 使用临时文件确保原子写入
                temp_path = save_path + '.tmp'
                try:
                    with open(temp_path, 'wb') as f:
                        bytes_written = 0
                        while True:
                            chunk = await resp.content.read(8192)
                            if not chunk:
                                break
                            f.write(chunk)
                            bytes_written += len(chunk)
                    
                    # 验证文件大小
                    if bytes_written == 0:
                        raise VectorizerServiceError("下载的文件为空")
                    
                    # 原子移动到最终位置
                    os.rename(temp_path, save_path)
                    
                    self.logger.info(f"文件下载成功: {save_path}, 大小: {bytes_written} 字节")
                    
                except Exception as e:
                    # 清理临时文件
                    if os.path.exists(temp_path):
                        os.remove(temp_path)
                    raise VectorizerServiceError(f"文件下载失败: {str(e)}")

    async def get_system_status(self) -> Dict[str, Any]:
        """获取系统状态"""
        return await self.execute_with_retry(
            self._do_get_system_status,
            error_context=" - 获取系统状态"
        )

    async def _do_get_system_status(self) -> Dict[str, Any]:
        """执行系统状态查询"""
        async with await self.create_http_session() as session:
            async with session.get(f"{self.base_url}/status", ssl=False) as resp:
                if resp.status != 200:
                    error_text = await resp.text()
                    raise VectorizerServiceError(f"获取系统状态失败 - HTTP {resp.status}: {error_text}")
                
                return await resp.json()

    async def _do_health_check(self) -> bool:
        """执行健康检查"""
        try:
            status = await self._do_get_system_status()
            return True
        except Exception as e:
            self.logger.warning(f"健康检查失败: {str(e)}")
            return False

    async def cleanup_failed_task(self, task_id: str) -> None:
        """清理失败的任务"""
        try:
            # 尝试取消任务（如果服务支持）
            async with await self.create_http_session() as session:
                async with session.delete(f"{self.base_url}/cancel_task", 
                                        params={'taskid': task_id}, 
                                        ssl=False) as resp:
                    if resp.status == 200:
                        self.logger.info(f"任务取消成功: {task_id}")
                    else:
                        self.logger.warning(f"任务取消失败: {task_id}, HTTP {resp.status}")
        except Exception as e:
            self.logger.warning(f"清理任务异常: {task_id}, 错误: {str(e)}")
        
        await super().cleanup_failed_task(task_id)

    async def _do_health_check(self) -> bool:
        """执行健康检查"""
        try:
            async with self.create_http_session() as session:
                # 测试系统状态端点
                async with session.get(f"{self.base_url}/system/status", ssl=False) as resp:
                    if resp.status == 200:
                        data = await resp.json()
                        self.logger.debug(f"矢量化服务健康检查成功: {data}")
                        return True
                    else:
                        self.logger.warning(f"矢量化服务健康检查失败: HTTP {resp.status}")
                        return False
        except Exception as e:
            self.logger.warning(f"矢量化服务健康检查异常: {e}")
            return False

    @classmethod
    async def test_connection(cls, base_url: Optional[str] = None) -> tuple[bool, str]:
        """测试矢量化服务连接"""
        service = cls(base_url) if base_url else cls()
        
        try:
            is_available, message = await service.test_connection()
            if is_available:
                # 额外测试系统状态
                status = await service.get_system_status()
                return True, f"连接成功: {status}"
            else:
                return False, message
        except Exception as e:
            logger.error(f"矢量化服务连接失败: {e}")
            return False, f"连接失败: {str(e)}"

    # 实现 PollingMixin 的抽象方法
    def _is_task_complete(self, result: Any) -> bool:
        """检查任务是否完成"""
        return result.get('code') == 0
    
    def _is_task_failed(self, result: Any) -> bool:
        """检查任务是否失败"""
        return result.get('code') == -1
    
    def _get_error_message(self, result: Any) -> str:
        """获取错误消息"""
        return result.get('message', '未知错误')


# 为了保持向后兼容性，保留原有的简单接口
async def vectorize_image(image_path: str, 
                         save_eps_path: Optional[str] = None,
                         timeout: int = 1200,
                         progress_callback: Optional[Callable[[str, dict], None]] = None) -> str:
    """
    简单的矢量化接口（向后兼容）
    
    Args:
        image_path: 输入图片路径
        save_eps_path: 输出EPS文件路径
        timeout: 超时时间
        progress_callback: 进度回调函数
        
    Returns:
        str: EPS文件路径
    """
    service = VectorizerService()
    return await service.image_to_eps(
        image_path=image_path,
        save_eps_path=save_eps_path,
        timeout=timeout,
        status_callback=progress_callback
    )