import sys
import time
import json
import threading
import os
from PyQt5 import QtCore, QtGui, QtWidgets
from pynput import mouse, keyboard
from pynput.keyboard import Key, Controller as KeyboardController, Listener as KeyboardListener, GlobalHotKeys
from pynput.mouse import Button, Controller as MouseController, Listener as MouseListener
from PyQt5.QtWidgets import QFileDialog, QMessageBox, QSystemTrayIcon, QMenu, QListWidgetItem, QInputDialog


PREMIUM_STYLE = """
    QMainWindow {
        background: qlineargradient(x1:0, y1:0, x2:1, y2:1,
                    stop:0 #0a192f,
                    stop:0.5 #112240,
                    stop:1 #1a365d);
        color: #ffffff;
    }
    
    QWidget[class="glass-panel"] {
        background: rgba(22, 33, 62, 0.65);
        border: 2px solid rgba(100, 255, 218, 0.1);
        border-radius: 16px;
        padding: 20px;
        margin: 10px;
    }
    
    QPushButton {
        background-color: rgba(100, 255, 218, 0.08);
        border: 2px solid #64ffda;
        border-radius: 8px;
        color: #64ffda;
        padding: 12px 24px;
        font-size: 14px;
        font-weight: 600;
        letter-spacing: 0.5px;
        text-transform: uppercase;
        margin: 5px;
    }
    
    QPushButton:hover {
        background-color: rgba(100, 255, 218, 0.15);
        border: 2px solid #64ffda;
    }
    
    QPushButton:pressed {
        background-color: rgba(100, 255, 218, 0.25);
        padding-top: 13px;
        padding-bottom: 11px;
    }
    
    QListWidget {
        background-color: rgba(10, 25, 47, 0.7);
        border: 2px solid rgba(100, 255, 218, 0.1);
        border-radius: 12px;
        padding: 5px;
        outline: none;
    }
    
    QListWidget::item {
        background-color: rgba(22, 33, 62, 0.6);
        border: 2px solid rgba(100, 255, 218, 0.1);
        border-radius: 8px;
        margin: 5px;
        padding: 12px;
        color: #ccd6f6;
    }
    
    QListWidget::item:selected {
        background-color: rgba(100, 255, 218, 0.15);
        border: 2px solid #64ffda;
        color: #ffffff;
    }
    
    QListWidget::item:hover {
        background-color: rgba(22, 33, 62, 0.8);
        border: 2px solid rgba(100, 255, 218, 0.3);
    }
    
    QLineEdit {
        background-color: rgba(10, 25, 47, 0.7);
        border: 2px solid rgba(100, 255, 218, 0.2);
        border-radius: 8px;
        padding: 10px;
        color: #ffffff;
        font-size: 14px;
        selection-background-color: #64ffda;
        selection-color: #0a192f;
    }
    
    QLineEdit:focus {
        border: 2px solid #64ffda;
        background-color: rgba(10, 25, 47, 0.85);
    }
    
    QLabel {
        color: #8892b0;
        font-size: 14px;
        font-weight: 500;
    }
    
    QLabel[class="status-label"] {
        color: #64ffda;
        font-size: 16px;
        font-weight: bold;
        padding: 10px;
        border-radius: 8px;
        background-color: rgba(100, 255, 218, 0.1);
        border: 1px solid rgba(100, 255, 218, 0.2);
    }
    
    QComboBox {
        background-color: rgba(10, 25, 47, 0.7);
        border: 2px solid rgba(100, 255, 218, 0.2);
        border-radius: 8px;
        padding: 8px;
        color: #ffffff;
        min-width: 100px;
    }
    
    QComboBox:hover {
        border: 2px solid #64ffda;
    }
    
    QComboBox::drop-down {
        border: none;
        width: 20px;
        border-radius: 8px;
    }
    
    QComboBox::down-arrow {
        width: 12px;
        height: 12px;
        background-color: #64ffda;
    }
    
    QScrollBar:vertical {
        background-color: rgba(10, 25, 47, 0.5);
        width: 12px;
        margin: 0;
        border-radius: 6px;
    }
    
    QScrollBar::handle:vertical {
        background-color: rgba(100, 255, 218, 0.3);
        min-height: 20px;
        border-radius: 6px;
    }
    
    QScrollBar::handle:vertical:hover {
        background-color: rgba(100, 255, 218, 0.5);
    }
    
    QScrollBar::add-line:vertical,
    QScrollBar::sub-line:vertical {
        height: 0px;
    }
    
    QProgressBar {
        border: none;
        background-color: rgba(10, 25, 47, 0.7);
        border-radius: 8px;
        height: 8px;
        text-align: center;
    }
    
    QProgressBar::chunk {
        background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0,
                    stop:0 #64ffda,
                    stop:1 #00bfff);
        border-radius: 8px;
    }
    
    QCheckBox {
        color: #8892b0;
        spacing: 8px;
    }
    
    QCheckBox::indicator {
        width: 18px;
        height: 18px;
        border-radius: 4px;
        border: 2px solid rgba(100, 255, 218, 0.3);
    }
    
    QCheckBox::indicator:checked {
        background-color: #64ffda;
        border: 2px solid #64ffda;
    }
    
    QCheckBox::indicator:hover {
        border: 2px solid #64ffda;
    }
    
    QMenuBar {
        background-color: rgba(10, 25, 47, 0.95);
        color: #ccd6f6;
        border-bottom: 2px solid rgba(100, 255, 218, 0.1);
    }
    
    QMenuBar::item:selected {
        background-color: rgba(100, 255, 218, 0.1);
        color: #64ffda;
    }
    
    QMenu {
        background-color: rgba(10, 25, 47, 0.95);
        border: 1px solid rgba(100, 255, 218, 0.2);
        border-radius: 8px;
        padding: 5px;
    }
    
    QMenu::item {
        padding: 8px 25px;
        border-radius: 4px;
        color: #ccd6f6;
    }
    
    QMenu::item:selected {
        background-color: rgba(100, 255, 218, 0.1);
        color: #64ffda;
    }
    
    QToolTip {
        background-color: #112240;
        color: #64ffda;
        border: 1px solid #64ffda;
        border-radius: 4px;
        padding: 5px;
    }
    
    QDialog {
        background: qlineargradient(x1:0, y1:0, x2:1, y2:1,
                    stop:0 #0a192f,
                    stop:0.5 #112240,
                    stop:1 #1a365d);
        border: 2px solid rgba(100, 255, 218, 0.2);
        border-radius: 12px;
    }
"""


