import sys
import os
import json
import subprocess
from pathlib import Path
from datetime import datetime
from PySide6.QtWidgets import (
    QApplication, QMainWindow, QTreeWidget, QTreeWidgetItem, QVBoxLayout,
    QHBoxLayout, QWidget, QPushButton, QFileDialog, QMenu, QProgressBar,
    QLabel, QHeaderView, QLineEdit, QSplitter, QTextEdit, QCheckBox,
    QTabWidget, QTableWidget, QTableWidgetItem, QMessageBox, QFileIconProvider
)
from PySide6.QtGui import QIcon, QPalette, QColor, QFont
from PySide6.QtCore import Qt, QThread, Signal, QTimer, QFileInfo
import sys

CREATE_NO_WINDOW = 0x08000000 if sys.platform == "win32" else 0

icon_provider = QFileIconProvider()

def get_icon_for_name(name: str, is_dir: bool):
    if is_dir:
        return icon_provider.icon(QFileIconProvider.IconType.Folder)

    icon = icon_provider.icon(QFileInfo(name))

    if icon.isNull():
        icon = icon_provider.icon(QFileIconProvider.IconType.File)
    return icon

def setup_rclone_remote():
    try:
        subprocess.run([
            'rclone', 'config', 'create', 'myrient', 'http',
            'url=https://myrient.erista.me/files/'
        ], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, creationflags=CREATE_NO_WINDOW)
    except (subprocess.CalledProcessError, FileNotFoundError):
        pass


class GlobalSearchWorker(QThread):
    result_found = Signal(dict)
    finished_search = Signal(int)
    error = Signal(str)

    def __init__(self, search_term, search_path=""):
        super().__init__()
        self.search_term = search_term.lower().strip()
        self.search_path = search_path
        self.results_count = 0
        self.should_stop = False

    def run(self):
        try:
            self.search_recursive(self.search_path)
            self.finished_search.emit(self.results_count)
        except Exception as e:
            self.error.emit(f"Search error: {str(e)}")

    def search_recursive(self, path):
        if self.should_stop:
            return

        try:
            result = subprocess.run(
                ['rclone', 'lsjson', f'myrient:{path}'],
                capture_output=True, text=True, check=True, timeout=120,
                encoding='utf-8', errors='replace', creationflags=CREATE_NO_WINDOW
            )
            contents = json.loads(result.stdout)

            for entry in contents:
                if self.should_stop:
                    return

                name = entry['Name']
                is_dir = entry['IsDir']
                full_path = f"{path}/{name}" if path else name

                if self.search_term in name.lower():
                    self.result_found.emit({
                        'name': name,
                        'path': full_path,
                        'is_dir': is_dir,
                        'size': entry.get('Size', -1)
                    })
                    self.results_count += 1

                if is_dir:
                    self.search_recursive(full_path)

        except subprocess.TimeoutExpired:
            pass
        except Exception:
            pass

    def stop(self):
        self.should_stop = True


class LoadWorker(QThread):
    data_ready = Signal(list, object)
    error = Signal(str)

    def __init__(self, path, parent_item=None):
        super().__init__()
        self.path = path
        self.parent_item = parent_item

    def run(self):
        try:
            result = subprocess.run(
                ['rclone', 'lsjson', f'myrient:{self.path}'],
                capture_output=True, text=True, check=True, timeout=120,
                encoding='utf-8', errors='replace', creationflags=CREATE_NO_WINDOW
            )
            contents = json.loads(result.stdout)
            self.data_ready.emit(contents, self.parent_item)
        except subprocess.TimeoutExpired:
            self.error.emit(f"Timeout loading {self.path} (try again or check connection)")
        except Exception as e:
            self.error.emit(f"Error loading {self.path}: {str(e)}")


