from abc import ABC,abstractmethod
import time
from typing import List, Union

import numpy as np


class Concept:
    # 初始化类，并存储传入的参数
    def __init__(self, concept_name, concept_description, concept_question, possible_responses, response_guide, response_mapping):
        self.concept_name = concept_name
        self.concept_description = concept_description
        self.concept_question = concept_question
        self.possible_responses = possible_responses
        self.response_guide = response_guide
        self.response_mapping = response_mapping
        
        # 保留字典 key 到类属性的映射
        self._key_to_attr_map = {
            "Concept Name": "concept_name",
            "Concept Description": "concept_description",
            "Concept Question": "concept_question",
            "Possible Responses": "possible_responses",
            "Response Guide": "response_guide",
            "Response Mapping": "response_mapping"
        }

    # 从字典创建类实例
    @classmethod
    def from_dict(cls, data):
        # 使用映射来动态获取字典中的值
        concept_name = data.get("Concept Name", "")
        concept_description = data.get("Concept Description", "")
        concept_question = data.get("Concept Question", "")
        possible_responses = data.get("Possible Responses", [])
        response_guide = data.get("Response Guide", {})
        response_mapping = data.get("Response Mapping", {})
        
        return cls(concept_name, concept_description, concept_question, possible_responses, response_guide, response_mapping)

    def to_dict(self):
        return {
            "Concept Name": self.concept_name,
            "Concept Description": self.concept_description,
            "Concept Question": self.concept_question,
            "Possible Responses": self.possible_responses,
            "Response Guide": self.response_guide,
            "Response Mapping": self.response_mapping
        }
    
    # 定义 __str__ 方法，用于输出类的字符串表示
    def __str__(self):
        response_guide_str = "\n".join([f"  {response}: {guide}" for response, guide in self.response_guide.items()])
        response_mapping_str = "\n".join([f"  {response}: {mapping}" for response, mapping in self.response_mapping.items()])

        return (f"Concept Name: {self.concept_name}\n"
                f"Concept Description: {self.concept_description}\n"
                f"Concept Question: {self.concept_question}\n"
                f"Possible Responses: {', '.join(self.possible_responses)}\n"
                f"Response Guide:\n{response_guide_str}\n"
                f"Response Mapping:\n{response_mapping_str}")

    # 通过字典的 key 获取类属性
    def __getitem__(self, key):
        attr_name = self._key_to_attr_map.get(key)
        if attr_name:
            return getattr(self, attr_name)
        raise KeyError(f"{key} not found in key-to-attribute mapping.")
    
    # 通过字典的 key 设置类属性
    def __setitem__(self, key, value):
        attr_name = self._key_to_attr_map.get(key)
        if attr_name:
            setattr(self, attr_name, value)
        else:
            raise KeyError(f"{key} not found in key-to-attribute mapping.")

if __name__ == "__main__":
    # 示例字典
    data = {
        "Concept Name": "Uninspiring plot",
        "Concept Description": "The concept 'Uninspiring plot' refers to texts that have a storyline or narrative structure that lacks excitement or fails to capture the reader's interest.",
        "Concept Question": "How would you rate the plot of the text?",
        "Possible Responses": ["uninspiring", "average", "engaging", "uncertain"],
        "Response Guide": {
            "uninspiring": "The plot of the text is dull, uninteresting, or lacks excitement.",
            "average": "The plot of the text is neither exceptionally dull nor particularly engaging.",
            "engaging": "The plot of the text is exciting, captivating, or engrossing.",
            "uncertain": "It is difficult to determine the quality or impact of the plot in the text."
        },
        "Response Mapping": {
            "uninspiring": -1,
            "average": 0,
            "engaging": 1,
            "uncertain": 0
        }
    }

    # 使用 from_dict 创建类的实例
    concept_instance = Concept.from_dict(data)

    # 打印对象，调用 __str__ 方法
    print(concept_instance)




class AbstractConceptLLM(ABC):
    
    def __init__(self, concept: Concept):
        pass

    @abstractmethod
    def inverse_transform(self, concepts: Union[List[List[str]], np.ndarray]):
        pass

class AbstractPredictor(ABC):

    @abstractmethod
    def __init__(self):
        self.__timer = 0
        pass
    def clear_timer(self):
        self.__timer = 0
    def timer(self):
        return self.__timer
    
    @abstractmethod
    def _predict(self, x, **kwargs):
        raise NotImplementedError
    
    def _predict_lr(self, x, **kwargs):
        res:np.ndarray = self._predict(x, **kwargs)
        res = np.argmax(res, axis=1)
        return res

    def predict(self, x, **kwargs):
        bg = time.time()
        res =  self._predict(x, **kwargs)
        self.__timer += time.time() - bg
        return res
    
    def predict_lr(self, x, **kwargs):
        bg = time.time()
        res =  self._predict_lr(x, **kwargs)
        self.__timer += time.time() - bg
        return res



class AbstractConceptPredictor(AbstractPredictor):
    

    @abstractmethod
    def _predict(self, now_c: Union[List[str], np.ndarray]):
        pass


class LocalConceptPredictor(AbstractConceptPredictor):
    
    def __init__(self, concepts: List[Concept], now_c: Union[List[str], np.ndarray]):
        super().__init__()

        if len(concepts) !=  len(now_c):
            raise ValueError('The length of concept and now_c should be the same')
        
        self.concepts = concepts
        self.now_c = now_c

    @abstractmethod
    def _predict(self, data:np.ndarray)->np.ndarray:
        pass
