import fissure.comms
import time
from PyQt5 import QtCore, QtWidgets, QtGui
import yaml
import os
import subprocess
import threading
import ast
import asyncio
from typing import List
import json
import qasync
import datetime

from fissure.Dashboard.UI_Components.Qt5 import MyMessageBox
# from ..Dashboard.Slots import StatusBarSlots  # how do you go from callbacks to slots?
from fissure.Dashboard.Slots import (
    ArchiveTabSlots,
    AttackTabSlots,
    DashboardSlots,
    IQDataTabSlots,
    LibraryTabSlots,
    LogTabSlots,
    MenuBarSlots,
    PDTabSlots,
    SensorNodesTabSlots,
    SensorNodesPluginsTabSlots,
    StatusBarSlots,
    TopBarSlots,
    TSITabSlots,
)

from fissure.Dashboard.UI_Components.Qt5 import (
    # CustomColor,
    # JointPlotDialog,
    # MiscChooser,
    # MyMessageBox,
    MyPlotWindow,
    # NewSOI,
    # OperationsThread,
    # OptionsDialog,
    # SigMF_Dialog,
    # TreeModel,
    # TreeNode,
    # TrimSettings,
)


async def recallInfoMeshtasticReturnLT(component: object, tab_index="", nickname="", location="", notes=""):
    """
    Populates the HardwareSelectDialog with the sensor node settings on connect.
    """
    if tab_index and 0 <= int(tab_index) < 5:
        # Dynamically retrieve all relevant widgets
        widget_number = str(int(tab_index)+1)
        nickname_widget = getattr(component.frontend.popups["HardwareSelectDialog"], f"textEdit_nickname_{widget_number}")
        location_widget = getattr(component.frontend.popups["HardwareSelectDialog"], f"textEdit_location_{widget_number}")
        notes_widget = getattr(component.frontend.popups["HardwareSelectDialog"], f"textEdit_notes_{widget_number}")
        
        # Set the values
        nickname_widget.setPlainText(nickname)
        location_widget.setPlainText(location)
        notes_widget.setPlainText(notes)


async def recallHardwareMeshtasticReturnLT(component: object, tsi={}):
    """
    Populates the HardwareSelectDialog with the sensor node settings on connect.
    """
    print("HARDWARE RETURN @#$#@$@#$#@$#@")  # To Do
    print(tsi)
    # Pass Sensor Node Settings to HardwareSelectDialog
    # component.frontend.popups["HardwareSelectDialog"].importResults(settings_dict=settings_dict)


async def recallStatusMeshtasticReturnLT(component: object, tab_index="", status=""):
    """
    Populates the HardwareSelectDialog with the sensor node settings on connect.
    """
    if tab_index and 0 <= int(tab_index) < 5:
        # Open a Text Dialog
        ret = await fissure.Dashboard.UI_Components.Qt5.async_ok_dialog(component.frontend.popups["HardwareSelectDialog"], status)
        return


async def findGPS_CoordinatesResultsLT(component: object, tab_index=0, coordinates=""):
    """
    Returns the GPS coordinate results to the HardwareSelectDialog.
    """
    # Populate Location
    location_widget = [
        component.frontend.popups["HardwareSelectDialog"].textEdit_location_1,
        component.frontend.popups["HardwareSelectDialog"].textEdit_location_2,
        component.frontend.popups["HardwareSelectDialog"].textEdit_location_3,
        component.frontend.popups["HardwareSelectDialog"].textEdit_location_4,
        component.frontend.popups["HardwareSelectDialog"].textEdit_location_5
    ]
    location_widget[int(tab_index)].setPlainText(str(coordinates))

    # Enable the Find Button
    find_widgets = [
        component.frontend.popups["HardwareSelectDialog"].pushButton_find_1,
        component.frontend.popups["HardwareSelectDialog"].pushButton_find_2,
        component.frontend.popups["HardwareSelectDialog"].pushButton_find_3,
        component.frontend.popups["HardwareSelectDialog"].pushButton_find_4,
        component.frontend.popups["HardwareSelectDialog"].pushButton_find_5
    ]
    # find_widgets[int(tab_index)].setEnabled(True)