class SizeWorker(QThread):
    size_ready = Signal(int, object)
    error = Signal(str)

    def __init__(self, path, item):
        super().__init__()
        self.path = path
        self.item = item

    def run(self):
        try:
            result = subprocess.run(
                ['rclone', 'size', f'myrient:{self.path}', '--json'],
                capture_output=True, text=True, check=True, timeout=120,
                encoding='utf-8', errors='replace', creationflags=CREATE_NO_WINDOW
            )
            data = json.loads(result.stdout)
            self.size_ready.emit(data['bytes'], self.item)
        except Exception as e:
            self.error.emit(f"Error calculating size: {str(e)}")


class DownloadWorker(QThread):
    progress = Signal(str, int, str)
    finished = Signal(str)
    error = Signal(str, str)

    def __init__(self, download_id, remote_path, local_path, item_name):
        super().__init__()
        self.download_id = download_id
        self.remote_path = remote_path
        self.local_path = local_path
        self.item_name = item_name
        self.should_stop = False

    def run(self):
        try:
            process = subprocess.Popen(
                ['rclone', 'copy', f'myrient:{self.remote_path}',
                 self.local_path, '--progress', '--transfers', '4'],
                stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True,
                encoding='utf-8', errors='replace', creationflags=CREATE_NO_WINDOW
            )

            last_progress = 0
            while True:
                if self.should_stop:
                    process.terminate()
                    self.error.emit(self.download_id, "Download cancelled")
                    return

                line = process.stderr.readline()
                if not line and process.poll() is not None:
                    break
                if 'Transferred:' in line and '%' in line:
                    try:
                        parts = line.split(',')
                        pct_part = [p for p in parts if '%' in p][0]
                        pct = int(pct_part.split('%')[0].split()[-1])
                        speed = parts[2].strip() if len(parts) > 2 else ""
                        if pct != last_progress:
                            last_progress = pct
                            self.progress.emit(self.download_id, pct, speed)
                    except:
                        pass

            if process.returncode == 0:
                self.finished.emit(self.download_id)
            else:
                stderr = process.stderr.read()
                self.error.emit(self.download_id, f"Failed: {stderr[:200]}")
        except Exception as e:
            self.error.emit(self.download_id, f"Error: {str(e)}")

    def stop(self):
        self.should_stop = True


