import json
from deepeval.metrics import ContextualPrecisionMetric, ContextualRecallMetric, ContextualRelevancyMetric, AnswerRelevancyMetric, FaithfulnessMetric
from deepeval.test_case import LLMTestCase
from deepeval import evaluate

class EvalGeneration:
    """
    Class for evaluating a prompt's effectiveness in generating high-quality output from an LLM model.
    """

    def __init__(self, llm_model: str, th: float, input: str, actual_output: str, expected_output: str, retrieval_context: str):
        """
        Initializes an instance of the EvalRAG class.

        Args:
            llm_model (str): The language model used for evaluation.
            th (float): The threshold value for evaluation metrics.
            input (str): The input text for the test case.
            actual_output (str): The actual output generated by the model.
            expected_output (str): The expected output for the test case.
            retrieval_context (str): The retrieval context for the test case.
        """
        retrieval_context = retrieval_context.split("; ")
        self.test_case = LLMTestCase(input=input, actual_output=actual_output, expected_output=expected_output, retrieval_context=retrieval_context)
        self.answer_relevancy = AnswerRelevancyMetric(threshold=th, model=llm_model, include_reason=True)
        self.faithfulness = FaithfulnessMetric(threshold=th, model=llm_model, include_reason=True)

    def evaluate(self):
        """
        Evaluates the test case using the specified evaluation metrics.

        Returns:
            str: The evaluation results in JSON format.
        """
        test_results = evaluate(test_cases=[self.test_case],
                                metrics=[
                                    self.answer_relevancy,
                                    self.faithfulness
                                ],
                                print_results=False)
        return test_results

    def measure(self, metric: str):
        """
        Measures a specific evaluation metric for the test case.

        Args:
            metric (str): The metric to measure. Possible values: "answer", "faithfulness".

        Returns:
            str: The measurement result in JSON format.
        """
        match metric:
            case "answer":
                self.answer_relevancy.measure(self.test_case)
                result = {
                    "Result": self.answer_relevancy.success,
                    "Score": self.answer_relevancy.score,
                    "Reason": self.answer_relevancy.reason
                }
                return result

            case "faithfulness":
                self.faithfulness.measure(self.test_case)
                result = {
                    "Result": self.faithfulness.success,
                    "Score": self.faithfulness.score,
                    "Reason": self.faithfulness.reason
                }
                return result
