"""
A graphical interface for managing CHD (Compressed Hunks of Data) files via chdman.
"""

import sys
import os
import importlib.metadata
from pathlib import Path
from PySide6 import QtWidgets
from PySide6.QtWidgets import (
    QApplication,
    QMainWindow,
    QWidget,
    QVBoxLayout,
    QHBoxLayout,
    QLabel,
    QLineEdit,
    QPushButton,
    QComboBox,
    QTextEdit,
    QGroupBox,
    QFormLayout,
    QSpinBox,
    QCheckBox,
    QProgressBar,
    QMessageBox,
)
from PySide6.QtCore import Qt, QProcess
from .config_handler import load_config, save_config
from .commands import CommandHandler
from .bulk_processing import BulkProcessor
from .settings import SettingsManager
from .utils import Utils


class ChdWarrden(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("ChdWarrden")
        self.setMinimumSize(800, 600)

        # Initialize config
        self.config = load_config()

        self.process = QProcess()
        self.process.readyReadStandardOutput.connect(self.handle_stdout)
        self.process.readyReadStandardError.connect(self.handle_stderr)
        self.process.finished.connect(self.process_finished)

        # Initialize modules
        self.command_handler = CommandHandler(self)
        self.bulk_processor = BulkProcessor(self)
        self.settings_manager = SettingsManager(self)
        self.utils = Utils(self)

        self.templates = []
        self.init_ui()
        self.settings_manager.check_chdman()
        self.fetch_templates()

    def fetch_templates(self):
        """Fetch available HD templates from chdman"""
        try:
            result = subprocess.run(
                ["chdman", "listtemplates"], capture_output=True, text=True
            )
            if result.returncode == 0:
                # Parse template names from output
                lines = result.stdout.strip().split("\n")
                self.templates = [
                    line.strip() for line in lines if line.strip() and ":" not in line
                ]
                self.statusBar().showMessage(
                    f"Loaded {len(self.templates)} HD templates"
                )
            else:
                self.statusBar().showMessage("Failed to load HD templates")
        except Exception as e:
            self.statusBar().showMessage(f"Error loading templates: {str(e)}")

    def init_ui(self):
        """Initialize the user interface"""
        # Create menus
        menubar = self.menuBar()
        settings_menu = menubar.addMenu("Settings")
        set_path_action = settings_menu.addAction("Set chdman Path")
        set_path_action.triggered.connect(self.settings_manager.show_settings_dialog)

        # Main layout
        central_widget = QWidget()
        main_layout = QVBoxLayout(central_widget)
        self.setCentralWidget(central_widget)

        # Command selection
        command_group = QGroupBox("Command")
        command_layout = QHBoxLayout()
        command_group.setLayout(command_layout)

        self.command_combo = QComboBox()
        commands = [
            "addmeta",
            "copy",
            "createcd",
            "createdvd",
            "createhd",
            "createld",
            "createraw",
            "delmeta",
            "dumpmeta",
            "extractcd",
            "extractdvd",
            "extracthd",
            "extractld",
            "extractraw",
            "info",
            "listtemplates",
            "verify",
        ]
        self.command_combo.addItems(commands)
        self.command_combo.setCurrentIndex(2)
        self.command_combo.currentIndexChanged.connect(self.update_form)
        command_layout.addWidget(self.command_combo)
        main_layout.addWidget(command_group)

        # Parameters section
        self.params_group = QGroupBox("Parameters")
        self.params_layout = QFormLayout()
        self.params_group.setLayout(self.params_layout)
        main_layout.addWidget(self.params_group)

        # Bulk processing option
        bulk_group = QGroupBox("Bulk Processing")
        bulk_layout = QVBoxLayout()
        bulk_group.setLayout(bulk_layout)

        self.bulk_checkbox = QCheckBox("Enable Bulk Processing")
        self.bulk_checkbox.stateChanged.connect(self.toggle_bulk_mode)

        bulk_layout.addWidget(self.bulk_checkbox)
        main_layout.addWidget(bulk_group)

        # Output section
        output_group = QGroupBox("Output")
        output_layout = QVBoxLayout()
        output_group.setLayout(output_layout)

        self.output_text = QTextEdit()
        self.output_text.setReadOnly(True)
        output_layout.addWidget(self.output_text)

        self.progress_bar = QProgressBar()
        self.progress_bar.setVisible(False)
        output_layout.addWidget(self.progress_bar)

        main_layout.addWidget(output_group)

        # Buttons
        button_layout = QHBoxLayout()

        self.run_button = QPushButton("Run")
        self.run_button.clicked.connect(self.run_command)

        self.clear_button = QPushButton("Clear Output")
        self.clear_button.clicked.connect(self.output_text.clear)

        button_layout.addWidget(self.run_button)
        button_layout.addWidget(self.clear_button)
        main_layout.addLayout(button_layout)

        # Initialize with the default command
        self.update_form()
        self.show()

    def toggle_bulk_mode(self, state):
        """Toggle bulk processing mode and update the input field accordingly"""
        self.update_form()  # Rebuild form based on bulk mode

    def update_form(self):
        """Update form fields based on selected command"""
        # Clear existing layout
        while self.params_layout.rowCount() > 0:
            self.params_layout.removeRow(0)

        command = self.command_combo.currentText()
        self.param_widgets = {}

        # Common parameters
        if command == "listtemplates":
            # No parameters needed for listtemplates
            pass
        elif self.bulk_checkbox.isChecked():
            self.utils.add_directory_param(
                self, "input", "Input Directory", required=True
            )
        else:
            self.utils.add_file_param(self, "input", "Input File", required=True)

        if command in ["createraw", "createhd", "createcd", "createdvd", "createld"]:
            self.utils.add_file_param(
                self, "output", "Output File (.chd)", True, is_save=True
            )
            self.add_delete_source_option()

            if command in ["createraw", "createhd"]:
                self.utils.add_int_param(
                    self, "hunksize", "Hunk Size", 4096, min_val=1024
                )
                self.utils.add_int_param(
                    self, "compression", "Compression (0-9)", 9, min_val=0, max_val=9
                )

                if command == "createhd":
                    self.utils.add_int_param(
                        self, "chs", "CHS Mode", 1, min_val=0, max_val=4
                    )
                    self.add_template_param()  # Add template selection for createhd

        elif command in [
            "extractraw",
            "extracthd",
            "extractcd",
            "extractdvd",
            "extractld",
        ]:
            self.utils.add_file_param(self, "output", "Output File", True, is_save=True)

            if command == "extractcd":
                self.utils.add_checkbox_param(self, "writebinonly", "Write BIN Only")
                self.utils.add_checkbox_param(self, "writetoconly", "Write TOC Only")

            elif command == "extractdvd":
                self.utils.add_checkbox_param(self, "writebinonly", "Write BIN Only")
                self.utils.add_checkbox_param(
                    self, "writeifosonly", "Write IFO/VOB Only"
                )

        elif command == "info":
            self.utils.add_checkbox_param(self, "verbose", "Verbose Output")

        elif command == "verify":
            pass  # Just requires input

        elif command == "dumpmeta":
            self.utils.add_file_param(
                self, "output", "Output XML File", True, is_save=True
            )
            self.utils.add_str_param(self, "tag", "Metadata Tag", "")
            self.utils.add_int_param(self, "index", "Index", 0, min_val=0)

        elif command == "copy":  # Renamed from copydata
            self.utils.add_file_param(
                self, "output", "Output File (.chd)", True, is_save=True
            )
            self.utils.add_int_param(
                self, "compression", "Compression (0-9)", 7, min_val=0, max_val=9
            )

        elif command == "addmeta":
            self.utils.add_str_param(self, "tag", "Metadata Tag", "")
            self.utils.add_int_param(self, "index", "Index", 0, min_val=0)
            self.utils.add_file_param(self, "valuefile", "Value File", False)
            self.utils.add_str_param(self, "valuetext", "Value Text", "")

        elif command == "delmeta":
            self.utils.add_str_param(self, "tag", "Metadata Tag", "")
            self.utils.add_int_param(self, "index", "Index", 0, min_val=0)

    def add_template_param(self):
        """Add a template selection dropdown for createhd command"""
        template_combo = QComboBox()
        template_combo.addItem("No Template")
        template_combo.addItems(self.templates)
        self.params_layout.addRow("Template:", template_combo)
        self.param_widgets["template"] = template_combo

    def add_delete_source_option(self):
        """Add an option to delete the source file after successful creation"""
        checkbox = QCheckBox("Delete Source File After Successful Run")
        self.params_layout.addRow(checkbox)
        self.param_widgets["delete_source"] = checkbox

    def run_command(self):
        """Run the chdman command with the specified parameters"""
        command = ["chdman", self.command_combo.currentText()]

        # Special case for listtemplates which doesn't need input
        if self.command_combo.currentText() == "listtemplates":
            self.run_templates_command()
            return

        if self.bulk_checkbox.isChecked():
            self.run_bulk_command(command)
        else:
            self.run_single_command(command)

    def run_templates_command(self):
        """Run the listtemplates command and display output"""
        command = ["chdman", "listtemplates"]
        self.output_text.append(f"Running: {' '.join(command)}\n")
        self.statusBar().showMessage("Running listtemplates command...")
        self.process.start(command[0], command[1:])

    def run_single_command(self, base_command):
        """Run a single chdman command"""
        # Validate required fields
        command_name = self.command_combo.currentText()

        if command_name != "listtemplates":
            input_value = self.param_widgets.get("input", QLineEdit()).text()
            if not input_value:
                QMessageBox.warning(self, "Warning", "Input file/directory is required")
                return

        if command_name in [
            "createraw",
            "createhd",
            "createcd",
            "createdvd",
            "createld",
            "extractraw",
            "extracthd",
            "extractcd",
            "extractdvd",
            "extractld",
            "dumpmeta",
            "copy",  # Renamed from copydata
        ]:
            output_value = self.param_widgets.get("output", QLineEdit()).text()
            if not output_value:
                # Try to auto-fill output if it's empty
                input_value = self.param_widgets.get("input", QLineEdit()).text()
                if input_value:
                    self.utils.update_output_path(self, input_value)
                    output_value = self.param_widgets.get("output", QLineEdit()).text()

            if not output_value:
                QMessageBox.warning(self, "Warning", "Output file is required")
                return

        # Build command arguments
        command = base_command.copy()
        for name, widget in self.param_widgets.items():
            # Skip delete_source as it's not a chdman parameter
            if name == "delete_source":
                continue

            # Handle template selection for createhd
            if name == "template" and isinstance(widget, QComboBox):
                template_text = widget.currentText()
                if template_text != "No Template":
                    command.append(f"--{name}")
                    command.append(template_text)
                continue

            if isinstance(widget, QLineEdit) and widget.text():
                command.append(f"--{name}")
                command.append(widget.text())
            elif isinstance(widget, QSpinBox):
                command.append(f"--{name}")
                command.append(str(widget.value()))
            elif isinstance(widget, QCheckBox) and widget.isChecked():
                command.append(f"--{name}")

        # Display the command
        cmd_str = " ".join(command)
        self.output_text.append(f"Running: {cmd_str}\n")
        self.statusBar().showMessage(f"Running {command[1]} command...")

        # Run the command
        self.process.start(command[0], command[1:])

    def run_bulk_command(self, base_command):
        """Run chdman command on multiple files in a directory"""
        directory = self.param_widgets.get("input", QLineEdit()).text()
        if not directory or not os.path.isdir(directory):
            QMessageBox.warning(
                self, "Warning", "Please select a valid directory for bulk processing."
            )
            return

        # Supported extensions based on command
        extensions = {
            "createraw": [".img", ".iso"],
            "createhd": [".img", ".iso"],
            "createcd": [".cue", ".iso"],
            "createdvd": [".iso"],
            "createld": [".cue", ".iso"],
            "extractraw": [".chd"],
            "extracthd": [".chd"],
            "extractcd": [".chd"],
            "extractdvd": [".chd"],
            "extractld": [".chd"],
            "verify": [".chd"],
            "info": [".chd"],
            "dumpmeta": [".chd"],
            "copy": [".chd"],  # Renamed from copydata
            "addmeta": [".chd"],
            "delmeta": [".chd"],
        }

        command_name = self.command_combo.currentText()
        supported_exts = extensions.get(command_name, [])
        files = [
            f
            for f in os.listdir(directory)
            if os.path.splitext(f)[1].lower() in supported_exts
        ]

        if not files:
            QMessageBox.warning(
                self,
                "Warning",
                f"No supported files found for '{command_name}' in the selected directory.",
            )
            return

        self.progress_bar.setMaximum(len(files))
        self.progress_bar.setValue(0)
        self.progress_bar.setVisible(True)

        self.bulk_files = [(os.path.join(directory, f), command_name) for f in files]
        self.bulk_index = 0
        self.run_next_bulk_command(base_command)

    def run_next_bulk_command(self, base_command):
        """Run the next command in the bulk queue"""
        if self.bulk_index >= len(self.bulk_files):
            self.progress_bar.setVisible(False)
            self.output_text.append("\nBulk processing completed.")
            return

        file_path, command_name = self.bulk_files[self.bulk_index]
        command = base_command.copy()
        command.append("--input")
        command.append(file_path)

        if command_name in [
            "createraw",
            "createhd",
            "createcd",
            "createdvd",
            "createld",
            "extractraw",
            "extracthd",
            "extractcd",
            "extractdvd",
            "extractld",
            "dumpmeta",
            "copy",  # Renamed from copydata
        ]:
            # Generate appropriate output filename based on command
            output_ext = self.utils.get_output_extension(command_name)
            output_path = os.path.splitext(file_path)[0] + output_ext
            command.append("--output")
            command.append(output_path)

        # Display the command
        cmd_str = " ".join(command)
        self.output_text.append(f"\nProcessing: {cmd_str}")
        self.statusBar().showMessage(
            f"Processing file {self.bulk_index + 1}/{len(self.bulk_files)}"
        )

        # Run the command
        self.process.start(command[0], command[1:])

    def handle_stdout(self):
        """Handle standard output from the process"""
        data = self.process.readAllStandardOutput().data().decode()
        self.output_text.append(data)

    def handle_stderr(self):
        """Handle standard error from the process"""
        data = self.process.readAllStandardError().data().decode()
        self.output_text.append(f"<span style='color:red'>{data}</span>")

    def process_finished(self, exit_code, exit_status):
        """Handle process completion"""
        command_name = self.command_combo.currentText()

        if exit_code == 0:
            self.statusBar().showMessage("Command completed successfully")
            self.output_text.append("\nCommand completed successfully")

            # Update templates list if this was a listtemplates command
            if command_name == "listtemplates":
                self.fetch_templates()

            # Delete source file if requested
            if (
                "delete_source" in self.param_widgets
                and not self.bulk_checkbox.isChecked()
            ):
                delete_source = self.param_widgets["delete_source"].isChecked()
                if delete_source:
                    input_file = self.param_widgets.get("input", QLineEdit()).text()
                    if os.path.exists(input_file):
                        # Special handling for .cue files to delete associated .bin files
                        if input_file.lower().endswith(".cue"):
                            try:
                                with open(input_file, "r") as f:
                                    for line in f:
                                        line = line.strip()
                                        if line.startswith("FILE"):
                                            # Parse the filename from the cue line
                                            parts = line.split('"')
                                            if len(parts) >= 2:
                                                bin_file = parts[1]
                                                bin_path = os.path.join(
                                                    os.path.dirname(input_file),
                                                    bin_file,
                                                )
                                                if os.path.exists(bin_path):
                                                    os.remove(bin_path)
                                                    self.output_text.append(
                                                        f"\nDeleted associated file: {bin_path}"
                                                    )
                            except Exception as e:
                                self.output_text.append(
                                    f"\n<span style='color:orange'>Warning: Could not parse .cue file: {str(e)}</span>"
                                )

                        # Delete the main input file
                        os.remove(input_file)
                        self.output_text.append(f"\nDeleted source file: {input_file}")

            if self.bulk_checkbox.isChecked():
                self.progress_bar.setValue(self.bulk_index + 1)
                self.bulk_index += 1
                self.run_next_bulk_command(["chdman", command_name])
        else:
            self.statusBar().showMessage(f"Command failed with exit code {exit_code}")
            self.output_text.append(
                f"\n<span style='color:red'>Command failed with exit code {exit_code}</span>"
            )


def main():
    # Find the name of the module that was used to start the app
    app_module = sys.modules["__main__"].__package__
    # Retrieve the app's metadata
    metadata = importlib.metadata.metadata(app_module)

    QtWidgets.QApplication.setApplicationName(metadata["Formal-Name"])

    app = QtWidgets.QApplication(sys.argv)
    main_window = ChdWarrden()
    sys.exit(app.exec())