class MyrientBrowser(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Myrient Browser")
        self.resize(1400, 800)

        self.load_workers = []
        self.download_workers = {}
        self.size_workers = []
        self.search_worker = None
        self.download_queue = []
        self.next_download_id = 0

        self.setup_ui()
        self.load_top_level()

    def setup_ui(self):
        central = QWidget()
        self.setCentralWidget(central)
        main_layout = QVBoxLayout(central)
        main_layout.setContentsMargins(8, 8, 8, 8)
        main_layout.setSpacing(8)

        tabs = QTabWidget()

        browse_tab = self.create_browse_tab()
        tabs.addTab(browse_tab, "Browse")

        downloads_tab = self.create_downloads_tab()
        tabs.addTab(downloads_tab, "Downloads")

        search_tab = self.create_search_tab()
        tabs.addTab(search_tab, "Global Search")

        main_layout.addWidget(tabs)

        self.status_label = QLabel("Ready")
        self.statusBar().addWidget(self.status_label)

    def create_browse_tab(self):
        widget = QWidget()
        layout = QVBoxLayout(widget)
        layout.setContentsMargins(8, 8, 8, 8)

        toolbar = QHBoxLayout()

        self.filter_box = QLineEdit()
        self.filter_box.setPlaceholderText("Filter current view...")
        self.filter_box.textChanged.connect(self.filter_tree)
        toolbar.addWidget(QLabel("Filter:"))
        toolbar.addWidget(self.filter_box)

        self.dirs_only_check = QCheckBox("Folders only")
        self.dirs_only_check.stateChanged.connect(self.filter_tree)
        toolbar.addWidget(self.dirs_only_check)

        refresh_btn = QPushButton("Refresh")
        refresh_btn.clicked.connect(self.refresh_current)
        toolbar.addWidget(refresh_btn)

        layout.addLayout(toolbar)

        splitter = QSplitter(Qt.Horizontal)

        self.tree = QTreeWidget()
        self.tree.setHeaderLabels(["Name", "Size", "Type"])
        self.tree.setSortingEnabled(True)
        self.tree.header().setSectionResizeMode(0, QHeaderView.Stretch)
        self.tree.header().setSectionResizeMode(1, QHeaderView.ResizeToContents)
        self.tree.header().setSectionResizeMode(2, QHeaderView.ResizeToContents)
        self.tree.setAlternatingRowColors(True)
        self.tree.itemExpanded.connect(self.on_expand)
        self.tree.setContextMenuPolicy(Qt.CustomContextMenu)
        self.tree.customContextMenuRequested.connect(self.show_context_menu)
        self.tree.itemSelectionChanged.connect(self.update_info_panel)
        self.tree.setUniformRowHeights(True)
        self.tree.setSelectionMode(QTreeWidget.ExtendedSelection)
        splitter.addWidget(self.tree)

        info_panel = QWidget()
        info_layout = QVBoxLayout(info_panel)
        info_layout.setContentsMargins(4, 4, 4, 4)

        info_layout.addWidget(QLabel("Selection Info"))
        self.info_text = QTextEdit()
        self.info_text.setReadOnly(True)
        self.info_text.setMaximumHeight(150)
        info_layout.addWidget(self.info_text)

        self.download_btn = QPushButton("Add to Download Queue")
        self.download_btn.clicked.connect(self.add_to_queue)
        info_layout.addWidget(self.download_btn)

        info_layout.addStretch()
        splitter.addWidget(info_panel)
        splitter.setStretchFactor(0, 3)
        splitter.setStretchFactor(1, 1)

        layout.addWidget(splitter)
        return widget

    def create_downloads_tab(self):
        widget = QWidget()
        layout = QVBoxLayout(widget)
        layout.setContentsMargins(8, 8, 8, 8)

        toolbar = QHBoxLayout()

        self.dest_folder_label = QLabel("Download to: Not set")
        toolbar.addWidget(self.dest_folder_label)

        select_dest_btn = QPushButton("Select Folder")
        select_dest_btn.clicked.connect(self.select_download_folder)
        toolbar.addWidget(select_dest_btn)

        toolbar.addStretch()

        self.start_downloads_btn = QPushButton("Start Downloads")
        self.start_downloads_btn.clicked.connect(self.start_downloads)
        self.start_downloads_btn.setEnabled(False)
        toolbar.addWidget(self.start_downloads_btn)

        clear_completed_btn = QPushButton("Clear Completed")
        clear_completed_btn.clicked.connect(self.clear_completed)
        toolbar.addWidget(clear_completed_btn)

        layout.addLayout(toolbar)

        self.downloads_table = QTableWidget()
        self.downloads_table.setColumnCount(5)
        self.downloads_table.setHorizontalHeaderLabels(["Name", "Status", "Progress", "Speed", "Action"])
        self.downloads_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
        self.downloads_table.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeToContents)
        self.downloads_table.horizontalHeader().setSectionResizeMode(2, QHeaderView.ResizeToContents)
        self.downloads_table.horizontalHeader().setSectionResizeMode(3, QHeaderView.ResizeToContents)
        self.downloads_table.horizontalHeader().setSectionResizeMode(4, QHeaderView.ResizeToContents)
        self.downloads_table.setAlternatingRowColors(True)
        self.downloads_table.setSelectionBehavior(QTableWidget.SelectRows)
        layout.addWidget(self.downloads_table)

        return widget

    def create_search_tab(self):
        widget = QWidget()
        layout = QVBoxLayout(widget)
        layout.setContentsMargins(8, 8, 8, 8)

        search_bar = QHBoxLayout()

        self.global_search_box = QLineEdit()
        self.global_search_box.setPlaceholderText("Search entire remote (wildcard search)...")
        self.global_search_box.returnPressed.connect(self.start_global_search)
        search_bar.addWidget(self.global_search_box)

        self.search_btn = QPushButton("Search")
        self.search_btn.clicked.connect(self.start_global_search)
        search_bar.addWidget(self.search_btn)

        self.stop_search_btn = QPushButton("Stop")
        self.stop_search_btn.clicked.connect(self.stop_global_search)
        self.stop_search_btn.setEnabled(False)
        search_bar.addWidget(self.stop_search_btn)

        layout.addLayout(search_bar)

        self.search_status = QLabel("Enter a search term and press Search (searches files and folders)")
        layout.addWidget(self.search_status)

        self.search_results_table = QTableWidget()
        self.search_results_table.setColumnCount(4)
        self.search_results_table.setHorizontalHeaderLabels(["Name", "Path", "Type", "Size"])
        self.search_results_table.setSortingEnabled(True)
        self.search_results_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeToContents)
        self.search_results_table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
        self.search_results_table.horizontalHeader().setSectionResizeMode(2, QHeaderView.ResizeToContents)
        self.search_results_table.horizontalHeader().setSectionResizeMode(3, QHeaderView.ResizeToContents)
        self.search_results_table.setAlternatingRowColors(True)
        self.search_results_table.setSelectionBehavior(QTableWidget.SelectRows)
        self.search_results_table.setSelectionMode(QTableWidget.ExtendedSelection)
        self.search_results_table.setContextMenuPolicy(Qt.CustomContextMenu)
        self.search_results_table.customContextMenuRequested.connect(self.show_search_context_menu)
        layout.addWidget(self.search_results_table)

        search_toolbar = QHBoxLayout()
        add_selected_btn = QPushButton("Add Selected to Queue")
        add_selected_btn.clicked.connect(self.add_search_selected_to_queue)
        search_toolbar.addWidget(add_selected_btn)
        search_toolbar.addStretch()
        layout.addLayout(search_toolbar)

        return widget

    def load_top_level(self):
        self.status_label.setText("Loading root folders...")
        worker = LoadWorker("")
        worker.data_ready.connect(self.populate_top_level)
        worker.error.connect(self.show_error)
        self.load_workers.append(worker)
        worker.start()

    def populate_top_level(self, contents, _):
        self.tree.clear()

        for entry in sorted(contents, key=lambda x: x['Name'].lower()):
            if not entry['IsDir']:
                continue

            name = entry['Name']
            item = QTreeWidgetItem([name, "", "Folder"])
            item.setData(0, Qt.UserRole, {
                "path": name,
                "is_dir": True,
                "size": -1,
                "loaded": False
            })
            item.setIcon(0, get_icon_for_name(name, True))

            dummy = QTreeWidgetItem(["..."])
            dummy.setFlags(Qt.NoItemFlags)
            item.addChild(dummy)

            self.tree.addTopLevelItem(item)

        self.status_label.setText(f"Loaded {self.tree.topLevelItemCount()} folders")

    def on_expand(self, item):
        data = item.data(0, Qt.UserRole)
        if not data or data.get("loaded"):
            return

        if item.childCount() > 0 and item.child(0).text(0) == "...":
            item.takeChild(0)

        loading_item = QTreeWidgetItem(["Loading..."])
        loading_item.setFlags(Qt.NoItemFlags)
        item.addChild(loading_item)

        path = data["path"]
        self.status_label.setText(f"Loading {path}... (large folders may take a minute)")

        worker = LoadWorker(path, item)
        worker.data_ready.connect(self.populate_folder)
        worker.error.connect(self.show_error)
        self.load_workers.append(worker)
        worker.start()

    def populate_folder(self, contents, item):
        if not item:
            return

        while item.childCount() > 0:
            item.takeChild(0)

        data = item.data(0, Qt.UserRole)
        data["loaded"] = True
        item.setData(0, Qt.UserRole, data)

        if not contents:
            empty = QTreeWidgetItem(["(empty)"])
            empty.setFlags(Qt.NoItemFlags)
            item.addChild(empty)
            self.status_label.setText("Folder is empty")
            return

        parent_path = data["path"]
        sorted_contents = sorted(contents, key=lambda x: (not x['IsDir'], x['Name'].lower()))

        for entry in sorted_contents:
            name = entry['Name']
            is_dir = entry['IsDir']
            size = entry.get('Size', -1)
            full_path = f"{parent_path}/{name}" if parent_path else name

            size_str = self.format_size(size) if size >= 0 else ""
            type_str = "Folder" if is_dir else self.get_file_type(name)

            child = QTreeWidgetItem([name, size_str, type_str])
            child.setData(0, Qt.UserRole, {
                "path": full_path,
                "is_dir": is_dir,
                "size": size,
                "loaded": False
            })
            child.setData(1, Qt.UserRole, size)
            if is_dir:
                dummy = QTreeWidgetItem(["..."])
                dummy.setFlags(Qt.NoItemFlags)
                child.addChild(dummy)

            child.setIcon(0, get_icon_for_name(name, is_dir))
            item.addChild(child)

        self.status_label.setText(f"Loaded {len(contents)} items")

    def show_context_menu(self, position):
        item = self.tree.itemAt(position)
        if not item:
            return

        data = item.data(0, Qt.UserRole)
        if not data:
            return

        menu = QMenu()

        if data["is_dir"]:
            calc_action = menu.addAction("Calculate Size")
            calc_action.triggered.connect(lambda: self.calculate_size(item))

        download_action = menu.addAction("Add to Queue")
        download_action.triggered.connect(lambda: self.add_item_to_queue(item))

        copy_path_action = menu.addAction("Copy Path")
        copy_path_action.triggered.connect(lambda: QApplication.clipboard().setText(data["path"]))

        menu.exec(self.tree.viewport().mapToGlobal(position))

    def show_search_context_menu(self, position):
        row = self.search_results_table.rowAt(position.y())
        if row < 0:
            return

        menu = QMenu()

        download_action = menu.addAction("Add to Queue")
        download_action.triggered.connect(lambda: self.add_search_result_to_queue(row))

        copy_path_action = menu.addAction("Copy Path")
        copy_path_action.triggered.connect(lambda: QApplication.clipboard().setText(
            self.search_results_table.item(row, 1).data(Qt.UserRole)
        ))

        menu.exec(self.search_results_table.viewport().mapToGlobal(position))

    def calculate_size(self, item):
        data = item.data(0, Qt.UserRole)
        path = data["path"]

        self.status_label.setText(f"Calculating size for {path}...")

        worker = SizeWorker(path, item)
        worker.size_ready.connect(self.update_size)
        worker.error.connect(self.show_error)
        self.size_workers.append(worker)
        worker.start()

    def update_size(self, size_bytes, item):
        item.setText(1, self.format_size(size_bytes))
        item.setData(1, Qt.UserRole, size_bytes)
        data = item.data(0, Qt.UserRole)
        data["size"] = size_bytes
        item.setData(0, Qt.UserRole, data)
        self.status_label.setText(f"Size: {self.format_size(size_bytes)}")

    def add_to_queue(self):
        selected = self.tree.selectedItems()
        if not selected:
            self.status_label.setText("No items selected")
            return

        for item in selected:
            self.add_item_to_queue(item)

    def add_item_to_queue(self, item):
        data = item.data(0, Qt.UserRole)
        if not data:
            return

        download_info = {
            'id': f"dl_{self.next_download_id}",
            'name': item.text(0),
            'path': data['path'],
            'is_dir': data['is_dir'],
            'size': data['size'],
            'status': 'Queued',
            'progress': 0,
            'speed': '',
            'worker': None
        }
        self.next_download_id += 1

        self.download_queue.append(download_info)
        self.add_download_to_table(download_info)
        self.status_label.setText(f"Added {item.text(0)} to queue")

    def add_search_selected_to_queue(self):
        selected = self.search_results_table.selectedItems()
        if not selected:
            return

        rows = set(item.row() for item in selected)
        for row in rows:
            self.add_search_result_to_queue(row)

    def add_search_result_to_queue(self, row):
        name = self.search_results_table.item(row, 0).text()
        path = self.search_results_table.item(row, 1).data(Qt.UserRole)
        is_dir = self.search_results_table.item(row, 2).text() == "Folder"
        size = self.search_results_table.item(row, 3).data(Qt.UserRole)

        download_info = {
            'id': f"dl_{self.next_download_id}",
            'name': name,
            'path': path,
            'is_dir': is_dir,
            'size': size if size else -1,
            'status': 'Queued',
            'progress': 0,
            'speed': '',
            'worker': None
        }
        self.next_download_id += 1

        self.download_queue.append(download_info)
        self.add_download_to_table(download_info)
        self.status_label.setText(f"Added {name} to queue")

    def add_download_to_table(self, download_info):
        row = self.downloads_table.rowCount()
        self.downloads_table.insertRow(row)

        name_item = QTableWidgetItem(download_info['name'])
        name_item.setData(Qt.UserRole, download_info['id'])
        self.downloads_table.setItem(row, 0, name_item)

        self.downloads_table.setItem(row, 1, QTableWidgetItem(download_info['status']))
        self.downloads_table.setItem(row, 2, QTableWidgetItem("0%"))
        self.downloads_table.setItem(row, 3, QTableWidgetItem(""))

        cancel_btn = QPushButton("Remove")
        cancel_btn.clicked.connect(lambda: self.remove_from_queue(download_info['id']))
        self.downloads_table.setCellWidget(row, 4, cancel_btn)

    def select_download_folder(self):
        folder = QFileDialog.getExistingDirectory(self, "Select Download Folder")
        if folder:
            self.download_folder = folder
            self.dest_folder_label.setText(f"Download to: {folder}")
            self.start_downloads_btn.setEnabled(len(self.download_queue) > 0)

    def start_downloads(self):
        if not hasattr(self, 'download_folder'):
            QMessageBox.warning(self, "No Folder", "Please select a download folder first")
            return

        if not self.download_queue:
            return

        self.start_downloads_btn.setEnabled(False)

        for download_info in self.download_queue:
            if download_info['status'] == 'Queued':
                self.start_single_download(download_info)

    def start_single_download(self, download_info):
        download_info['status'] = 'Downloading'
        self.update_download_status(download_info['id'], 'Downloading', 0, '')

        worker = DownloadWorker(
            download_info['id'],
            download_info['path'],
            self.download_folder,
            download_info['name']
        )
        worker.progress.connect(self.update_download_progress)
        worker.finished.connect(self.download_finished)
        worker.error.connect(self.download_error)

        download_info['worker'] = worker
        self.download_workers[download_info['id']] = worker
        worker.start()

    def update_download_progress(self, download_id, percent, speed):
        for i, dl in enumerate(self.download_queue):
            if dl['id'] == download_id:
                dl['progress'] = percent
                dl['speed'] = speed
                self.update_download_row(i, 'Downloading', f"{percent}%", speed)
                break

    def download_finished(self, download_id):
        for i, dl in enumerate(self.download_queue):
            if dl['id'] == download_id:
                dl['status'] = 'Completed'
                self.update_download_row(i, 'Completed', '100%', '')
                self.status_label.setText(f"Downloaded: {dl['name']}")
                break

        if download_id in self.download_workers:
            del self.download_workers[download_id]

    def download_error(self, download_id, error_msg):
        for i, dl in enumerate(self.download_queue):
            if dl['id'] == download_id:
                dl['status'] = 'Failed'
                self.update_download_row(i, f"Failed: {error_msg}", '', '')
                break

        if download_id in self.download_workers:
            del self.download_workers[download_id]

    def update_download_status(self, download_id, status, progress, speed):
        for i, dl in enumerate(self.download_queue):
            if dl['id'] == download_id:
                self.update_download_row(i, status, f"{progress}%", speed)
                break

    def update_download_row(self, row, status, progress, speed):
        if row < self.downloads_table.rowCount():
            self.downloads_table.item(row, 1).setText(status)
            self.downloads_table.item(row, 2).setText(progress)
            self.downloads_table.item(row, 3).setText(speed)

    def remove_from_queue(self, download_id):
        for i, dl in enumerate(self.download_queue):
            if dl['id'] == download_id:
                if dl['worker']:
                    dl['worker'].stop()
                self.download_queue.pop(i)
                self.downloads_table.removeRow(i)
                self.status_label.setText(f"Removed from queue")
                break

    def clear_completed(self):
        i = 0
        while i < len(self.download_queue):
            if self.download_queue[i]['status'] in ['Completed', 'Failed']:
                self.download_queue.pop(i)
                self.downloads_table.removeRow(i)
            else:
                i += 1

    def start_global_search(self):
        search_term = self.global_search_box.text().strip()
        if not search_term:
            return

        self.search_results_table.setRowCount(0)
        self.search_status.setText(f"Searching for '{search_term}'...")
        self.search_btn.setEnabled(False)
        self.stop_search_btn.setEnabled(True)

        self.search_worker = GlobalSearchWorker(search_term)
        self.search_worker.result_found.connect(self.add_search_result)
        self.search_worker.finished_search.connect(self.search_complete)
        self.search_worker.error.connect(self.show_error)
        self.search_worker.start()

    def stop_global_search(self):
        if self.search_worker:
            self.search_worker.stop()
            self.search_status.setText("Search stopped")
            self.search_btn.setEnabled(True)
            self.stop_search_btn.setEnabled(False)

    def add_search_result(self, result):
        row = self.search_results_table.rowCount()
        self.search_results_table.insertRow(row)

        name_item = QTableWidgetItem(result['name'])
        name_item.setIcon(get_icon_for_name(result['name'], result['is_dir']))
        self.search_results_table.setItem(row, 0, name_item)

        path_item = QTableWidgetItem(result['path'])
        path_item.setData(Qt.UserRole, result['path'])
        self.search_results_table.setItem(row, 1, path_item)

        type_item = QTableWidgetItem("Folder" if result['is_dir'] else self.get_file_type(result['name']))
        self.search_results_table.setItem(row, 2, type_item)

        size_str = self.format_size(result['size']) if result['size'] >= 0 else ""
        size_item = QTableWidgetItem(size_str)
        size_item.setData(Qt.UserRole, result['size'])
        self.search_results_table.setItem(row, 3, size_item)

    def search_complete(self, count):
        self.search_status.setText(f"Search complete. Found {count} results.")
        self.search_btn.setEnabled(True)
        self.stop_search_btn.setEnabled(False)

    def show_error(self, msg):
        self.status_label.setText(f"Error: {msg}")

    def filter_tree(self):
        search_text = self.filter_box.text().lower()
        dirs_only = self.dirs_only_check.isChecked()

        def filter_recursive(item):
            visible = False
            data = item.data(0, Qt.UserRole)

            if data:
                name_match = search_text in item.text(0).lower()
                type_match = not dirs_only or data.get("is_dir", False)

                if name_match and type_match:
                    visible = True

            for i in range(item.childCount()):
                child = item.child(i)
                if filter_recursive(child):
                    visible = True

            item.setHidden(not visible)
            return visible

        for i in range(self.tree.topLevelItemCount()):
            filter_recursive(self.tree.topLevelItem(i))

    def refresh_current(self):
        selected = self.tree.selectedItems()
        if selected:
            item = selected[0]
            data = item.data(0, Qt.UserRole)
            if data and data.get("is_dir"):
                data["loaded"] = False
                item.setData(0, Qt.UserRole, data)
                while item.childCount() > 0:
                    item.takeChild(0)
                dummy = QTreeWidgetItem(["..."])
                dummy.setFlags(Qt.NoItemFlags)
                item.addChild(dummy)
                if item.isExpanded():
                    item.setExpanded(False)
                    item.setExpanded(True)
        else:
            self.load_top_level()

    def update_info_panel(self):
        selected = self.tree.selectedItems()
        if not selected:
            self.info_text.clear()
            return

        if len(selected) == 1:
            item = selected[0]
            data = item.data(0, Qt.UserRole)

            if not data:
                return

            info = f"<b>Name:</b> {item.text(0)}<br>"
            info += f"<b>Path:</b> {data['path']}<br>"
            info += f"<b>Type:</b> {'Folder' if data['is_dir'] else 'File'}<br>"

            if data['size'] >= 0:
                info += f"<b>Size:</b> {self.format_size(data['size'])}<br>"

            self.info_text.setHtml(info)
        else:
            info = f"<b>Selected:</b> {len(selected)} items<br>"
            total_size = sum(item.data(0, Qt.UserRole).get('size', 0) for item in selected if
                             item.data(0, Qt.UserRole).get('size', 0) > 0)
            if total_size > 0:
                info += f"<b>Total Size:</b> {self.format_size(total_size)}<br>"
            self.info_text.setHtml(info)

    @staticmethod
    def format_size(size):
        if size < 0:
            return ""
        units = ['B', 'KB', 'MB', 'GB', 'TB']
        for unit in units:
            if size < 1024.0:
                return f"{size:.1f} {unit}"
            size /= 1024.0
        return f"{size:.1f} PB"

    @staticmethod
    def get_file_type(filename):
        ext = Path(filename).suffix.lower()
        type_map = {
            '.zip': 'Archive', '.7z': 'Archive', '.rar': 'Archive',
            '.iso': 'Disc Image', '.cue': 'Disc Image', '.bin': 'Disc Image',
            '.gba': 'ROM', '.nds': 'ROM', '.nes': 'ROM', '.smc': 'ROM',
            '.sfc': 'ROM', '.n64': 'ROM', '.z64': 'ROM', '.gb': 'ROM',
            '.gbc': 'ROM', '.psx': 'ROM', '.ps2': 'ROM'
        }
        return type_map.get(ext, 'File')


def set_dark_theme(app):
    palette = QPalette()
    palette.setColor(QPalette.Window, QColor(30, 30, 30))
    palette.setColor(QPalette.WindowText, QColor(220, 220, 220))
    palette.setColor(QPalette.Base, QColor(25, 25, 25))
    palette.setColor(QPalette.AlternateBase, QColor(35, 35, 35))
    palette.setColor(QPalette.Text, QColor(220, 220, 220))
    palette.setColor(QPalette.Button, QColor(45, 45, 45))
    palette.setColor(QPalette.ButtonText, QColor(220, 220, 220))
    palette.setColor(QPalette.Highlight, QColor(66, 165, 245))
    palette.setColor(QPalette.HighlightedText, QColor(255, 255, 255))
    palette.setColor(QPalette.Link, QColor(66, 165, 245))
    app.setPalette(palette)
    app.setStyle("Fusion")


if __name__ == "__main__":
    setup_rclone_remote()
    app = QApplication(sys.argv)
    set_dark_theme(app)

    font = app.font()
    font.setPointSize(10)
    app.setFont(font)

    browser = MyrientBrowser()
    browser.show()
    sys.exit(app.exec())