
from PyQt6 import QtWidgets
from PyQt6.QtGui import QColor
from PyQt6.QtCore import pyqtSignal, pyqtSlot, QObject
from LowLevelWidgetsPackage.defaultConfigForWidgets import LOGGER 
from TestsRelatedPackage.TestTemplatePackage.TestTemplateGUI import Ui_Form
from TestsRelatedPackage.TestOverviewPackage.TestOverviewModule import AllGroupsOverviewClass
from TestsRelatedPackage.TestOverviewPackage.TestOverviewModule import _CommandResultClass, _TestResultClass
import traceback
from typing import Dict


class _TestTemplateSignals(QObject):
    run_test_request = pyqtSignal(_TestResultClass,str)
    run_test_started_response = pyqtSignal(_TestResultClass,str)
    run_test_finished_response = pyqtSignal(_TestResultClass,str)

    run_command_request = pyqtSignal(_CommandResultClass,str)
    run_command_started_response = pyqtSignal(_CommandResultClass,str)
    run_command_finished_response = pyqtSignal(_CommandResultClass,str)

    run_group_request = pyqtSignal(str,str)
    run_group_started_response = pyqtSignal(str,str)
    run_group_finished_response = pyqtSignal(str,str)

class TestTemplateClass(QtWidgets.QWidget):
   
    def __init__(self, AllGroupsOverviewClass : AllGroupsOverviewClass = AllGroupsOverviewClass, log:LOGGER = LOGGER("ALL"),*args, **kwargs):#args = multiple input variables, **kvargs multiple keyword variables (dictionaries, touples etc.)
    ################ INIT Function ##################
        super().__init__(*args, **kwargs)#super inherits all methods and properties from its parent  (QWIDGET classes)
        #Load UI forms creaated in QTdDesigner
        self.log = log
        self.AllGroupsOverviewClass = AllGroupsOverviewClass
        self.log.log("STARTING TestTemplate CLASS","UNNECESSARY")
        self.ui = Ui_Form() #UI form generated by UI designe
        self.ui.setupUi(self)
        self._currently_selected_group:str = None
        self._currently_selected_test :_TestResultClass = None
        self._placeholder_widget = self.ui.pb_test_widget_placeholder# variable to keep reference to widget that is currently loaded in the placeholder
        self.signals = _TestTemplateSignals()
        self._loadGroups()
        #self._loadTests() #this is called in _loadGroups
        self._loadCommands(self._currently_selected_test)
        self._styleWholeTable(self._currently_selected_test)
        self._loadTestWidget(self._currently_selected_test)
        self._styleWholeTable(self._currently_selected_test.commands[0])

        #callbacks
        #table clicks
        self.ui.table_test_selection.itemClicked.connect(self._clickAtTestSelectionCallback)

        #button clicks
        self.ui.pb_run_group.clicked.connect(self._runGroupButtonCallback)

    #WIDGET SIGNAL HANDLERS ------------------------
    #Received signals:
        self.signals.run_test_started_response.connect(self._runTestStartedResponse)
        self.signals.run_test_finished_response.connect(self._runTestFinishedResponse)

        self.signals.run_command_started_response.connect(self._runCommandStartedResponse)
        self.signals.run_command_finished_response.connect(self._runCommandFinishedResponse)

        self.signals.run_group_started_response.connect(self._runGroupStartedResponse)
        self.signals.run_group_finished_response.connect(self._runGroupFinishedResponse)

    #Received signals callback:
    def _runTestStartedResponse(self, test: _TestResultClass, source:str):
        self.log.log(f"TestTemplateClass: received signal that test {test.name} STARTED from {source}","SIGNAL")
        self._loadTests(test._parent_group_name)
        self._loadCommands(test)
        self._styleWholeTable(test)


    def _runTestFinishedResponse(self, test: _TestResultClass, source:str):
        self.log.log(f"TestTemplateClass: received signal that test {test.name} FINISHED from {source}","SIGNAL")
        self._loadTests(test._parent_group_name)
        self._loadTestWidget(test)
        self._styleWholeTable(test)#style test row only
        

    def _runCommandStartedResponse(self, command: _CommandResultClass, source:str):
        self.log.log(f"TestTemplateClass: received signal that command {command.command} STARTED from {source}","SIGNAL")
        self._styleTableRow(command)

    def _runCommandFinishedResponse(self, command: _CommandResultClass, source:str):
        self.log.log(f"TestTemplateClass: received signal that command {command.command} FINISHED from {source}","SIGNAL")
        if command.result != "PASS":
            self.log.log(f"TestTemplateClass: Command:{command.command} FAILED with result = {command.result}","WARNING")

        self._loadTableRow(command)
        self._styleTableRow(command)

    def _runGroupStartedResponse(self, group_name: str, source:str):
        self.log.log(f"TestTemplateClass: received signal that group {group_name} STARTED from {source}","SIGNAL")
        self.ui.cbox_tests_group.setCurrentText(group_name) #also updates test table
        #self._loadGroups(group_name)
        self._styleWholeTable(self.AllGroupsOverviewClass.groups_dict[group_name].tests[0])
        self._styleWholeTable(self.AllGroupsOverviewClass.groups_dict[group_name].tests[0].commands[0])

    def _runGroupFinishedResponse(self, group_name: str, source:str):
        self.log.log(f"TestTemplateClass: received signal that command {group_name} FINISHED from {source}","SIGNAL")
        self.ui.la_group_result.setText(self.AllGroupsOverviewClass.groups_dict[group_name].group_result)
        group_time = self.AllGroupsOverviewClass.groups_dict[group_name].group_time
        group_time  =   f"{float(str(group_time).replace('s','')):.3f}"
        self.ui.la_group_time.setText(group_time)
        self._styleWholeTable(self.AllGroupsOverviewClass.groups_dict[group_name].tests[0])

        #self._styleTableRow(command)
        
    #Buttons callbacks:

    #CALLBACKS FOR STATIC BUTTONS
    def _runGroupButtonCallback(self):
        self.log.log(f"TestTemplateClass: Sending request to run group {self._currently_selected_group}","SIGNAL")
        self.signals.run_group_request.emit(self._currently_selected_group, "TestTemplateClass")

    #CALLBACKS FOR DYNAMICALLY CREATED BUTTONS
    def _callbackForButtons(self):
        if isinstance(self.sender().to_run, _TestResultClass):
            self.log.log(f"TestTemplateClass: Sending request to run {self.sender().to_run.name} Test","SIGNAL")
            self.signals.run_test_request.emit(self.sender().to_run, "TestTemplateClass")

        if isinstance(self.sender().to_run, _CommandResultClass):
            self.log.log(f"TestTemplateClass: Sending request to run {self.sender().to_run.command} Command","SIGNAL")
            self.signals.run_command_request.emit(self.sender().to_run, "TestTemplateClass")
    
    
    #TABLE VALUES LOADING AND STYLING ---------------
    def _clickAtTestSelectionCallback(self,item:QtWidgets.QTableWidgetItem):
        self.log.log(f"TestTemplateClass: Clicked at {item.row()} row and {item.column()} column","DEBUG")
        header_indexes = self._findHeaderIndexes(self.ui.table_test_selection)
        test:_TestResultClass = self.ui.table_test_selection.cellWidget(item.row(),header_indexes["RUN"]).to_run #get test object from button
        self._loadCommands(test)
        self._styleWholeTable(test.commands[0])
        self._loadTestWidget(test)

    def _tablesRepaintAndUpdate(self, test: _TestResultClass | _CommandResultClass):
        if isinstance(test, _TestResultClass):
            self.ui.table_test_selection.resizeColumnsToContents()
            self.ui.table_test_selection.update()
            self.ui.table_test_selection.repaint()

        elif isinstance(test, _CommandResultClass):
            self.ui.table_command_selection.resizeColumnsToContents()
            self.ui.table_command_selection.update()
            self.ui.table_command_selection.repaint()
        
    def _loadGroups(self):
        self.ui.cbox_tests_group.clear()
        list_of_groups = self.AllGroupsOverviewClass.getListOfGroups()
        self.ui.cbox_tests_group.addItems(list_of_groups)
        
        #self.ui.table_test_selection.currentItemChanged.connect(self._generalSelectionChanged)
        self.ui.cbox_tests_group.setCurrentIndex(0)
        self.ui.cbox_tests_group.currentIndexChanged.connect(self._loadTests)
        #self.ui.cbox_tests_group.currentIndexChanged.emit(0)
        self._loadTests(self.ui.cbox_tests_group.currentText())
    
    def _loadTests(self,group_name:str):
        if self.sender() == self.ui.cbox_tests_group:
            group_name = self.ui.cbox_tests_group.currentText()

        self.log.log(f"TestTemplateClass: Loading tests for Group: {group_name}", "DEBUG")
        self._currently_selected_group = group_name
        list_of_tests = self.AllGroupsOverviewClass.groups_dict[group_name].tests
        self.ui.table_test_selection.clearContents()
        self.ui.table_test_selection.setRowCount(len(list_of_tests))
        header_indexes = self._findHeaderIndexes(self.ui.table_test_selection)
        for tests in list_of_tests:
            self.ui.table_test_selection.setItem(tests._index,header_indexes["Test Name"],QtWidgets.QTableWidgetItem(tests.name))
            test_time_string = f"{float(str(tests.test_time).replace('s','')):0.3f}s"
            self.ui.table_test_selection.setItem(tests._index,header_indexes["Time"],QtWidgets.QTableWidgetItem(test_time_string))
            self.ui.table_test_selection.setItem(tests._index,header_indexes["Result"],QtWidgets.QTableWidgetItem(tests.result))
            btn_widget = QtWidgets.QPushButton("RUN")
            btn_widget.to_run = tests
            btn_widget.clicked.connect(self._callbackForButtons)
            self.ui.table_test_selection.setCellWidget(tests._index,header_indexes["RUN"],btn_widget)

        self._currently_selected_test = list_of_tests[0]
        if self.sender() == self.ui.cbox_tests_group:
            self._loadCommands(self._currently_selected_test)
            self._styleWholeTable(self._currently_selected_test)
            self._styleWholeTable(self._currently_selected_test.commands[0])
            self._loadTestWidget(self._currently_selected_test)

    def _loadTableRow(self, test: _TestResultClass | _CommandResultClass):
        if isinstance(test, _TestResultClass):
            self.ui.table_test_selection.resizeColumnsToContents()
            self.ui.table_test_selection.update()
            self.ui.table_test_selection.repaint()

        elif isinstance(test, _CommandResultClass):
            header_indexes = self._findHeaderIndexes(self.ui.table_command_selection)
            self.ui.table_command_selection.item(test._index,header_indexes["Result"]).setText(test.result)
            self.ui.table_command_selection.item(test._index,header_indexes["Duration"]).setText(f"{float(str(test.command_time).replace('s','')):0.3f}s")
            self.ui.table_command_selection.item(test._index,header_indexes["Measured"]).setText(test.measured.replace("\r","\\r").replace("\n","\\n"))

    def _loadCommands(self,test:_TestResultClass):
        self._currently_selected_test = test
        self.ui.la_selected_test_name.setText(self._currently_selected_test.name)
        list_of_commands = test.commands
        self.ui.table_command_selection.clearContents()
        self.ui.table_command_selection.setRowCount(len(list_of_commands))
        header_indexes = self._findHeaderIndexes(self.ui.table_command_selection)
        for commands in list_of_commands:
            self.ui.table_command_selection.setItem(commands._index,header_indexes["Command"],QtWidgets.QTableWidgetItem(commands.command))
            self.ui.table_command_selection.setItem(commands._index,header_indexes["Result"],QtWidgets.QTableWidgetItem(commands.result))
            self.ui.table_command_selection.setItem(commands._index,header_indexes["Expected"],QtWidgets.QTableWidgetItem(commands.expected))
            self.ui.table_command_selection.setItem(commands._index,header_indexes["Measured"],QtWidgets.QTableWidgetItem(commands.measured))
            self.ui.table_command_selection.setItem(commands._index,header_indexes["Duration"],QtWidgets.QTableWidgetItem(f"{float(str(commands.command_time).replace('s','')):0.3f}s"))
            self.ui.table_command_selection.setItem(commands._index,header_indexes["From"],QtWidgets.QTableWidgetItem(commands.lower_lim))
            self.ui.table_command_selection.setItem(commands._index,header_indexes["To"],QtWidgets.QTableWidgetItem(commands.upper_lim))
            btn_widget = QtWidgets.QPushButton("RUN")
            btn_widget.to_run = commands
            btn_widget.clicked.connect(self._callbackForButtons)
            self.ui.table_command_selection.setCellWidget(commands._index,header_indexes["RUN"],btn_widget)

    
            
    def _styleWholeTable(self,test_or_command):
        if isinstance(test_or_command,_TestResultClass):
            table = self.ui.table_test_selection
            iterable = self.AllGroupsOverviewClass.groups_dict[test_or_command._parent_group_name].tests

        elif isinstance(test_or_command,_CommandResultClass):
            table = self.ui.table_command_selection
            iterable = test_or_command._parent_test.commands
        
        else:
            self.log.log(f"TestTemplateClass: Invalid type for test_or_command: {type(test_or_command)}","ERROR")
            return
        
        for row in range(table.rowCount()):
            self._styleTableRow(iterable[row])

        self._tablesRepaintAndUpdate(test_or_command)


    def _styleTableRow(self,test_or_command : _TestResultClass | _CommandResultClass):
        if isinstance(test_or_command,_TestResultClass):
            table = self.ui.table_test_selection
            row = test_or_command._index
            background_color = self._colorResultStyling(test_or_command.result)

        elif isinstance(test_or_command,_TestResultClass | _CommandResultClass):
            table = self.ui.table_command_selection
            row = test_or_command._index
            background_color = self._colorResultStyling(test_or_command.result)

        else:
            self.log.log(f"TestTemplateClass: Invalid type for test_or_command: {type(test_or_command)}","ERROR")
            return
        
        header_indexes = self._findHeaderIndexes(table)
        for column in range(table.columnCount()):
            self.log.log(f"TestTemplateClass: Setting background color for row: {row} column: {column}","UNNECESSARY")
            if header_indexes["RUN"] == column:
                continue
            table.item(row,column).setBackground(background_color)
        
        self._tablesRepaintAndUpdate(test_or_command)

    def _colorResultStyling(self, test_result) -> QColor:
        if test_result == "PASS":
             background_color = QColor(150,200,150) #green
        elif test_result == "FAIL":
            background_color = QColor(200,150,150) #red

        elif test_result == "RUNNING":
            background_color = QColor(180,180,35) #yellow

        else:
             background_color = QColor(230,230,250) #white
           
        return background_color



        
    def _findHeaderIndexes(self,table:QtWidgets.QTableWidget) -> Dict[str,int]:
        header_indexes = {}
        for i in range(table.columnCount()):
            if table.horizontalHeaderItem(i) == None: #it is not item but cellWidget (RUN button)
                self.log.log(f"TestTemplateClass: Header {i} is RUN BUTTON","WARNING")
                header_indexes["RUN"] = i
                continue
            header_indexes[table.horizontalHeaderItem(i).text()] = i
        return header_indexes
    
    def _loadTestWidget(self,test:_TestResultClass):
        if test.result_widget == None:
            self.log.log(f"TestTemplateClass: Widget not loaded","CRITICAL")
            return
        if test.result_widget == self._placeholder_widget:
            self.log.log(f"TestTemplateClass: Widget already loaded","UNNECESSARY")
            return
        self.log.log(f"TestTemplateClass: Replacing {self._placeholder_widget} with {test.result_widget}","INFO")
        self.ui.layout_with_placeholder.layout().replaceWidget(self._placeholder_widget,test.result_widget)
        self._placeholder_widget.setParent(None) #delete placeholder from memory
        self._placeholder_widget = test.result_widget
        
    