import logging
import time
from typing import Dict, Any, List
from abc import ABC, abstractmethod
from models.ollama_client import OllamaClient

class Benchmark(ABC):
    """Базовый класс для всех бенчмарков."""

    def __init__(self, name: str):
        """
        Инициализация бенчмарка.

        Args:
            name: Название бенчмарка
        """
        self.name = name
        self.logger = logging.getLogger(__name__)

    @abstractmethod
    def load_test_data(self) -> List[Dict[str, Any]]:
        """
        Загрузка тестовых данных.

        Returns:
            Список тестовых случаев
        """
        pass

    @abstractmethod
    def evaluate(self, model_response: str, expected: str) -> float:
        """
        Оценка качества ответа модели.

        Args:
            model_response: Ответ от модели
            expected: Ожидаемый ответ

        Returns:
            Метрика качества (0-1)
        """
        pass

    def run(self, ollama_client: OllamaClient, model_name: str, context_size: int = 32000) -> Dict[str, Any]:
        """
        Запуск бенчмарка.

        Args:
            ollama_client: Клиент для работы с Ollama
            model_name: Название модели
            context_size: Размер контекста для модели

        Returns:
            Результаты бенчмарка
        """
        from utils.scoring import get_all_scores

        test_cases = self.load_test_data()
        results = []

        for i, test_case in enumerate(test_cases, 1):
            try:
                self.logger.info(f"Running test case {i}/{len(test_cases)} for {self.name}")
                self.logger.info(f"Test name: {test_case['name']}")

                # Замер времени
                start_time = time.time()

                # Получение ответа от модели
                prompt = test_case['prompt']
                self.logger.debug(f"Prompt: {prompt[:200]}...")  # Логируем начало промпта
                # Подготовка опций для вызова
                options = {'temperature': 0.7}
                if context_size is not None:
                    # Для Ollama параметры контекста передаются в options
                    options['num_ctx'] = context_size
                    self.logger.debug(f"Setting context size to {context_size}")

                self.logger.debug(f"About to call generate with model={model_name}, prompt length={len(prompt)}, options={options}")
                model_response = ollama_client.generate(
                    model=model_name,
                    prompt=prompt,
                    options=options
                )
                self.logger.debug(f"Generate call completed, response length={len(model_response) if model_response else 0}")

                # Замер времени
                latency = time.time() - start_time

                # Оценка качества
                score = self.evaluate(model_response, test_case['expected'])

                # Вычисление всех дополнительных метрик
                scores = get_all_scores(model_response, test_case['expected'])

                results.append({
                    'test_case': test_case['name'],
                    'prompt': prompt,
                    'expected': test_case['expected'],
                    'model_response': model_response,
                    'score': score,
                    'latency': latency,
                    'f1_score': scores['f1_score'],
                    'normalized_levenshtein': scores['normalized_levenshtein'],
                    'bleu_score': scores['bleu_score'],
                    'rouge_scores': scores['rouge_scores'],
                    'code_similarity': scores['code_similarity']
                })

            except Exception as e:
                error_msg = f"Error in test case {i} (name: {test_case['name']}): {e}"
                self.logger.error(error_msg)
                if 'prompt' in locals():
                    self.logger.debug(f"Failed prompt: {prompt[:500]}")
                results.append({
                    'test_case': test_case['name'],
                    'error': str(e)
                })

        return {
            'benchmark_name': self.name,
            'total_tests': len(test_cases),
            'successful_tests': len([r for r in results if 'score' in r]),
            'results': results
        }