class MacroEvent:
    def __init__(self, event_type, time_delay, details):
        self.event_type = event_type
        self.time_delay = time_delay  # in milliseconds
        self.details = details


class Macro:
    def __init__(self, name):
        self.name = name
        self.events = []


class Settings:
    def __init__(self):
        self.playback_speed = 1.0
        self.loop = False
        self.save_path = "macros"
        self.hotkeys = {}
        self.schedule_playbacks = {}


class SettingsDialog(QtWidgets.QDialog):
    def __init__(self, settings, parent=None):
        super(SettingsDialog, self).__init__(parent)
        self.setWindowTitle("Settings")
        self.setFixedSize(400, 600)
        self.settings = settings
        layout = QtWidgets.QVBoxLayout()

        # Playback Speed
        speed_layout = QtWidgets.QHBoxLayout()
        speed_label = QtWidgets.QLabel("Playback Speed:")
        self.speed_combo = QtWidgets.QComboBox()
        self.speed_combo.addItems(["0.5x", "1x", "1.5x", "2x"])
        index = {0.5: 0, 1.0: 1, 1.5: 2, 2.0: 3}.get(self.settings.playback_speed, 1)
        self.speed_combo.setCurrentIndex(index)
        speed_layout.addWidget(speed_label)
        speed_layout.addWidget(self.speed_combo)
        layout.addLayout(speed_layout)

        # Loop Playback
        loop_layout = QtWidgets.QHBoxLayout()
        loop_label = QtWidgets.QLabel("Loop Playback:")
        self.loop_checkbox = QtWidgets.QCheckBox()
        self.loop_checkbox.setChecked(self.settings.loop)
        loop_layout.addWidget(loop_label)
        loop_layout.addWidget(self.loop_checkbox)
        layout.addLayout(loop_layout)

        # Save Path
        path_layout = QtWidgets.QHBoxLayout()
        path_label = QtWidgets.QLabel("Save Path:")
        self.path_edit = QtWidgets.QLineEdit(self.settings.save_path)
        self.browse_btn = QtWidgets.QPushButton("Browse")
        self.browse_btn.clicked.connect(self.browse_folder)
        path_layout.addWidget(path_label)
        path_layout.addWidget(self.path_edit)
        path_layout.addWidget(self.browse_btn)
        layout.addLayout(path_layout)

        # Hotkeys
        hotkeys_label = QtWidgets.QLabel("Hotkeys (Format: <ctrl>+<alt>+<key>):")
        layout.addWidget(hotkeys_label)
        self.hotkeys_edits = {}
        actions = ["start_recording", "stop_recording", "start_playback", "stop_playback"]
        for action in actions:
            hk_layout = QtWidgets.QHBoxLayout()
            hk_label = QtWidgets.QLabel(f"{action.replace('_', ' ').title()}:")
            hk_edit = QtWidgets.QLineEdit(self.settings.hotkeys.get(action, ""))
            self.hotkeys_edits[action] = hk_edit
            hk_layout.addWidget(hk_label)
            hk_layout.addWidget(hk_edit)
            layout.addLayout(hk_layout)

        # Schedule Playback
        schedule_label = QtWidgets.QLabel("Schedule Playbacks:")
        layout.addWidget(schedule_label)
        self.schedule_list = QtWidgets.QListWidget()
        for macro_name, schedule in self.settings.schedule_playbacks.items():
            item = QListWidgetItem(f"Macro: {macro_name}, Time: {schedule['time']}, Repeat: {'Yes' if schedule['repeat'] else 'No'}")
            self.schedule_list.addItem(item)
        layout.addWidget(self.schedule_list)
        schedule_btn_layout = QtWidgets.QHBoxLayout()
        add_schedule_btn = QtWidgets.QPushButton("Add Schedule")
        remove_schedule_btn = QtWidgets.QPushButton("Remove Schedule")
        add_schedule_btn.clicked.connect(self.add_schedule)
        remove_schedule_btn.clicked.connect(self.remove_schedule)
        schedule_btn_layout.addWidget(add_schedule_btn)
        schedule_btn_layout.addWidget(remove_schedule_btn)
        layout.addLayout(schedule_btn_layout)

        # Buttons
        buttons = QtWidgets.QHBoxLayout()
        save_btn = QtWidgets.QPushButton("Save")
        cancel_btn = QtWidgets.QPushButton("Cancel")
        save_btn.clicked.connect(self.save_settings)
        cancel_btn.clicked.connect(self.reject)
        buttons.addWidget(save_btn)
        buttons.addWidget(cancel_btn)
        layout.addLayout(buttons)

        self.setLayout(layout)

    def browse_folder(self):
        folder = QFileDialog.getExistingDirectory(self, "Select Save Folder", self.settings.save_path)
        if folder:
            self.path_edit.setText(folder)

    def add_schedule(self):
        # Accessing parent macros
        parent_macros = self.parent().macros if self.parent() else []
        macro_names = [macro.name for macro in parent_macros]
        if not macro_names:
            QMessageBox.warning(self, "Warning", "No macros available to schedule.")
            return
        macro, ok = QInputDialog.getItem(self, "Select Macro", "Macro:", macro_names, 0, False)
        if not ok or not macro:
            return
        time_str, ok = QInputDialog.getText(self, "Set Time", "Enter time (HH:MM:SS):")
        if not ok or not time_str:
            return
        # Validate time format
        try:
            time.strptime(time_str, "%H:%M:%S")
        except ValueError:
            QMessageBox.warning(self, "Warning", "Invalid time format. Please use HH:MM:SS.")
            return
        repeat, ok = QInputDialog.getItem(self, "Repeat", "Repeat:", ["Yes", "No"], 1, False)
        if not ok or not repeat:
            return
        self.settings.schedule_playbacks[macro] = {"time": time_str, "repeat": True if repeat == "Yes" else False}
        item = QListWidgetItem(f"Macro: {macro}, Time: {time_str}, Repeat: {repeat}")
        self.schedule_list.addItem(item)

    def remove_schedule(self):
        selected = self.schedule_list.currentRow()
        if selected >= 0:
            item = self.schedule_list.takeItem(selected)
            text = item.text()
            parts = text.split(", ")
            macro_part = parts[0].split(": ")[1]
            if macro_part in self.settings.schedule_playbacks:
                del self.settings.schedule_playbacks[macro_part]
            del item

    def save_settings(self):
        # Update settings from dialog fields
        self.settings.playback_speed = float(self.speed_combo.currentText().replace('x', ''))
        self.settings.loop = self.loop_checkbox.isChecked()
        self.settings.save_path = self.path_edit.text()
        for action, edit in self.hotkeys_edits.items():
            self.settings.hotkeys[action] = edit.text()
        self.accept()


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setWindowTitle("Advanced Macro Recorder")
        self.setMinimumSize(800, 600)
        self.settings = Settings()
        self.macros = []
        self.recording = False
        self.playing = False
        self.current_macro = None
        self.hotkey_listener = None
        self.schedule_threads = []
        self.kb_controller = KeyboardController()
        self.mouse_controller = MouseController()
        self.init_ui()
        self.init_tray()
        self.load_settings()
        self.register_hotkeys()
        self.start_schedules()

    def init_ui(self):
        # Apply premium styles
        self.setStyleSheet(PREMIUM_STYLE)
        
        # Main layout with proper spacing
        central = QtWidgets.QWidget()
        main_layout = QtWidgets.QHBoxLayout()
        main_layout.setSpacing(25)
        main_layout.setContentsMargins(25, 25, 25, 25)

        # Create side panels
        self.create_command_center(main_layout)
        self.create_control_hub(main_layout)

        central.setLayout(main_layout)
        self.setCentralWidget(central)

    def create_command_center(self, main_layout):
        left_panel = QtWidgets.QVBoxLayout()
        left_panel.setSpacing(20)

        # Premium Header Dashboard
        header = self.create_premium_header()
        left_panel.addWidget(header)

        # Advanced Search System
        search_system = self.create_search_system()
        left_panel.addWidget(search_system)

        # Macro Management Center
        macro_center = self.create_macro_center()
        left_panel.addWidget(macro_center)

        main_layout.addLayout(left_panel, 2)

    def create_control_hub(self, main_layout):
        right_panel = QtWidgets.QVBoxLayout()
        right_panel.setSpacing(20)

        # Control Command Center
        control_center = self.create_control_center()
        right_panel.addWidget(control_center)

        # Performance Monitor
        monitor = self.create_performance_monitor()
        right_panel.addWidget(monitor)

        # Quick Actions Panel
        actions_panel = self.create_quick_actions()
        right_panel.addWidget(actions_panel)

        main_layout.addLayout(right_panel, 3)

    def create_premium_header(self):
        header = QtWidgets.QFrame()
        header.setProperty("class", "glass-panel")
        layout = QtWidgets.QVBoxLayout(header)

        # Premium branding
        title = QtWidgets.QLabel("🚀 PREMIUM MACRO STUDIO")
        title.setStyleSheet("""
            font-size: 28px;
            color: #64ffda;
            font-weight: bold;
            letter-spacing: 2px;
            padding: 15px 0;
            text-align: center;
        """)
        layout.addWidget(title)

        # Premium status bar
        status_bar = QtWidgets.QFrame()
        status_layout = QtWidgets.QHBoxLayout(status_bar)
        status_layout.setSpacing(20)

        metrics = [
            ("Active Macros", "12", "#64ffda"),
            ("Total Runtime", "124h", "#ff7eb6"),
            ("Success Rate", "99.9%", "#7ee8fa")
        ]

        for label, value, color in metrics:
            metric = QtWidgets.QFrame()
            metric_layout = QtWidgets.QVBoxLayout(metric)
            
            label_widget = QtWidgets.QLabel(label)
            value_widget = QtWidgets.QLabel(value)
            value_widget.setStyleSheet(f"""
                color: {color};
                font-size: 24px;
                font-weight: bold;
            """)
            
            metric_layout.addWidget(label_widget)
            metric_layout.addWidget(value_widget)
            status_layout.addWidget(metric)

        layout.addWidget(status_bar)
        return header

    def create_search_system(self):
        search_frame = QtWidgets.QFrame()
        search_frame.setProperty("class", "glass-panel")
        layout = QtWidgets.QVBoxLayout(search_frame)

        # Advanced Search Bar
        search_bar = QtWidgets.QLineEdit()
        search_bar.setProperty("class", "search-input")
        search_bar.setPlaceholderText("🔍 Search macros with advanced filters...")
        layout.addWidget(search_bar)

        # Filter Tags
        tags_layout = QtWidgets.QHBoxLayout()
        tags = ["Recent", "Favorites", "Automated", "Custom"]
        
        for tag in tags:
            tag_btn = QtWidgets.QPushButton(tag)
            tag_btn.setStyleSheet("""
                QPushButton {
                    background: rgba(100, 255, 218, 0.1);
                    border: 1px solid #64ffda;
                    border-radius: 15px;
                    padding: 5px 15px;
                    color: #64ffda;
                }
                QPushButton:hover {
                    background: rgba(100, 255, 218, 0.2);
                }
            """)
            tags_layout.addWidget(tag_btn)
        
        layout.addLayout(tags_layout)
        return search_frame

    def create_macro_center(self):
        center = QtWidgets.QFrame()
        center.setProperty("class", "glass-panel")
        layout = QtWidgets.QVBoxLayout(center)

        # Category Navigation
        nav_layout = QtWidgets.QHBoxLayout()
        categories = ["All Macros", "Scheduled", "Templates", "Archives"]
        
        for category in categories:
            cat_btn = QtWidgets.QPushButton(category)
            cat_btn.setStyleSheet("""
                QPushButton {
                    background: transparent;
                    border: none;
                    color: #ffffff;
                    padding: 10px;
                    font-size: 14px;
                }
                QPushButton:hover {
                    color: #64ffda;
                    border-bottom: 2px solid #64ffda;
                }
            """)
            nav_layout.addWidget(cat_btn)
        
        layout.addLayout(nav_layout)

        # Enhanced Macro List
        self.macro_list = QtWidgets.QListWidget()
        self.macro_list.setStyleSheet("""
            QListWidget::item {
                background: rgba(22, 33, 62, 0.7);
                margin: 5px;
                border-radius: 8px;
                padding: 15px;
            }
            QListWidget::item:selected {
                background: rgba(100, 255, 218, 0.15);
                border: 1px solid #64ffda;
            }
        """)
        layout.addWidget(self.macro_list)

        return center

    def create_control_center(self):
        control = QtWidgets.QFrame()
        control.setProperty("class", "glass-panel")
        layout = QtWidgets.QVBoxLayout(control)

        # Premium Control Buttons
        buttons_layout = QtWidgets.QGridLayout()
        buttons = [
            ("⏺ RECORD", "record_btn", 0, 0),
            ("⏹ STOP", "stop_btn", 0, 1),
            ("▶ PLAY", "play_btn", 0, 2),
            ("💾 SAVE", "save_btn", 1, 0),
            ("📂 LOAD", "load_btn", 1, 1),
            ("🗑 DELETE", "delete_btn", 1, 2)
        ]

        for text, attr, row, col in buttons:
            btn = QtWidgets.QPushButton(text)
            btn.setProperty("class", "premium-btn")
            setattr(self, attr, btn)
            buttons_layout.addWidget(btn, row, col)

        layout.addLayout(buttons_layout)
        return control

    def create_performance_monitor(self):
        monitor = QtWidgets.QFrame()
        monitor.setProperty("class", "glass-panel")
        layout = QtWidgets.QVBoxLayout(monitor)

        # Status Indicator
        self.status_label = QtWidgets.QLabel("◉ System Active")
        self.status_label.setProperty("class", "status-label")
        layout.addWidget(self.status_label)

        # Performance Metrics
        metrics_layout = QtWidgets.QGridLayout()
        metrics = [
            ("CPU Usage", "2%", 0, 0),
            ("Memory", "124MB", 0, 1),
            ("Network", "1.2 MB/s", 1, 0),
            ("Threads", "4 Active", 1, 1)
        ]

        for label, value, row, col in metrics:
            metric_frame = QtWidgets.QFrame()
            metric_frame.setProperty("class", "premium-card")
            metric_layout = QtWidgets.QVBoxLayout(metric_frame)
            
            label_widget = QtWidgets.QLabel(label)
            value_widget = QtWidgets.QLabel(value)
            value_widget.setStyleSheet("color: #64ffda; font-size: 16px; font-weight: bold;")
            
            metric_layout.addWidget(label_widget)
            metric_layout.addWidget(value_widget)
            metrics_layout.addWidget(metric_frame, row, col)

        layout.addLayout(metrics_layout)
        return monitor

    def create_quick_actions(self):
        actions = QtWidgets.QFrame()
        actions.setProperty("class", "glass-panel")
        layout = QtWidgets.QHBoxLayout(actions)

        # Quick Action Buttons
        buttons = [
            ("⚙ Settings", "settings_btn"),
            ("📊 Analytics", "analytics_btn"),
            ("🔄 Sync", "sync_btn"),
            ("📱 Mobile", "mobile_btn"),
            ("❔ Help", "help_btn")
        ]

        for text, attr in buttons:
            btn = QtWidgets.QPushButton(text)
            btn.setProperty("class", "premium-btn")
            setattr(self, attr, btn)
            layout.addWidget(btn)

        return actions

    def init_tray(self):
        self.tray = QSystemTrayIcon(self)
        self.tray.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_ComputerIcon))
        tray_menu = QMenu()

        open_action = tray_menu.addAction("Open")
        exit_action = tray_menu.addAction("Exit")
        open_action.triggered.connect(self.show)
        exit_action.triggered.connect(QtWidgets.qApp.quit)

        self.tray.setContextMenu(tray_menu)
        self.tray.activated.connect(self.on_tray_activated)
        self.tray.show()

    def on_tray_activated(self, reason):
        if reason == QSystemTrayIcon.Trigger:
            self.show()

    def closeEvent(self, event):
        event.ignore()
        self.hide()

    def register_hotkeys(self):
        if self.hotkey_listener:
            self.hotkey_listener.stop()
        hotkey_dict = {}
        for action, key in self.settings.hotkeys.items():
            if key:
                # Convert the key string to the format pynput expects
                hotkey = self.parse_hotkey(key)
                if hotkey:
                    hotkey_dict[hotkey] = getattr(self, action, None)
        if hotkey_dict:
            try:
                self.hotkey_listener = GlobalHotKeys(hotkey_dict)
                self.hotkey_listener.start()
            except Exception as e:
                QMessageBox.warning(self, "Warning", f"Failed to register hotkeys: {e}")

    def parse_hotkey(self, key_str):
        # Convert string like "<ctrl>+<alt>+r" to the format pynput expects
        return key_str.replace('<', '').replace('>', '').replace(' ', '').lower()

    def load_settings(self):
        if not os.path.exists(self.settings.save_path):
            os.makedirs(self.settings.save_path)
        if os.path.exists("settings.json"):
            try:
                with open("settings.json", "r") as f:
                    data = json.load(f)
                    self.settings.playback_speed = data.get("playback_speed", 1.0)
                    self.settings.loop = data.get("loop", False)
                    self.settings.save_path = data.get("save_path", "macros")
                    self.settings.hotkeys = data.get("hotkeys", {})
                    self.settings.schedule_playbacks = data.get("schedule_playbacks", {})
            except Exception as e:
                QMessageBox.warning(self, "Warning", f"Failed to load settings: {e}")
        else:
            # Create default settings.json
            self.save_settings_to_file()

    def save_settings_to_file(self):
        data = {
            "playback_speed": self.settings.playback_speed,
            "loop": self.settings.loop,
            "save_path": self.settings.save_path,
            "hotkeys": self.settings.hotkeys,
            "schedule_playbacks": self.settings.schedule_playbacks
        }
        try:
            with open("settings.json", "w") as f:
                json.dump(data, f, indent=4)
        except Exception as e:
            QMessageBox.warning(self, "Warning", f"Failed to save settings: {e}")

    def start_schedules(self):
        for macro_name, schedule in self.settings.schedule_playbacks.items():
            t = threading.Thread(target=self.schedule_playback, args=(macro_name, schedule["time"], schedule["repeat"]))
            t.daemon = True
            t.start()
            self.schedule_threads.append(t)

    def schedule_playback(self, macro_name, time_str, repeat):
        while True:
            now = time.strftime("%H:%M:%S", time.localtime())
            if now == time_str:
                macro = next((m for m in self.macros if m.name == macro_name), None)
                if macro:
                    self.play_macro_threaded(macro)
            time.sleep(1)
            if not repeat:
                break

    def start_recording(self):
        if self.playing:
            QMessageBox.warning(self, "Warning", "Cannot record while playing back.")
            return
        if self.recording:
            QMessageBox.warning(self, "Warning", "Already recording.")
            return
        name, ok = QInputDialog.getText(self, "Macro Name", "Enter name for the macro:")
        if not ok or not name:
            return
        if any(m.name == name for m in self.macros):
            QMessageBox.warning(self, "Warning", "Macro with this name already exists.")
            return
        self.current_macro = Macro(name)
        self.macros.append(self.current_macro)
        self.update_macro_list()
        self.recording = True
        self.status_label.setText("Status: Recording")
        self.mouse_listener = MouseListener(on_move=self.on_move, on_click=self.on_click)
        self.keyboard_listener = KeyboardListener(on_press=self.on_press, on_release=self.on_release)
        self.start_time = time.time()
        self.last_time = self.start_time
        self.mouse_listener.start()
        self.keyboard_listener.start()

    def stop_action(self):
        if self.recording:
            self.stop_recording()
        elif self.playing:
            self.stop_playback()

    def stop_recording(self):
        self.recording = False
        if hasattr(self, 'mouse_listener'):
            self.mouse_listener.stop()
        if hasattr(self, 'keyboard_listener'):
            self.keyboard_listener.stop()
        self.status_label.setText("Status: Idle")
        self.save_settings_to_file()

    def on_move(self, x, y):
        if not self.recording:
            return
        current_time = time.time()
        delay = (current_time - self.last_time) * 1000  # in milliseconds
        event = MacroEvent("move", delay, {"x": x, "y": y})
        self.current_macro.events.append(event)
        self.last_time = current_time

    def on_click(self, x, y, button, pressed):
        if not self.recording:
            return
        current_time = time.time()
        delay = (current_time - self.last_time) * 1000  # in milliseconds
        event_type = "click_down" if pressed else "click_up"
        event = MacroEvent(event_type, delay, {"button": button.name, "x": x, "y": y})
        self.current_macro.events.append(event)
        self.last_time = current_time

    def on_press(self, key):
        if not self.recording:
            return
        current_time = time.time()
        delay = (current_time - self.last_time) * 1000  # in milliseconds
        key_str = str(key).replace('Key.', '').lower() if isinstance(key, Key) else str(key).replace("'", "").lower()
        event = MacroEvent("key_down", delay, {"key": key_str})
        self.current_macro.events.append(event)
        self.last_time = current_time

    def on_release(self, key):
        if not self.recording:
            return
        current_time = time.time()
        delay = (current_time - self.last_time) * 1000  # in milliseconds
        key_str = str(key).replace('Key.', '').lower() if isinstance(key, Key) else str(key).replace("'", "").lower()
        event = MacroEvent("key_up", delay, {"key": key_str})
        self.current_macro.events.append(event)
        self.last_time = current_time

    def start_playback(self):
        if self.playing:
            QMessageBox.warning(self, "Warning", "Already playing a macro.")
            return
        selected_items = self.macro_list.selectedItems()
        if not selected_items:
            QMessageBox.warning(self, "Warning", "No macro selected.")
            return
        macro_name = selected_items[0].text()
        macro = next((m for m in self.macros if m.name == macro_name), None)
        if not macro:
            QMessageBox.warning(self, "Warning", "Selected macro not found.")
            return
        self.playing = True
        self.status_label.setText(f"Status: Playing '{macro.name}'")
        t = threading.Thread(target=self.play_macro, args=(macro,))
        t.start()

    def play_macro_threaded(self, macro):
        # Start playback in a new thread
        if self.playing:
            # Optionally, handle multiple playbacks
            return
        self.playing = True
        self.status_label.setText(f"Status: Playing '{macro.name}'")
        t = threading.Thread(target=self.play_macro, args=(macro,))
        t.start()

    def play_macro(self, macro):
        try:
            while self.playing:
                for event in macro.events:
                    if not self.playing:
                        break
                    time.sleep(event.time_delay / 1000.0 / self.settings.playback_speed)
                    if event.event_type == "move":
                        self.mouse_controller.position = (event.details["x"], event.details["y"])
                    elif event.event_type == "click_down":
                        self.mouse_controller.position = (event.details["x"], event.details["y"])
                        self.mouse_controller.press(Button[event.details["button"]])
                    elif event.event_type == "click_up":
                        self.mouse_controller.position = (event.details["x"], event.details["y"])
                        self.mouse_controller.release(Button[event.details["button"]])
                    elif event.event_type == "key_down":
                        try:
                            key = getattr(Key, event.details["key"])
                        except AttributeError:
                            key = event.details["key"]
                        self.kb_controller.press(key)
                    elif event.event_type == "key_up":
                        try:
                            key = getattr(Key, event.details["key"])
                        except AttributeError:
                            key = event.details["key"]
                        self.kb_controller.release(key)
                if not self.settings.loop:
                    break
        except Exception as e:
            QMessageBox.warning(self, "Warning", f"Error during playback: {e}")
        finally:
            self.playing = False
            self.status_label.setText("Status: Idle")

    def stop_playback(self):
        if self.playing:
            self.playing = False
            self.status_label.setText("Status: Stopped")

    def save_macro(self):
        selected_items = self.macro_list.selectedItems()
        if not selected_items:
            QMessageBox.warning(self, "Warning", "No macro selected.")
            return
        macro_name = selected_items[0].text()
        macro = next((m for m in self.macros if m.name == macro_name), None)
        if not macro:
            QMessageBox.warning(self, "Warning", "Selected macro not found.")
            return
        file_path, _ = QFileDialog.getSaveFileName(self, "Save Macro", self.settings.save_path, "JSON Files (*.json)")
        if file_path:
            data = []
            for event in macro.events:
                detail = event.details.copy()
                data.append({
                    "event_type": event.event_type,
                    "time_delay": event.time_delay,
                    "details": detail
                })
            try:
                with open(file_path, "w") as f:
                    json.dump(data, f, indent=4)
                QMessageBox.information(self, "Saved", f"Macro saved to {file_path}")
            except Exception as e:
                QMessageBox.warning(self, "Warning", f"Failed to save macro: {e}")

    def load_macro(self):
        file_path, _ = QFileDialog.getOpenFileName(self, "Load Macro", self.settings.save_path, "JSON Files (*.json)")
        if file_path:
            try:
                with open(file_path, "r") as f:
                    data = json.load(f)
                name, ok = QInputDialog.getText(self, "Macro Name", "Enter name for the loaded macro:")
                if not ok or not name:
                    return
                if any(m.name == name for m in self.macros):
                    QMessageBox.warning(self, "Warning", "Macro with this name already exists.")
                    return
                macro = Macro(name)
                for item in data:
                    details = item["details"]
                    # No need to convert button and key here since playback handles it
                    event = MacroEvent(item["event_type"], item["time_delay"], details)
                    macro.events.append(event)
                self.macros.append(macro)
                self.update_macro_list()
                QMessageBox.information(self, "Loaded", f"Macro '{name}' loaded successfully.")
            except Exception as e:
                QMessageBox.warning(self, "Warning", f"Failed to load macro: {e}")

    def delete_macro(self):
        selected_items = self.macro_list.selectedItems()
        if not selected_items:
            QMessageBox.warning(self, "Warning", "No macro selected.")
            return
        macro_name = selected_items[0].text()
        macro = next((m for m in self.macros if m.name == macro_name), None)
        if macro:
            reply = QMessageBox.question(self, 'Delete Macro', f"Are you sure you want to delete macro '{macro_name}'?",
                                         QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
            if reply == QMessageBox.Yes:
                self.macros.remove(macro)
                self.update_macro_list()

    def export_macro(self):
        self.save_macro()

    def import_macro(self):
        self.load_macro()

    def update_macro_list(self):
        self.macro_list.clear()
        for macro in self.macros:
            self.macro_list.addItem(macro.name)

    def open_settings(self):
        dialog = SettingsDialog(self.settings, self)
        if dialog.exec_():
            # Update settings from dialog
            self.settings.playback_speed = self.settings.playback_speed
            self.settings.loop = self.settings.loop
            self.settings.save_path = self.settings.save_path
            self.settings.hotkeys = self.settings.hotkeys
            self.settings.schedule_playbacks = self.settings.schedule_playbacks
            self.save_settings_to_file()
            self.register_hotkeys()

    def save_settings_to_file(self):
        data = {
            "playback_speed": self.settings.playback_speed,
            "loop": self.settings.loop,
            "save_path": self.settings.save_path,
            "hotkeys": self.settings.hotkeys,
            "schedule_playbacks": self.settings.schedule_playbacks
        }
        try:
            with open("settings.json", "w") as f:
                json.dump(data, f, indent=4)
        except Exception as e:
            QMessageBox.warning(self, "Warning", f"Failed to save settings: {e}")


def main():
    app = QtWidgets.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main() update the code with the full layout being better not like that strucuitee perfect layout with all filled next fix that buttons not full text visabkle and then do all in the gui implemented and all buttons working doing there actiosn do also th e real system actrive monitoring logics and actrivr macros total runtime and sucess rate real one okay that i monitors it all