async def hardwareProbeResultsLT(component: object, tab_index=0, output="", height_width=[]):
    """
    Returns the probe results to the HardwareSelectDialog.
    """
    # Parse Return String
    probe_text = output
    
    # Hide Label
    scan_results_labels = [
        component.frontend.popups["HardwareSelectDialog"].label2_scan_results_probe_1,
        component.frontend.popups["HardwareSelectDialog"].label2_scan_results_probe_2,
        component.frontend.popups["HardwareSelectDialog"].label2_scan_results_probe_3,
        component.frontend.popups["HardwareSelectDialog"].label2_scan_results_probe_4,
        component.frontend.popups["HardwareSelectDialog"].label2_scan_results_probe_5
    ]
    scan_results_labels[int(tab_index)].setVisible(False)

    # Enable Probe Button
    probe_buttons = [
        component.frontend.popups["HardwareSelectDialog"].pushButton_scan_results_probe_1,
        component.frontend.popups["HardwareSelectDialog"].pushButton_scan_results_probe_2,
        component.frontend.popups["HardwareSelectDialog"].pushButton_scan_results_probe_3,
        component.frontend.popups["HardwareSelectDialog"].pushButton_scan_results_probe_4,
        component.frontend.popups["HardwareSelectDialog"].pushButton_scan_results_probe_5
    ]
    probe_buttons[int(tab_index)].setEnabled(True)

    # Open a Text Dialog
    if height_width[0] == '':
        msgBox = MyMessageBox(my_text = probe_text)
    else:
        msgBox = MyMessageBox(my_text = probe_text, height=height_width[0], width=height_width[1])
    msgBox.exec_()
    

async def hardwareScanResultsLT(component: object, tab_index=0, hardware_scan_results=[]):
    """
    Returns Auto Scan results to the HardwareSelectDialog.
    """
    component.frontend.popups["HardwareSelectDialog"].scanReturn(tab_index=tab_index, all_scan_results=hardware_scan_results)


async def hardwareGuessResultsLT(component: object, tab_index=0, table_row=0, hardware_type="", scan_results="", new_guess_index=0):
    """
    Fills the scan results table row with hardware information in HardwareSelectDialog.
    """
    # Fill the Table
    component.frontend.popups["HardwareSelectDialog"].guessReturn(tab_index, table_row, hardware_type, scan_results, new_guess_index)


async def uptimeMeshtasticReturnLT(component: object, sensor_node_id:str, uptime: str):
    """
    Returns the uptime results to the HardwareSelectDialog.
    """
    # Open a Text Dialog
    ret = await fissure.Dashboard.UI_Components.Qt5.async_ok_dialog(component.frontend.popups["HardwareSelectDialog"], uptime)


async def memoryMeshtasticReturnLT(component: object, sensor_node_id:str, memory: list):
    """
    Returns the memory usage results to the HardwareSelectDialog.
    """
    # Open a Text Dialog
    headers = ["total", "used", "free", "shared", "buff/cache", "available"]
    memory_string = "\n".join(f"{key}: {value}" for key, value in zip(headers, memory))
    ret = await fissure.Dashboard.UI_Components.Qt5.async_ok_dialog(component.frontend.popups["HardwareSelectDialog"], memory_string)


async def diskMeshtasticReturnLT(component: object, sensor_node_id:str, disk: dict):
    """
    Returns the disk usage results to the HardwareSelectDialog.
    """
    # Open a Text Dialog
    disk_string = "\n".join(f"{k}: {v}" for k, v in disk.items())
    ret = await fissure.Dashboard.UI_Components.Qt5.async_ok_dialog(component.frontend.popups["HardwareSelectDialog"], disk_string)


async def cpuMeshtasticReturnLT(component: object, sensor_node_id:str, cpu: str):
    """
    Returns the CPU percentage results to the HardwareSelectDialog.
    """
    # Open a Text Dialog
    ret = await fissure.Dashboard.UI_Components.Qt5.async_ok_dialog(component.frontend.popups["HardwareSelectDialog"], cpu)


async def processesMeshtasticReturnLT(component: object, sensor_node_id:str, processes: str):
    """
    Returns the processes results to the HardwareSelectDialog.
    """
    # Open a Text Dialog
    ret = await fissure.Dashboard.UI_Components.Qt5.async_ok_dialog(component.frontend.popups["HardwareSelectDialog"], processes)


async def ifconfigMeshtasticReturnLT(component: object, sensor_node_id:str, ifconfig:str):
    """
    Returns the ifconfig results to the HardwareSelectDialog.
    """
    # Open a Text Dialog
    ret = await fissure.Dashboard.UI_Components.Qt5.async_ok_dialog(component.frontend.popups["HardwareSelectDialog"], ifconfig)


async def iwconfigMeshtasticReturnLT(component: object, sensor_node_id:str, iwconfig:str):
    """
    Returns the iwconfig results to the HardwareSelectDialog.
    """
    # Open a Text Dialog
    ret = await fissure.Dashboard.UI_Components.Qt5.async_ok_dialog(component.frontend.popups["HardwareSelectDialog"], iwconfig)


