import logging
from typing import Optional
from fastapi import FastAPI, HTTPException, Response
from prometheus_client import Counter, Histogram, generate_latest
import time
from pydantic import BaseModel
import yaml

from src.database import connect_to_milvus, create_collection_if_not_exists, disconnect_from_milvus
from src.text_processing import split_text_to_chunks, get_embeddings_from_api, load_env


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


config = load_config()
text_processing_params = config.get('text_processing')

class MetricsLogFilter(logging.Filter):
    """Фильтр для исключения логов метрик из access логов"""
    
    def filter(self, record):
        if hasattr(record, 'args') and record.args and len(record.args) >= 3:
            request_path = record.args[2]
            if '/metrics' in request_path:
                return False
        return True

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S',
)
logger = logging.getLogger(__name__)

uvicorn_access_logger = logging.getLogger("uvicorn.access")
uvicorn_access_logger.addFilter(MetricsLogFilter())

app = FastAPI(title="Document Parser API", version="1.0.0")

PROCESSING_TIME = Histogram(
    "confluence_document_processing_seconds",
    "Время обработки документа в секундах",
    buckets=(0.5, 1.0, 2.5, 5.0, 10.0, 30.0, 60.0)
)
PROCESSING_COUNT = Counter(
    "confluence_document_processing_count",
    "Количество обработанных страниц",
    ["project_id", "status"]
)

class DocumentRequest(BaseModel):
    title: str
    type: str  # file or page
    link: str
    id_external: str
    project_id: str
    content: str 


class DocumentResponse(BaseModel):
    success: bool
    message: str
    chunks_created: Optional[int] = None

@app.get("/metrics")
def metrics():
    return Response(generate_latest(), media_type="text/plain")

@app.post("/process_text", response_model=DocumentResponse)
async def parse_document(request: DocumentRequest) -> DocumentResponse:
    """
    Обрабатывает одну страницу документа и сохраняет в Milvus с логикой upsert.
    1. Если коллекции нет - создает и добавляет данные
    2. Если коллекция есть, но нет записей с данным id_external - добавляет
    3. Если коллекция есть и есть записи с данным id_external - удаляет старые и добавляет новые
    """
    start_time = time.time()
    processing_success = False

    try:
        logger.info(f"Начинаем обработку документа: {request.title} (ID: {request.id_external})")
        
        load_env()

        # 1. Уже в Markdown
        markdown_content = request.content
        
        if not markdown_content.strip():
            raise HTTPException(status_code=400, detail="Пустой контент")
        
        # 2. Разбиваем на чанки
        text_chunks = split_text_to_chunks(markdown_content, 
                                           chunk_size=text_processing_params.get('chunk_size'), 
                                           chunk_overlap=text_processing_params.get('chunk_overlap'))
        
        if not text_chunks:
            raise HTTPException(status_code=400, detail="Не удалось создать чанки из контента")
        
        # 3. Получаем эмбеддинги
        embeddings = get_embeddings_from_api(text_chunks, 
                                             vector_dim=text_processing_params.get('vector_dim'),
                                             batch_size=text_processing_params.get('batch_size', 20))  # Используем из config.yaml)
        
        if len(embeddings) != len(text_chunks):
            raise HTTPException(status_code=500, detail="Количество эмбеддингов не соответствует количеству чанков")
        
        # 4. Подготавливаем коллекцию
        if not connect_to_milvus():
            raise HTTPException(status_code=500, detail="Не удалось подключиться к Milvus")

        collection = create_collection_if_not_exists(request.project_id, 
                                                     vector_dim=text_processing_params.get('vector_dim'))
        
        # 5. Проверяем существующие записи с данным page_id (id_external)
        try:
            collection.load()
            logger.debug('Коллекция успешно загружена в память')
            
            expr = f"page_id == '{request.id_external}'"
            existing_results = collection.query(expr=expr, output_fields=["page_id"], limit=1)
            
            if existing_results:
                logger.info(f"Найдены существующие записи для документа {request.id_external}, удаляем их")
                delete_result = collection.delete(expr)
                collection.flush()
                logger.info(f"Удалены существующие записи для документа {request.id_external}")
            else:
                logger.info(f"Существующих записей для документа {request.id_external} не найдено")
                
        except Exception as e:
            logger.warning(f"Ошибка при проверке существующих записей: {str(e)}")
        
        # 6. Подготавливаем данные для вставки
        chunks_data = []
        for i, (chunk_text, embedding) in enumerate(zip(text_chunks, embeddings)):
            chunk_data = {
                "page_id": request.id_external,
                "link": request.link,
                "page_title": request.title,
                "content": chunk_text,
                "embedding": embedding,
                "chunk_index": i
            }
            chunks_data.append(chunk_data)
        
        # 7. Вставляем новые данные в Milvus
        insert_result = collection.insert(chunks_data)
        collection.flush()
        
        logger.info(f"Документ '{request.title}' успешно обработан и сохранен, создано {len(text_chunks)} чанков")
        collection.release()

        processing_success = True

        return DocumentResponse(
            success=True,
            message=f"Документ успешно обработан и сохранен",
            chunks_created=len(text_chunks)
        )
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Ошибка обработки документа {request.title}: {str(e)}")
        raise HTTPException(status_code=500, detail=f"Ошибка обработки документа: {str(e)}")
    finally:
        PROCESSING_TIME.observe(time.time() - start_time)
        status = "success" if processing_success else "error"
        PROCESSING_COUNT.labels(project_id=request.project_id, status=status).inc()
        
        disconnect_from_milvus()
