
import asyncio
import aiohttp
import logging
import time
import yaml
from datetime import datetime
from typing import List, Dict
from .state_manager import ProjectStateManager
from .document_processor import DocumentProcessor

logger = logging.getLogger("lightrag")

def load_config():
    with open('config.yaml', 'r', encoding='utf-8') as f:
        return yaml.safe_load(f)

class SyncOrchestrator:
    """Основной координатор синхронизации проектов"""
    
    def __init__(self, external_api_base: str):
        self.external_api_base = external_api_base
        
        # Инициализируем компоненты
        self.state_manager = ProjectStateManager()
        self.document_processor = DocumentProcessor()
        
        # Состояние синхронизации
        self.running = False
        self.force_sync_running = False
        self.force_sync_start_time = None
        self.force_sync_status = None
        
        # Состояние планировщика перестройки
        self.rebuild_scheduler_running = False
        
        # Ленивая инициализация RAG менеджера
        self.rag_manager = None
        
        # Конфигурация
        self.config = load_config()
        self.retry_delay_seconds = self.config.get('sync', {}).get('retry_delay_seconds')
        
        logger.info(f"🚀 Инициализация SyncOrchestrator: API={external_api_base}")
    
    def _init_rag_manager(self):
        """Ленивая инициализация RAG менеджера"""
        if self.rag_manager is None:
            from src.rag_config import rag_manager
            self.rag_manager = rag_manager
    
    async def start_periodic_sync(self):
        """Запускает синхронизацию каждые N часов"""
        if self.running:
            return
            
        self.running = True
        interval_hours = self.state_manager.periodic_interval_hours
        logger.info(f"🚀 Запуск периодической синхронизации каждые {interval_hours} часов")
        
        asyncio.create_task(self._sync_loop())
    
    async def force_sync_projects(self):
        """Запуск принудительной синхронизации с отслеживанием статуса"""
        if self.force_sync_running:
            return "already_running"
            
        self.force_sync_running = True
        self.force_sync_start_time = datetime.utcnow()
        self.force_sync_status = "running"
        
        try:
            await self.sync_projects()
            self.force_sync_status = "completed"
            return "success"
        except Exception as e:
            error_msg = str(e)
            logger.error(f"❌ Ошибка принудительной синхронизации: {error_msg}")
            self.force_sync_status = "error"
            return "error"
        finally:
            self.force_sync_running = False

    async def _sync_loop(self):
        """Основной цикл синхронизации"""
        interval_hours = self.state_manager.periodic_interval_hours
        
        while self.running:
            try:
                await self.sync_projects()
                logger.info(f"😴 Следующая синхронизация через {interval_hours} часов")
                await asyncio.sleep(interval_hours * 60 * 60)
            except Exception as e:
                logger.error(f"❌ Ошибка синхронизации: {e}")
                await asyncio.sleep(self.retry_delay_seconds)
    
    async def sync_projects(self):
        """Основная функция синхронизации"""
        self._init_rag_manager()
        
        logger.info("🔄 === НАЧАЛО СИНХРОНИЗАЦИИ ===")
        start_time = datetime.utcnow()
        
        try:
            # ГЛОБАЛЬНОЕ время нужно только для запроса СПИСКА обновленных проектов
            last_sync = self.state_manager.get_last_sync_time()
            logger.info(f"📅 Последняя глобальная синхронизация: {last_sync.strftime('%Y-%m-%d %H:%M:%S')}")
            
            # Получаем проекты, обновленные после глобального времени
            projects = await self._get_updated_projects(last_sync)
            
            if not projects:
                logger.info("✅ Новых обновлений нет")
                self.state_manager.save_last_sync_time(start_time)
                return
            
            logger.info(f"📦 Найдено {len(projects)} обновленных проектов")
            
            # Обрабатываем каждый проект
            for project in projects:
                try:
                    await self._process_single_project(project)
                    # ВРЕМЯ СИНХРОНИЗАЦИИ ТЕПЕРЬ СОХРАНЯЕТСЯ ВНУТРИ _process_single_project
                    logger.info(f"✅ Обработан: {project['title']}")
                except Exception as e:
                    logger.error(f"❌ Ошибка в проекте {project['title']}: {e}")
                    continue
            
            # Обновляем ГЛОБАЛЬНОЕ время (для следующего запроса списка проектов)
            self.state_manager.save_last_sync_time(start_time)
            logger.info("🎉 === СИНХРОНИЗАЦИЯ ЗАВЕРШЕНА ===")
            
        except Exception as e:
            logger.error(f"💥 Критическая ошибка: {e}")

    async def _process_single_project(self, project: Dict):
        """
        Обрабатывает один проект с ИНКРЕМЕНТАЛЬНОЙ загрузкой документов
        
        КЛЮЧЕВОЕ ИЗМЕНЕНИЕ: использует новую ручку для получения только изменений
        """
        project_id = project["source_id"]
        start_time = time.time()
        
        # Добавляем проект в расписание перестройки если новый
        is_new_project = self.state_manager.ensure_project_in_rebuild_schedule(project_id)
        if is_new_project:
            logger.info(f"🆕 Проект {project_id} впервые обрабатывается")
        
        try:
            # ============ КЛЮЧЕВОЕ ИЗМЕНЕНИЕ: ИНКРЕМЕНТАЛЬНАЯ ЗАГРУЗКА ============
            
            # Получаем время последней синхронизации ЭТОГО КОНКРЕТНОГО проекта
            last_project_sync = self.state_manager.get_project_sync_time(project_id)
            
            # Пробуем получить ТОЛЬКО изменения с последней синхронизации проекта
            try:
                documents = await self._get_project_content_incremental(
                    project_id, 
                    last_project_sync
                )
                logger.info(f"📥 Инкрементальная загрузка для {project_id}: {len(documents)} изменений с {last_project_sync.strftime('%Y-%m-%d %H:%M:%S')}")
                
            except Exception as e:
                # Fallback: если новая ручка не работает - используем старую
                logger.warning(f"⚠️ Инкрементальная загрузка не удалась для {project_id}, fallback на полную загрузку: {e}")
                documents = await self._get_project_content(project_id)
                logger.info(f"📥 Полная загрузка для {project_id}: {len(documents)} документов")
            
            # =====================================================================
            
            if not documents:
                logger.info(f"✅ Проект {project_id}: нет изменений")
                # ВАЖНО: всё равно обновляем время синхронизации!
                self.state_manager.save_project_sync_time(project_id, datetime.utcnow())
                return
            
            # Получаем RAG проекта (создаём если не существует)
            project_rag = await self.rag_manager.get_rag_for_project(
                project_id, create_if_not_exists=True
            )
            
            # Обрабатываем документы (удаление существующих + вставка всех)
            # ЭТО НЕ МЕНЯЕТСЯ - логика та же, но теперь документов НАМНОГО меньше!
            deleted_count, inserted_count = await self.document_processor.process_project_documents(
                project_rag, documents, project_id
            )
            
            # ============ ВАЖНО: Сохраняем время синхронизации ПРОЕКТА ============
            current_time = datetime.utcnow()
            self.state_manager.save_project_sync_time(project_id, current_time)
            # ====================================================================
            
            # Записываем метрики
            processing_time = time.time() - start_time
            self.document_processor.record_processing_metrics(
                project_id, inserted_count, processing_time, success=True
            )
            
            logger.info(
                f"📊 Проект {project_id}: удалено {deleted_count}, "
                f"вставлено {inserted_count} документов за {processing_time:.2f}s"
            )
            
        except Exception as e:
            processing_time = time.time() - start_time
            self.document_processor.record_processing_metrics(
                project_id, 0, processing_time, success=False
            )
            
            logger.error(f"❌ Ошибка при обработке проекта {project_id}: {e}")
            raise

    # ========== API МЕТОДЫ ==========

    async def _get_updated_projects(self, since_time: datetime) -> List[Dict]:
        """
        Получает обновленные проекты (СТАРАЯ РУЧКА - не меняется)
        
        GET /projects/updated/{timestamp}
        """
        timestamp_str = since_time.replace(tzinfo=None).isoformat() + "Z"
        
        url = f"{self.external_api_base}/projects/updated/{timestamp_str}"
        
        logger.info(f"🔍 Запрос обновленных проектов с {timestamp_str} по URL: {url}")
        
        async with aiohttp.ClientSession() as session:
            async with session.get(
                url, 
                ssl=False, 
                timeout=aiohttp.ClientTimeout(total=30)
            ) as response:
                if response.status != 200:
                    error_text = await response.text()
                    logger.error(f"API ошибка: {response.status} - {error_text[:200]}")
                    raise Exception(f"API ошибка: {response.status}")
                
                data = await response.json()
                projects = data.get("projects", [])
                
                for project in projects:
                    logger.info(f"📦 Получен проект: ID={project.get('source_id')}, Title={project.get('title')}")
                
                return projects
    
    async def _get_project_content(self, source_id: str) -> List[Dict]:
        """
        Получает ВСЕ документы проекта (СТАРАЯ РУЧКА - для fallback)
        
        GET /project/{source_id}/content
        """
        url = f"{self.external_api_base}/project/{source_id}/content"
        
        logger.info(f"🔍 Запрос ВСЕХ документов для проекта {source_id} по URL: {url}")
        
        async with aiohttp.ClientSession() as session:
            async with session.get(
                url, 
                ssl=False, 
                timeout=aiohttp.ClientTimeout(total=30)
            ) as response:
                if response.status != 200:
                    error_text = await response.text()
                    logger.error(f"API ошибка при получении контента проекта {source_id}: {response.status} - {error_text[:200]}")
                    raise Exception(f"API ошибка: {response.status}")
                
                data = await response.json()
                contents = data.get("contents", [])
                logger.info(f"📦 Получено {len(contents)} документов для проекта {source_id}")
                return contents
    
    async def _get_project_content_incremental(self, source_id: str, since_time: datetime) -> List[Dict]:
        """
        Получает ТОЛЬКО измененные документы проекта с определенного времени (НОВАЯ РУЧКА!)
        
        GET /project/{source_id}/content/{timestamp}
        
        Args:
            source_id: ID проекта
            since_time: время, с которого получать изменения
            
        Returns:
            List[Dict]: список измененных документов
        """
        timestamp_str = since_time.replace(tzinfo=None).isoformat() + "Z"
        url = f"{self.external_api_base}/project/{source_id}/content/{timestamp_str}"
        
        logger.info(f"🔍 ИНКРЕМЕНТАЛЬНЫЙ запрос для проекта {source_id} с {timestamp_str} по URL: {url}")
        
        async with aiohttp.ClientSession() as session:
            async with session.get(
                url,
                ssl=False,
                timeout=aiohttp.ClientTimeout(total=30)
            ) as response:
                if response.status != 200:
                    error_text = await response.text()
                    logger.error(f"API ошибка при инкрементальной загрузке {source_id}: {response.status} - {error_text[:200]}")
                    raise Exception(f"API ошибка: {response.status}")
                
                data = await response.json()
                contents = data.get("contents", [])
                
                logger.info(f"✅ Инкрементальная загрузка {source_id}: получено {len(contents)} измененных документов")
                return contents
    
    def stop(self):
        """Останавливает синхронизацию"""
        self.running = False
    
    # === УПРАВЛЕНИЕ ПЛАНИРОВЩИКОМ ПЕРЕСТРОЙКИ ===
    
    async def start_rebuild_scheduler(self):
        """Запускает планировщик полной перестройки проектов"""
        if self.rebuild_scheduler_running:
            return
            
        self.rebuild_scheduler_running = True
        interval_hours = self.state_manager.rebuild_check_interval_hours
        logger.info(f"🔄 Запуск планировщика полной перестройки проектов (интервал проверки: {interval_hours} ч)")
        
        asyncio.create_task(self._rebuild_scheduler_loop())

    async def _rebuild_scheduler_loop(self):
        """Основной цикл планировщика перестройки - проверяет каждые N часов из конфига"""
        interval_hours = self.state_manager.rebuild_check_interval_hours
        rebuild_check_interval_seconds = interval_hours * 3600
        
        while self.rebuild_scheduler_running:
            try:
                await self._check_and_rebuild_projects()
                
                logger.info(f"😴 Планировщик перестройки: следующая проверка через {interval_hours} ч")
                await asyncio.sleep(rebuild_check_interval_seconds)
                
            except Exception as e:
                logger.error(f"❌ Ошибка в планировщике перестройки: {e}")
                await asyncio.sleep(600)

    async def _check_and_rebuild_projects(self):
        """Проверяет проекты и выполняет перестройку тех, кому пора"""
        try:
            projects_to_rebuild = self.state_manager.get_projects_needing_rebuild()
            
            if projects_to_rebuild:
                logger.info(f"🚀 Начинаем полную перестройку {len(projects_to_rebuild)} проектов")
                
                for project_id in projects_to_rebuild:
                    await self._rebuild_single_project(project_id)
            else:
                logger.info("✅ Все проекты актуальны, перестройка не требуется")
                
        except Exception as e:
            logger.error(f"❌ Ошибка при проверке проектов для перестройки: {e}")

    async def _rebuild_single_project(self, project_id: str):
        """Выполняет полную перестройку одного проекта"""
        try:
            logger.info(f"🔥 Начинаем полную перестройку проекта {project_id}")
            start_time = time.time()
            
            # Инициализируем зависимости если нужно
            self._init_rag_manager()
            
            # 1. Получаем актуальные данные проекта из API
            documents = await self._get_project_content(project_id)
            
            if not documents:
                logger.warning(f"⚠️ Проект {project_id} не содержит документов - пропускаем перестройку")
                return
            
            # 2. ПРОВЕРЯЕМ существование БД БЕЗ создания RAG instance
            from src.project_rag import ProjectRAG
            import os
            
            temp_project_rag = ProjectRAG(
                project_id=project_id,
                db_host=os.getenv('POSTGRES_HOST'),
                db_port=int(os.getenv('POSTGRES_PORT')),
                db_user=os.getenv('POSTGRES_USER'),
                db_password=os.getenv('POSTGRES_PASSWORD'),
                llm_func=None,  # Не нужно для операций с БД
                embedding_func=None
            )
            
            # 3. Если БД существует - удаляем её БЕЗ активации RAG
            db_exists = await temp_project_rag.check_database_exists()
            if db_exists:
                logger.info(f"🗄️ Удаляем существующую БД для {project_id}")
                await temp_project_rag.drop_database()
                temp_project_rag.cleanup_working_directory()
                logger.info(f"✅ БД и рабочая директория для {project_id} удалены")
                
                # Пауза после удаления
                await asyncio.sleep(2)
            
            # 4. ПРИНУДИТЕЛЬНО очищаем кэш менеджера
            self.rag_manager.project_instances.pop(project_id, None)
            
            # 5. Создаем новый RAG с принудительным созданием БД
            logger.info(f"🔄 Создаем новый RAG экземпляр для {project_id}")
            project_rag = await self.rag_manager.get_rag_for_project(
                project_id, create_if_not_exists=True
            )
            
            # 6. Загружаем ВСЕ документы заново
            deleted_count, inserted_count = await self.document_processor.process_project_documents(
                project_rag, documents, project_id
            )
            
            # 7. Обновляем время последней полной перестройки
            current_time = datetime.utcnow()
            self.state_manager.update_project_rebuild_time(project_id, current_time)
            
            processing_time = time.time() - start_time
            
            logger.info(
                f"🎉 Полная перестройка проекта {project_id} завершена: "
                f"загружено {inserted_count} документов за {processing_time:.2f}s"
            )
            
        except Exception as e:
            logger.error(f"❌ Критическая ошибка при полной перестройке проекта {project_id}: {e}")
            return

    def stop_rebuild_scheduler(self):
        """Останавливает планировщик перестройки"""
        if hasattr(self, 'rebuild_scheduler_running'):
            self.rebuild_scheduler_running = False
            logger.info("🛑 Планировщик перестройки остановлен")
    
    # === ВСПОМОГАТЕЛЬНЫЕ МЕТОДЫ ДЛЯ API ===
    
    def get_sync_status_info(self) -> Dict:
        """Возвращает информацию о статусе синхронизации для API"""
        force_sync_info = {}
        if self.force_sync_running:
            elapsed_time = datetime.utcnow() - self.force_sync_start_time
            force_sync_info = {
                "running": True,
                "start_time": self.force_sync_start_time.isoformat(),
                "elapsed_seconds": int(elapsed_time.total_seconds())
            }
        elif self.force_sync_status:
            force_sync_info = {
                "running": False,
                "status": self.force_sync_status,
                "last_run": self.force_sync_start_time.isoformat() if self.force_sync_start_time else None
            }
        
        return {
            "periodic_sync": {
                "running": self.running,
                "sync_interval": f"Каждые {self.state_manager.periodic_interval_hours} часов",
            },
            "force_sync": force_sync_info,
            "full_rebuild": {
                "enabled": True,
                "scheduler_running": self.rebuild_scheduler_running,
                "interval_days": self.state_manager.full_rebuild_interval_days,
                "check_interval": f"Каждые {self.state_manager.rebuild_check_interval_hours} ч",
                "projects": self.state_manager.get_rebuild_status_info()
            },
            "external_api": self.external_api_base
        }