########################################################################

async def alertReturnLT(component: object, sensor_node_id=0, alert_text=""):
    """ 
    Updates the Sensor Nodes Alert tab with a new alert.
    """
    # Get Sensor Node Nickname
    if sensor_node_id == 0:
        get_nickname = component.settings['sensor_node1']['nickname']
    elif sensor_node_id == 1:
        get_nickname = component.settings['sensor_node2']['nickname']
    elif sensor_node_id == 2:
        get_nickname = component.settings['sensor_node3']['nickname']
    elif sensor_node_id == 3:
        get_nickname = component.settings['sensor_node4']['nickname']
    elif sensor_node_id == 4:
        get_nickname = component.settings['sensor_node5']['nickname']
    else:
        get_nickname = ""

    # Generate a timestamp
    timestamp = datetime.datetime.now().strftime("[%Y-%m-%d %H:%M:%S]")
    sensor_node_text = "[" + get_nickname + "]"
    formatted_message = f"{timestamp} {sensor_node_text} {alert_text}"

    # Append the message
    current_content = component.frontend.ui.textEdit2_sensor_nodes_alerts.toPlainText()
    updated_content = current_content + '\n' + formatted_message if current_content else formatted_message

    component.frontend.ui.textEdit2_sensor_nodes_alerts.setPlainText(updated_content)
    component.frontend.ui.textEdit2_sensor_nodes_alerts.verticalScrollBar().setValue(component.frontend.ui.textEdit2_sensor_nodes_alerts.verticalScrollBar().maximum())

    # Calculate Alert Total
    current_text = component.frontend.ui.tabWidget_sensor_nodes.tabBar().tabText(3)
    if "(" in current_text and ")" in current_text:
        base_text, count = current_text.rsplit("(", 1)
        count = count.rstrip(")")
        try:
            current_count = int(count)
        except ValueError:
            current_count = 0
    else:
        base_text = current_text
        current_count = 0

    new_count = current_count + 1
    new_text = f"{base_text.strip()} ({new_count})"

    # Update Alert Tab with Count
    component.frontend.ui.tabWidget_sensor_nodes.tabBar().setTabText(3, new_text)

    # Update Sensor Nodes Tab with Count
    component.frontend.ui.tabWidget.tabBar().setTabText(6, new_text.replace("Alerts", "Sensor Nodes"))


async def exploitReturnLT(component: object, sensor_node_id: str, protocol:str, modulation:str, hardware:str, type:str, attack:str, variables:str):
    """ 
    Updates the Sensor Nodes Exploit tab with a new alert.
    """
    # Append the message
    row_position = component.frontend.ui.tableWidget_exploits.rowCount()
    component.frontend.ui.tableWidget_exploits.insertRow(row_position)
    component.frontend.ui.tableWidget_exploits.setItem(row_position, 0, QtWidgets.QTableWidgetItem(protocol))
    component.frontend.ui.tableWidget_exploits.setItem(row_position, 1, QtWidgets.QTableWidgetItem(modulation))
    component.frontend.ui.tableWidget_exploits.setItem(row_position, 2, QtWidgets.QTableWidgetItem(hardware))
    component.frontend.ui.tableWidget_exploits.setItem(row_position, 3, QtWidgets.QTableWidgetItem(type))
    component.frontend.ui.tableWidget_exploits.setItem(row_position, 4, QtWidgets.QTableWidgetItem(attack))
    component.frontend.ui.tableWidget_exploits.setItem(row_position, 5, QtWidgets.QTableWidgetItem(str(variables)))

    # Calculate Alert Total
    current_text = component.frontend.ui.tabWidget_sensor_nodes.tabBar().tabText(4)
    if "(" in current_text and ")" in current_text:
        base_text, count = current_text.rsplit("(", 1)
        count = count.rstrip(")")
        try:
            current_count = int(count)
        except ValueError:
            current_count = 0
    else:
        base_text = current_text
        current_count = 0

    new_count = current_count + 1
    new_text = f"{base_text.strip()} ({new_count})"

    # Update Alert Tab with Count
    #component.frontend.ui.tabWidget_sensor_nodes.tabBar().setTabText(3, new_text)
    
    # Update Epxloits Tab with Count
    component.frontend.ui.tabWidget_sensor_nodes.tabBar().setTabText(4, new_text)
    
    # Update Sensor Nodes Tab with Count
    component.frontend.ui.tabWidget.tabBar().setTabText(6, new_text.replace("Exploits", "Sensor Nodes"))
