from LowLevelWidgetsPackage.defaultConfigForWidgets import LOGGER
from TestsRelatedPackage.TestResultWidgetsPackage.DefaultTestWidgetPromptPackage.DefaultTestWidgetPromptModule import DefaultTestWidgetPromptClass
from PyQt6 import QtWidgets
from typing import Dict,List
import yaml
import pickle
import traceback

def catchErrors(func):
    def wrapper(*args, **kwargs):
        #args[0] is self from Connection Class
        try:
            result = func(*args,**kwargs)
            return result
        
        except KeyError:
            args[0].log.log(f"Error in {func.__name__} : group name: {kwargs['group_name']} propably doesn't exist", level = "ERROR")
            
        except:
            args[0].log.log(f"Error in {func.__name__} : {traceback.format_exc()}", level = "CRITICAL", oneline = False)
            args[0].log.log(f"Error in {func.__name__} : funtion called with args = {args} and kwargs = {kwargs}", level = "CRITICAL")
            return False
    return wrapper

class _CommandResultClass():
    def __init__(self,parent_test_class,
                 parent_group_name:str,
                 index:int,
                 list_of_variable_dict:List[Dict[str,str]] = [{"NAME": "VERSION" , "VALUE" :  "V12.225.11"}],
                 test_dict:dict ={"COMMAND": "LOG THIS IS UMBRELA",
                            "LOWER_LIM": "NULL",
                            "UPPER_LIM": "NULL",
                            "EXPECTED": "THIS IS UMBRELA",
                            "WIDGET_INFO": "X_AXIS"},
                            ) -> None:
        

        self.list_of_variable_dict = list_of_variable_dict
        self._index = index
        self._parent_test: _TestResultClass = parent_test_class
        self._parent_group_name = parent_group_name
        self.command = self._replaceVariables(test_dict["COMMAND"])
        self.lower_lim = self._replaceVariables(test_dict["LOWER_LIM"])
        self.upper_lim = self._replaceVariables(test_dict["UPPER_LIM"])
        self.expected = self._replaceVariables(test_dict["EXPECTED"])
        self.measured = "???"
        self.result = "???"
        self.data = "???"
        self.command_time = 0
        if "WIDGET_INFO" in test_dict:
            self.command_widget_information = test_dict["WIDGET_INFO"]
        else:
            self.command_widget_information = None

    def _replaceVariables(self,toUpdate:str):
        for variable_dict in self.list_of_variable_dict:
            toUpdate = str(toUpdate).replace(f"$${variable_dict['NAME']}$$",f"{variable_dict['VALUE']}")
        return toUpdate
    
    def _findWidgetInformation(self) -> list:
        ...

class _TestResultClass():
    def __init__(self,index:int, yaml_file:dict, group_name:str, test_name:str = "TEST_LOG_DATA" ) -> None:
        self.name = test_name
        self._parent_group_name = group_name
        self._index = index
        self.result = "???"
        self.test_time = "0s"
        self.result_widget = None#must be loaded later
        self.commands: List[_CommandResultClass] = []
        self.test_widget_information = {}
        for index,tests in enumerate(yaml_file[self.name]):
            self.commands.append(_CommandResultClass(parent_test_class = self,
                                                                      index=index,
                                                                      parent_group_name = self._parent_group_name,
                                                                      test_dict = tests,list_of_variable_dict = yaml_file["VARIABLES"]))


class _TestOverviewClass():
    def __init__(self, log:LOGGER, yaml_file:dict) -> None:
        self.log = log
        self.yaml_file = yaml_file
        self.last_tested_group = "UNKNOWN"

    def _fillValues(self,group_name = "BASICGROUP", test_name = None):
        self.last_tested_group = group_name
        self.group_result = "???"
        self.group_time = "0s"
        self.tests : List[_TestResultClass] = []

        if test_name == None: #if test name is not specified then we will use all tests from group
            for index,test in enumerate(self.yaml_file["TESTGROUPS"][self.last_tested_group]):
                self.tests.append(_TestResultClass(index =index, group_name=self.last_tested_group,yaml_file = self.yaml_file, test_name = test))

        else: #if test name is specified then we will use only this test
            self.tests.append(_TestResultClass(index =index, group_name=self.last_tested_group,yaml_file = self.yaml_file, test_name = test_name))
            self.last_tested_group = "UNSPECIFIED"
            self.group_result = "???"
            self.group_time = "0s"


    def _printItself(self):
        self.log.log(f"GROUP: {self.last_tested_group}","DEBUG")
        self.log.log(f"GROUP RESULT: {self.group_result}","DEBUG")
        self.log.log(f"GROUP TIME: {self.group_time}","DEBUG")

        test: _TestResultClass
        for test in self.tests:
            self.log.log(f"\tTEST: {test.name}","DEBUG")
            self.log.log(f"\tTEST RESULT: {test.result}","DEBUG")
            self.log.log(f"\tTEST TIME: {test.test_time}","DEBUG")
            self.log.log(f"\tTEST Widget: {test.result_widget}","DEBUG")

            command: _CommandResultClass
            for command in test.commands:
                self.log.log(f"\t\tSPECIFIC TEST: {command.command}:: TIME: {command.command_time}:: RESULT:{command.result}","DEBUG")

        
