"""
增量索引器服务
监听文件变化事件，自动触发增量索引构建
"""
import os
import time
from pathlib import Path
from typing import Optional, List, Dict, Any
from threading import Thread, Lock
from queue import Queue, Empty
from loguru import logger

from llama_index.core import Document
from llama_index.core import SimpleDirectoryReader

from src.config import get_config_manager, ConfigManager
from .event_bus import EventBus, EventType
from .events import FileWatcherEvent, IndexBuildEvent


class IncrementalIndexer:
    """增量索引器"""
    
    _instance: Optional["IncrementalIndexer"] = None
    
    def __new__(cls):
        """实现单例模式"""
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._initialized = False
        return cls._instance
        
    def __init__(self):
        """初始化增量索引器"""
        if self._initialized:
            return
            
        self.config = get_config_manager()
        self.event_bus = EventBus.get_instance()
        self.rag_service = None  # 延迟初始化
        self.file_queue = Queue()
        self.processing_thread: Optional[Thread] = None
        self.is_running = False
        self.lock = Lock()
        self.batch_size = 5  # 批量处理文件数量
        self.batch_timeout = 10.0  # 批量处理超时时间（秒）
        self.last_batch_time = 0
        
        # 订阅文件变化事件
        self._subscribe_events()
        
        self._initialized = True
        logger.debug("增量索引器已初始化")
        
    @classmethod
    def get_instance(cls) -> "IncrementalIndexer":
        """获取单例实例"""
        return cls()
        
    def _subscribe_events(self):
        """订阅相关事件"""
        # 订阅文件创建和修改事件
        self.event_bus.subscribe(EventType.FILE_CREATED, self._handle_file_event)
        self.event_bus.subscribe(EventType.FILE_MODIFIED, self._handle_file_event)
        self.event_bus.subscribe(EventType.FILE_MOVED, self._handle_file_event)
        
        logger.debug("增量索引器已订阅文件变化事件")
        
    def _handle_file_event(self, event):
        """
        处理文件变化事件
        
        Args:
            event: 事件对象
        """
        try:
            # 检查事件类型
            if event.event_type not in [EventType.FILE_CREATED, EventType.FILE_MODIFIED, EventType.FILE_MOVED]:
                return
                
            event_data = event.data
            event_type = event_data.get("event_type", "")
            file_path = event_data.get("file_path", "")
            
            if not file_path:
                return
                
            # 只处理创建、修改和移动事件
            if event_type in ["created", "modified", "moved"]:
                logger.info(f"收到文件{event_type}事件: {file_path}")
                
                # 将文件添加到处理队列
                self.file_queue.put({
                    "file_path": file_path,
                    "event_type": event_type,
                    "timestamp": time.time()
                })
                
                # 确保处理线程在运行
                self._ensure_processing_thread()
                
        except Exception as e:
            logger.error(f"处理文件事件失败: {e}")
            
    def _ensure_processing_thread(self):
        """确保处理线程在运行"""
        with self.lock:
            if not self.is_running or not self.processing_thread or not self.processing_thread.is_alive():
                self.start()
                
    def start(self) -> bool:
        """
        启动增量索引器
        
        Returns:
            bool: 启动是否成功
        """
        if self.is_running:
            logger.warning("增量索引器已经在运行")
            return True
            
        try:
            self.is_running = True
            self.processing_thread = Thread(target=self._process_files, daemon=True)
            self.processing_thread.start()
            
            logger.info("增量索引器已启动")
            return True
            
        except Exception as e:
            logger.error(f"启动增量索引器失败: {e}")
            self.is_running = False
            return False
            
    def stop(self) -> bool:
        """
        停止增量索引器
        
        Returns:
            bool: 停止是否成功
        """
        if not self.is_running:
            logger.warning("增量索引器未运行")
            return True
            
        try:
            self.is_running = False
            if self.processing_thread and self.processing_thread.is_alive():
                self.processing_thread.join(timeout=5)
                
            logger.info("增量索引器已停止")
            return True
            
        except Exception as e:
            logger.error(f"停止增量索引器失败: {e}")
            return False
            
    def _process_files(self):
        """处理文件队列中的文件"""
        logger.info("增量索引处理线程已启动")
        
        current_batch: List[Dict[str, Any]] = []
        
        while self.is_running:
            try:
                # 从队列中获取文件（带超时）
                try:
                    file_item = self.file_queue.get(timeout=1.0)
                    current_batch.append(file_item)
                except Empty:
                    file_item = None
                    
                current_time = time.time()
                
                # 检查是否应该处理当前批次
                should_process_batch = (
                    len(current_batch) >= self.batch_size or
                    (current_batch and current_time - self.last_batch_time > self.batch_timeout)
                )
                
                if should_process_batch and current_batch:
                    self._process_batch(current_batch)
                    current_batch = []
                    self.last_batch_time = current_time
                    
            except Exception as e:
                logger.error(f"处理文件队列时发生错误: {e}")
                time.sleep(1)  # 避免快速循环
                
        logger.info("增量索引处理线程已停止")
        
    def _get_rag_service(self):
        """获取RAGService实例（延迟初始化）"""
        if self.rag_service is None:
            # 延迟导入以避免循环导入
            from src.services import RAGService
            self.rag_service = RAGService()
        return self.rag_service
    
    def _process_batch(self, batch: List[Dict[str, Any]]):
        """
        处理一批文件
        
        Args:
            batch: 文件批次
        """
        if not batch:
            return
            
        logger.info(f"开始处理批次: {len(batch)} 个文件")
        
        try:
            # 发布索引构建开始事件
            self.event_bus.publish(
                IndexBuildEvent.build_started(
                    nodes_count=len(batch),
                    source="IncrementalIndexer"
                )
            )
            
            # 加载文件为文档
            documents = self._load_documents_from_batch(batch)
            
            if not documents:
                logger.warning("批次中没有可处理的文档")
                return
                
            # 获取RAGService实例并插入文档到索引
            rag_service = self._get_rag_service()
            result = rag_service.insert_documents([
                {
                    "text": doc.text,
                    "metadata": doc.metadata
                }
                for doc in documents
            ])
            
            if result.get("success"):
                logger.info(f"✅ 成功插入 {len(documents)} 个文档到索引")
                
                # 发布索引构建完成事件
                self.event_bus.publish(
                    IndexBuildEvent.build_completed(
                        index_type="incremental",
                        nodes_count=len(documents),
                        duration=0.0,  # 实际时间可以在实现中计算
                        source="IncrementalIndexer"
                    )
                )
            else:
                logger.error(f"❌ 插入文档失败: {result.get('error')}")
                
                # 发布索引构建失败事件
                self.event_bus.publish(
                    IndexBuildEvent.build_failed(
                        index_type="incremental",
                        error=result.get("error", "未知错误"),
                        source="IncrementalIndexer"
                    )
                )
                
        except Exception as e:
            logger.error(f"处理批次失败: {e}")
            
            # 发布索引构建失败事件
            self.event_bus.publish(
                IndexBuildEvent.build_failed(
                    index_type="incremental",
                    error=str(e),
                    source="IncrementalIndexer"
                )
            )
            
    def _load_documents_from_batch(self, batch: List[Dict[str, Any]]) -> List[Document]:
        """
        从批次中加载文档
        
        Args:
            batch: 文件批次
            
        Returns:
            List[Document]: 文档列表
        """
        documents = []
        
        for file_item in batch:
            file_path = file_item.get("file_path", "")
            event_type = file_item.get("event_type", "")
            
            if not file_path or not os.path.exists(file_path):
                logger.warning(f"文件不存在: {file_path}")
                continue
                
            try:
                # 使用 SimpleDirectoryReader 加载单个文件
                # 注意：SimpleDirectoryReader 通常用于目录，但我们可以创建一个临时目录
                temp_dir = Path(file_path).parent
                file_name = Path(file_path).name
                
                # 检查文件扩展名
                supported_exts = {".txt", ".md", ".pdf", ".docx"}
                if Path(file_path).suffix.lower() not in supported_exts:
                    logger.debug(f"跳过不支持的文件类型: {file_path}")
                    continue
                    
                # 加载文档
                reader = SimpleDirectoryReader(
                    input_dir=str(temp_dir),
                    recursive=False,
                    required_exts=[Path(file_path).suffix.lower()],
                    filename_as_id=True
                )
                
                file_docs = reader.load_data()
                
                if file_docs:
                    # 设置文档元数据
                    for doc in file_docs:
                        doc.metadata.update({
                            "file_path": file_path,
                            "file_name": file_name,
                            "event_type": event_type,
                            "processed_at": time.time()
                        })
                    documents.extend(file_docs)
                    logger.info(f"成功加载文件: {file_name}")
                else:
                    logger.warning(f"无法加载文件内容: {file_path}")
                    
            except Exception as e:
                logger.error(f"加载文件失败 {file_path}: {e}")
                
        return documents
        
    def get_status(self) -> Dict[str, Any]:
        """
        获取增量索引器状态
        
        Returns:
            dict: 状态信息
        """
        return {
            "is_running": self.is_running,
            "queue_size": self.file_queue.qsize(),
            "batch_size": self.batch_size,
            "batch_timeout": self.batch_timeout,
            "last_batch_time": self.last_batch_time,
            "processing_thread_alive": self.processing_thread.is_alive() if self.processing_thread else False
        }
        
    def trigger_manual_indexing(self, file_paths: List[str]) -> Dict[str, Any]:
        """
        手动触发增量索引
        
        Args:
            file_paths: 文件路径列表
            
        Returns:
            dict: 操作结果
        """
        try:
            logger.info(f"手动触发增量索引: {len(file_paths)} 个文件")
            
            # 创建批次
            batch = []
            for file_path in file_paths:
                if os.path.exists(file_path):
                    batch.append({
                        "file_path": file_path,
                        "event_type": "manual",
                        "timestamp": time.time()
                    })
                    
            if not batch:
                return {
                    "success": False,
                    "error": "没有有效的文件路径",
                    "processed_count": 0
                }
                
            # 处理批次
            self._process_batch(batch)
            
            return {
                "success": True,
                "processed_count": len(batch),
                "message": f"已处理 {len(batch)} 个文件"
            }
            
        except Exception as e:
            logger.error(f"手动触发增量索引失败: {e}")
            return {
                "success": False,
                "error": str(e),
                "processed_count": 0
            }


# 便捷函数
def get_incremental_indexer() -> IncrementalIndexer:
    """获取增量索引器实例"""
    return IncrementalIndexer.get_instance()


def start_incremental_indexer() -> bool:
    """启动增量索引器"""
    return get_incremental_indexer().start()


def stop_incremental_indexer() -> bool:
    """停止增量索引器"""
    return get_incremental_indexer().stop()


def trigger_manual_indexing(file_paths: List[str]) -> Dict[str, Any]:
    """手动触发增量索引"""
    return get_incremental_indexer().trigger_manual_indexing(file_paths)