import logging
from typing import Annotated, Optional, List
import time

from fastapi import FastAPI, HTTPException, Response
from pydantic import BaseModel, Field

from src.metrics import (
    compute_metrics
)
from src.ragas_test import comprehensive_evaluation
from prometheus_client import Counter, Histogram, Summary, generate_latest, CONTENT_TYPE_LATEST

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

def setup_logging(level: int = logging.INFO) -> logging.Logger:
    """Настраивает базовые параметры логирования для приложения."""
    logging.basicConfig(
        level=level,
        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())

    return logger


logger = setup_logging()
app = FastAPI()

MIN_ALLOWED_VALUE = 0.0
MAX_ALLOWED_VALUE = 1.0
HTTP_ERROR_CODE = 500

REQUEST_COUNT = Counter('eval_api_requests_total', 'Общее количество запросов', ['endpoint', 'status'])
REQUEST_LATENCY = Histogram('eval_api_request_duration_seconds', 'Время выполнения запросов', ['endpoint'])
QUALITY_SCORES = Summary('eval_api_quality_scores', 'Значения метрик качества', ['metric_name'])

class MetricsRequest(BaseModel):
    """Модель запроса для расчёта метрик."""

    reference: str
    hypothesis: str
    user_input: Optional[str] = None
    context: Optional[List[str]] = None


class NERMetrics(BaseModel):
    """Модель для метрик распознавания именованных сущностей."""

    f1: Annotated[float, Field(ge=MIN_ALLOWED_VALUE, le=MAX_ALLOWED_VALUE)]
    precision: Annotated[float, Field(ge=MIN_ALLOWED_VALUE, le=MAX_ALLOWED_VALUE)]
    recall: Annotated[float, Field(ge=MIN_ALLOWED_VALUE, le=MAX_ALLOWED_VALUE)]
    iou: Annotated[float, Field(ge=MIN_ALLOWED_VALUE, le=MAX_ALLOWED_VALUE)]


class RougeMetrics(BaseModel):
    """Модель для метрик ROUGE."""

    rouge_l: Annotated[float, Field(ge=MIN_ALLOWED_VALUE, le=MAX_ALLOWED_VALUE)]
    rouge_2: Annotated[float, Field(ge=MIN_ALLOWED_VALUE, le=MAX_ALLOWED_VALUE)]

class RagasMetrics(BaseModel):
    """Модель для метрик Ragas."""

    response_relevancy: Annotated[float, Field(ge=MIN_ALLOWED_VALUE, le=MAX_ALLOWED_VALUE)]
    context_utilization: Annotated[float, Field(ge=MIN_ALLOWED_VALUE, le=MAX_ALLOWED_VALUE)]
    answer_accuracy: Annotated[float, Field(ge=MIN_ALLOWED_VALUE, le=MAX_ALLOWED_VALUE)]
    non_llm_string_similarity: Annotated[float, Field(ge=MIN_ALLOWED_VALUE, le=MAX_ALLOWED_VALUE)]


class MetricsResponse(BaseModel):
    """Модель ответа с метриками."""

    ner: NERMetrics
    rouge: RougeMetrics
    ragas: RagasMetrics
    bert_score: Annotated[float, Field(ge=MIN_ALLOWED_VALUE, le=MAX_ALLOWED_VALUE)]
    meteor: Annotated[float, Field(ge=MIN_ALLOWED_VALUE, le=MAX_ALLOWED_VALUE)]


@app.get('/metrics')
async def metrics():
    """Эндпоинт для экспорта метрик Prometheus."""
    return Response(content=generate_latest(), media_type=CONTENT_TYPE_LATEST)

@app.post('/compute_metrics', response_model=MetricsResponse)
async def compute_metrics_endpoint(request: MetricsRequest) -> MetricsResponse:
    """Эндпоинт для вычисления метрик качества текста."""
    start_time = time.time()
    logger.info('Получен запрос')
    try:
        metrics_result = compute_metrics(request.reference, request.hypothesis)
        data_sample = [{
            "user_input": request.user_input,
            "retrieved_contexts": request.context,
            "response": request.hypothesis,
            "reference": request.reference
        }]
        ragas_result = comprehensive_evaluation(data_sample)

        REQUEST_COUNT.labels(endpoint='/compute_metrics', status='success').inc()
        
        REQUEST_LATENCY.labels(endpoint='/compute_metrics').observe(time.time() - start_time)
        return MetricsResponse(
            ragas=ragas_result,
            ner=metrics_result['ner'],
            rouge=metrics_result['rouge'],
            bert_score=metrics_result['bert_score'],
            meteor=metrics_result['meteor'],
        )
    except Exception as error:
        error_msg = f'Ошибка: {str(error)}'
        logger.error(error_msg)
        REQUEST_COUNT.labels(endpoint='/compute_metrics', status='error').inc()
        raise HTTPException(status_code=HTTP_ERROR_CODE, detail=error_msg)