class AllGroupsOverviewClass():
    def __init__(self, yaml_file:dict, log:LOGGER = LOGGER("ALL")) -> None:
        self.log = log
        self.groups_dict: Dict[str, _TestOverviewClass] = {}
        for group in yaml_file["TESTGROUPS"]:
            self.groups_dict[group] = _TestOverviewClass(log = self.log, yaml_file = yaml_file)
            self.groups_dict[group]._fillValues(group_name = group)


    @catchErrors
    def getSecificTestsFromTest(self,test:_TestResultClass):
        return test.commands

    @catchErrors
    def getTestsFromGroup(self,group_name = "BASICGROUP"):
        return self.groups_dict[group_name].tests

    @catchErrors
    def getListOfGroups(self):
        return list(self.groups_dict.keys())

    def updateTestResult(self,test:_TestResultClass, time = 0, result = "FAIL"):
        self.groups_dict[test._parent_group_name].tests[test._index].result = result
        self.groups_dict[test._parent_group_name].tests[test._index].test_time = time
    
    def updateSpecificTestResult(self,command: _CommandResultClass, time = 0, result = "FAIL"):
        self.groups_dict[command._parent_group_name].tests[command._parent_test._index].commands[command._index].result = result
        self.groups_dict[command._parent_group_name].tests[command._parent_test._index].commands[command._index].command_time = time

    @catchErrors
    def updateGroupResult(self,group_name = "BASICGROUP", time = 0, result = "FAIL"):
        self.groups_dict[group_name].group_result = result
        self.groups_dict[group_name].group_time = time
        
    @catchErrors
    def printGroup(self,group_name = "BASICGROUP"):
        self.groups_dict[group_name]._printItself()
        
    def printAllGroups(self):
        for group in self.groups_dict:
            self.groups_dict[group]._printItself()



    def loadDefaultsForTest(self,test:_TestResultClass):
        test.result = "???"
        test.test_time = 0
        self.log.log(f"Loading defaults for test: {test.name}","INFO")
        test.result_widget.loadDefaults()
        test.test_widget_information = {}
        for command in test.commands:
            command.result = "???"
            command.command_time = 0
            command.measured = ""
            command.data = ""

    def loadDefaultsForGroup(self,group_name):
        self.groups_dict[group_name].group_result = "???"
        self.groups_dict[group_name].group_time = 0
        test: _TestResultClass
        for test in self.groups_dict[group_name].tests:
            self.loadDefaultsForTest(test)
            
    def loadDefaultValuesForAll(self):
        """This function will respect loaded widgets and will set them to default values"""
        tests: _TestOverviewClass
        for group in self.getListOfGroups():
            self.loadDefaultsForGroup(group)
            


    def loadDefaultValuesTests(self,group_name = "BASICGROUP"):
        """This function will respect loaded widgets and will set them to default values"""
        for tests in self.groups_dict.values():
            tests._fillValues(group_name = group_name)

    def reloadItselfFromSavedBinary(self,input_file_name = "results.pkl"):
        with open(input_file_name, "rb") as file:
            self: AllGroupsOverviewClass = pickle.load(file)
            tests: _TestOverviewClass
            for tests in self.groups_dict.values():
                for test in tests.tests:
                    if test.result_widget == "DefaultTestWidgetPromptClass":
                        test.result_widget = DefaultTestWidgetPromptClass(test, self.log)

    def safeResultsToBinary(self,output_file_name = "results.pkl"):
        list_of_widgets = []
        for tests in self.groups_dict.values(): #widgets must be cleard -> than pickled -> than restored
            for test in tests.tests:
                list_of_widgets.append(test.result_widget)
                test.result_widget = test.result_widget.__class__.__name__

        with open(output_file_name, "wb") as file:
            pickle.dump(self, file)

        for tests in self.groups_dict.values():
            for test in tests.tests:
                test.result_widget = list_of_widgets.pop(0)



