import os
import subprocess
import platform
import shutil
import sys
from typing import Self
from PySide6.QtWidgets import (
    QApplication, QMainWindow, QWidget, QPushButton, QVBoxLayout, QTabWidget, QLabel, QLineEdit, QFileDialog, QMessageBox, QFormLayout, QSpinBox, QComboBox, QHBoxLayout, QListWidget, QListWidgetItem, QTextEdit, 
)
from PySide6.QtGui import (QIcon, QPixmap
)
from PySide6.QtCore import Qt
from pathlib import Path

from core.openssl_wrapper import generate_root_ca
from core.openssl_wrapper import generate_certificate

openssl_path = os.path.join(os.getcwd(), "openssl", "openssl.exe")
print(f"Using OpenSSL path: {openssl_path}")

# Remove the problematic logo_label creation from module level
# logo_label = QLabel()  # This line was causing the error
# logo_label.setAlignment(Qt.AlignCenter)
def resource_path(relative_path):
    base_path = getattr(sys, '_MEIPASS', os.path.abspath("."))
    return os.path.join(base_path, relative_path)

def ensure_ca_directories(ca_name="local-root"):
    documents_path = os.path.join(os.path.expanduser("~"), "Documents")
    base_path = os.path.join(documents_path, "GC-Created-Certs", "ca", ca_name)

    paths_to_create = [
        base_path,
        os.path.join(base_path, "newcerts"),
        os.path.join(base_path, "crl")
    ]

    for path in paths_to_create:
        os.makedirs(path, exist_ok=True)

    required_files = {
        "index.txt": "",
        "serial": "1000\n",
        "crlnumber": "1000\n"
    }

    for filename, default_content in required_files.items():
        full_path = os.path.join(base_path, filename)
        if not os.path.exists(full_path):
            with open(full_path, "w") as f:
                f.write(default_content)

    print(f"✅ CA directory structure initialized at: {base_path}")
    return base_path


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

        # Set window icon using MEIPASS-compatible path
        icon_path = resource_path("assets/icons/app_icon.ico")
        self.setWindowIcon(QIcon(icon_path))
        self.setWindowTitle("Local CA Manager")
        self.setGeometry(300, 200, 600, 400)
        self.ca_base_path = os.path.join(os.path.expanduser("~"), "Documents", "GC-Created-Certs", "ca")

        # Create tab widget
        self.tabs = QTabWidget()
        self.tabs.addTab(self.create_ca_tab(), "Create CA")
        self.tabs.addTab(self.issue_cert_tab(), "Issue Cert")
        self.tabs.addTab(self.revoke_cert_tab(), "Revoke Cert")
        self.tabs.addTab(self.cert_conversion_tab(), "Cert Conversion")
        self.tabs.addTab(self.info_tab(), "ℹ️ Info")
        self.tabs.currentChanged.connect(self.on_tab_changed)
        self.setCentralWidget(self.tabs)
        
    def create_ca_tab(self):
        tab = QWidget()
        layout = QVBoxLayout()

        self.ca_name_input = QLineEdit()
        self.ca_name_input.setPlaceholderText("Enter CA base name (e.g., mycompany-root)")
        self.status_label = QLabel('<span style="color: green; font-weight: bold;">●</span> Ready')
        self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter)

        create_button = QPushButton("Generate Root CA")
        create_button.clicked.connect(self.handle_create_ca)
        open_folder_button = QPushButton("Open CA Folder")
        open_folder_button.clicked.connect(self.open_ca_folder)
        layout.addWidget(open_folder_button)

        layout.addWidget(self.ca_name_input)
        layout.addWidget(self.status_label)
        layout.addWidget(create_button)
        tab.setLayout(layout)
        return tab

    def open_ca_folder(self):
        path = self.ca_base_path  # Use the global or class variable pointing to CA folder
        if platform.system() == "Windows":
            subprocess.run(["explorer", path])
        elif platform.system() == "Darwin":  # macOS
            subprocess.run(["open", path])
        else:  # Linux and others
            subprocess.run(["xdg-open", path])

    def refresh_ca_selector(self):
        ca_base_folder = os.path.join(os.path.expanduser("~"), "Documents", "GC-Created-Certs", "ca")
        self.ca_selector.clear()

        if not os.path.exists(ca_base_folder):
            print(f"CA base folder does not exist: {ca_base_folder}")
            return

        print(f"Scanning CA base folder: {ca_base_folder}")
        for entry in os.listdir(ca_base_folder):
            full_path = os.path.join(ca_base_folder, entry)
            if os.path.isdir(full_path):
                # Check if this folder contains the CA cert and key files
                crt_path = os.path.join(full_path, f"{entry}.crt")
                key_path = os.path.join(full_path, f"{entry}.key")
                print(f"Checking folder '{entry}' for cert/key:")
                print(f" - Cert: {crt_path} (exists: {os.path.exists(crt_path)})")
                print(f" - Key: {key_path} (exists: {os.path.exists(key_path)})")

                if os.path.exists(crt_path) and os.path.exists(key_path):
                    print(f"Adding CA '{entry}' to selector")
                    self.ca_selector.addItem(entry)

    def on_tab_changed(self, index):
        if self.tabs.tabText(index) == "Issue Cert":
            self.refresh_ca_selector()
            self.refresh_revoke_ca_list()

    def issue_cert_tab(self):
        tab = QWidget()
        layout = QFormLayout()

        self.ca_selector = QComboBox()

        note_label = QLabel("📂 Drop your .crt and .key files into:\n"
                    f"{self.ca_base_path}\n"
                    "They must share the same base filename (e.g., MyCA.crt + MyCA.key).")
        note_label.setWordWrap(True)
        note_label.setStyleSheet("color: gray; font-size: 10pt;")

        # Certificate name input
        self.cert_name_input = QLineEdit()
       
        # Common Name input
        self.cn_input = QLineEdit()
        self.cn_input.setPlaceholderText("Device common name (e.g. device1.local)")

        # SANs input (comma-separated)
        self.san_input = QLineEdit()
        self.san_input.setPlaceholderText("Subject Alternative Names (comma-separated)")

        # Validity days input
        self.validity_input = QSpinBox()
        self.validity_input.setRange(1, 3650)
        self.validity_input.setValue(365)  # default 1 year

        # Generate button
        generate_button = QPushButton("Generate Certificate")
        generate_button.clicked.connect(self.handle_generate_cert)

        # Refresh CA list button
        self.refresh_button = QPushButton("🔄 Refresh CA List")
        self.refresh_button.clicked.connect(self.refresh_ca_selector)
        self.refresh_button.setToolTip("Re-scan the folder for new or updated CA .crt/.key pairs.")

        # Status label
        self.cert_status_label = QLabel('<span style="color: green; font-weight: bold;">●</span> Ready')
        self.cert_status_label.setWordWrap(True)
        self.cert_status_label.setAlignment(Qt.AlignmentFlag.AlignCenter)

        # Add to form layout
        layout.addRow("Select Root CA:", self.ca_selector)
        layout.addRow("", self.refresh_button)
        layout.addRow("", note_label)
        layout.addRow("Certificate Name:", self.cert_name_input)
        layout.addRow("Common Name:", self.cn_input)
        layout.addRow("Subject Alternative Names:", self.san_input)
        layout.addRow("Validity (days):", self.validity_input)
        layout.addRow(generate_button)
        layout.addRow(self.cert_status_label)

        tab.setLayout(layout)
        
        self.refresh_ca_selector()

        return tab

    def revoke_cert_tab(self):
        tab = QWidget()
        layout = QVBoxLayout()

        # CA selector specific for revoke tab
        self.revoke_ca_selector = QComboBox()
        self.revoke_ca_selector.currentIndexChanged.connect(self.load_issued_certs)

        layout.addWidget(QLabel("Select Root CA:"))
        layout.addWidget(self.revoke_ca_selector)

        # Issued cert selector
        self.issued_cert_selector = QComboBox()
        layout.addWidget(QLabel("Select certificate to revoke:"))
        layout.addWidget(self.issued_cert_selector)

        # Cert details display placeholder
        self.cert_info_label = QLabel("Select a certificate to see details.")
        self.cert_info_label.setWordWrap(True)
        layout.addWidget(self.cert_info_label)

        # Revoke button
        revoke_button = QPushButton("Revoke Certificate")
        revoke_button.clicked.connect(self.handle_revoke_cert)
        layout.addWidget(revoke_button)

        # Status label
        self.revoke_status_label = QLabel('<span style="color: green; font-weight: bold;">●</span> Ready')
        layout.addWidget(self.revoke_status_label)

        tab.setLayout(layout)

        # Initialize CA selector for revoke tab
        self.refresh_revoke_ca_list()

        return tab

    def refresh_revoke_ca_list(self):
        self.revoke_ca_selector.clear()

        # Use the root folder, not a specific CA folder
        if not os.path.exists(self.ca_base_path):
            print(f"❌ CA base folder not found: {self.ca_base_path}")
            return

        for entry in os.listdir(self.ca_base_path):
            full_path = os.path.join(self.ca_base_path, entry)
            if os.path.isdir(full_path):
                # Only show folders that look like CA folders (contain .crt and .key)
                crt_path = os.path.join(full_path, f"{entry}.crt")
                key_path = os.path.join(full_path, f"{entry}.key")
                if os.path.exists(crt_path) and os.path.exists(key_path):
                    self.revoke_ca_selector.addItem(entry)

        if self.revoke_ca_selector.count() > 0:
            self.load_issued_certs()


    

    def load_issued_certs(self):
        self.issued_cert_selector.clear()
        ca_name = self.revoke_ca_selector.currentText()
        if not ca_name:
            return

        ca_folder = os.path.join(self.ca_base_path, ca_name)

        try:
            for entry in os.listdir(ca_folder):
                entry_path = os.path.join(ca_folder, entry)
                print(f"Checking: {entry_path}")

                if entry == "Revoked Certs" or not os.path.isdir(entry_path):
                    print(f"Skipping: {entry}")
                    continue

                crt_files = [f for f in os.listdir(entry_path) if f.endswith(".crt")]
                print(f"Found crt files: {crt_files}")
                if crt_files:
                    print(f"Adding cert: {entry}")
                    self.issued_cert_selector.addItem(entry)

        except Exception as e:
            self.revoke_status_label.setText(f"❌ Error loading certs: {str(e)}")
            print(f"Exception while loading certs: {e}")

        self.cert_info_label.setText("Select a certificate to see details.")
        self.issued_cert_selector.currentIndexChanged.connect(self.display_cert_details)

    def display_cert_details(self):
        cert_name = self.issued_cert_selector.currentText()
        if not cert_name:
            self.cert_info_label.setText("Select a certificate to see details.")
            return
        # For now just display cert name. We'll parse cert info later.
        self.cert_info_label.setText(f"Selected certificate:\n{cert_name}")

    def display_cert_info(self, current, previous=None):
        if current:
            self.convert_status_label.setText(f"Selected: {current.text()}")


    def handle_create_ca(self):
        ca_name = self.ca_name_input.text().strip()
        if not ca_name:
            self.status_label.setText("❌ CA Name is required.")
            return

        ca_base_path = os.path.join(
            os.path.expanduser("~"), "Documents", "GC-Created-Certs", "ca", ca_name
        )
        os.makedirs(ca_base_path, exist_ok=True)

        ca_key_path = os.path.join(ca_base_path, f"{ca_name}.key")
        ca_crt_path = os.path.join(ca_base_path, f"{ca_name}.crt")

        self.status_label.setText(f"🔄 Generating Root CA '{ca_name}'...")

        openssl_path = os.path.join(os.getcwd(), "openssl", "openssl.exe")
        config_path = os.path.join(os.getcwd(), "config", "openssl.cnf")

        # Pass the correct local variable here!
        result = generate_root_ca(
           ca_base_path,
           config_path,
           openssl_path,
           ca_name,
        )

        self.status_label.setText(result)

        self.refresh_ca_selector()

    def load_cert_files(self, folder_path):
        self.cert_files_list.clear()
        cert_extensions = ('.crt', '.pem', '.der', '.p7b', '.pfx')
        try:
            for file in os.listdir(folder_path):
                if file.lower().endswith(cert_extensions):
                    self.cert_files_list.addItem(file)
            if self.cert_files_list.count() == 0:
                self.convert_status_label.setText("⚠️ No certificate files found.")
            else:
                self.convert_status_label.setText(f"✅ {self.cert_files_list.count()} certificates loaded.")
        except Exception as e:
            self.convert_status_label.setText(f"❌ Error reading folder: {e}")


    def browse_cert_folder(self):
        folder_path = QFileDialog.getExistingDirectory(self, "Select Certificate Folder", "")
        if folder_path:
            self.input_cert_folder_path.setText(folder_path)
            self.load_cert_files(folder_path)


    def cert_conversion_tab(self):
        tab = QWidget()
        layout = QFormLayout()

        # Certificate folder input (readonly, populated by browse)
        self.input_cert_folder_path = QLineEdit()
        self.input_cert_folder_path.setPlaceholderText("Path to certificate folder")
        self.input_cert_folder_path.setReadOnly(True)

        # Browse button to select folder
        browse_btn = QPushButton("Browse...")
        browse_btn.clicked.connect(self.browse_cert_folder)

        # Horizontal layout for folder + button
        folder_layout = QHBoxLayout()
        folder_layout.addWidget(self.input_cert_folder_path)
        folder_layout.addWidget(browse_btn)

        # Certificate list
        self.cert_files_list = QListWidget()
        self.cert_files_list.setSelectionMode(QListWidget.SingleSelection)
        self.cert_files_list.currentItemChanged.connect(self.display_cert_info)

        # Conversion format selector
        self.format_selector = QComboBox()
        self.format_selector.addItems(["PEM", "DER", "PKCS7 (.p7b)", "PKCS12 (.pfx)"])

        # Output file name input
        self.output_name_input = QLineEdit()
        self.output_name_input.setPlaceholderText("Output filename (without extension)")

        # Convert button
        convert_btn = QPushButton("Convert")
        convert_btn.clicked.connect(self.handle_cert_conversion)

        # Status label
        self.convert_status_label = QLabel('<span style="color: green; font-weight: bold;">●</span> Ready')
        self.convert_status_label.setWordWrap(True)
        self.convert_status_label.setAlignment(Qt.AlignmentFlag.AlignCenter)

        # Add widgets to layout
        layout.addRow("Certificate Folder:", folder_layout)
        layout.addRow("Certificates Found:", self.cert_files_list)
        layout.addRow("Convert to Format:", self.format_selector)
        layout.addRow("Output Filename:", self.output_name_input)
        layout.addRow(convert_btn)
        layout.addRow(self.convert_status_label)

        tab.setLayout(layout)
        return tab

    def handle_cert_conversion(self):
        folder_path = self.input_cert_folder_path.text().strip()
        selected_item = self.cert_files_list.currentItem()
        output_name = self.output_name_input.text().strip()
        target_format = self.format_selector.currentText()

        if not folder_path or not selected_item or not output_name:
            self.convert_status_label.setText("❌ Select folder, certificate, and enter output filename.")
            return

        input_path = os.path.join(folder_path, selected_item.text())
        openssl_path = os.path.join(os.getcwd(), "openssl", "openssl.exe")
        output_dir = os.path.dirname(input_path)
        output_path = os.path.join(output_dir, output_name)

        try:
            if target_format == "PEM":
                cmd = [openssl_path, "x509", "-in", input_path, "-out", output_path + ".pem", "-outform", "PEM"]
            elif target_format == "DER":
                cmd = [openssl_path, "x509", "-in", input_path, "-out", output_path + ".der", "-outform", "DER"]
            elif target_format == "PKCS7 (.p7b)":
                cmd = [openssl_path, "crl2pkcs7", "-certfile", input_path, "-out", output_path + ".p7b", "-nocrl"]
            elif target_format == "PKCS12 (.pfx)":
                # Note: Needs private key and password handling - placeholder example
                self.convert_status_label.setText("❌ PKCS12 conversion requires private key input (not implemented).")
                return
            else:
                self.convert_status_label.setText(f"❌ Unsupported format: {target_format}")
                return

            subprocess.run(cmd, check=True)
            self.convert_status_label.setText(f"✅ Conversion successful: {output_path}")
        except subprocess.CalledProcessError as e:
            self.convert_status_label.setText(f"❌ Conversion failed: {e}")


    ### ---------------------------------------------- ###
    ### Note to add multi CA handler Later ###
    ### ---------------------------------------------- ###

    def handle_generate_cert(self):
        ca_name = self.ca_selector.currentText().strip()
        cert_name = self.cn_input.text().strip()
        san_raw = self.san_input.text().strip()
        days = self.validity_input.value()
        san_list = [s.strip() for s in san_raw.split(",") if s.strip()]
        cn = self.cn_input.text().strip()

        ca_base_path = os.path.join(
            os.path.expanduser("~"),
            "Documents",
            "GC-Created-Certs",
            "ca",
            ca_name  # e.g. 'TestCA'
        )

        ca_cert_path = os.path.join(ca_base_path, f"{ca_name}.crt")

        if not cn:
            self.cert_status_label.setText("❌ Common Name is required.")
            return

        if not ca_name or not cert_name:
            self.cert_status_label.setText("❌ CA and Common Name are required.")
            return

           
        cert_output_path = os.path.join(ca_base_path, cert_name)
        # Ensure cert output folder exists
        os.makedirs(cert_output_path, exist_ok=True)

        openssl_path = os.path.join(os.getcwd(), "openssl", "openssl.exe")
        config_path = os.path.join(os.getcwd(), "config", "openssl.cnf")

        self.cert_status_label.setText("🔄 Generating certificate...")

        result = generate_certificate(
            ca_path=ca_base_path,
            cert_output_path=cert_output_path,
            config_path=config_path,
            openssl_path=openssl_path,
            cert_name=cert_name,
            san_list=san_list,
            validity_days=days,
            ca_basename=ca_name
        )

        self.cert_status_label.setText(result)

    def handle_revoke_cert(self):
        cert_name = self.issued_cert_selector.currentText()
        ca_name = self.revoke_ca_selector.currentText()

        if not cert_name or not ca_name:
            self.revoke_status_label.setText("❌ Select a CA and certificate first.")
            return

        # TODO: Add actual OpenSSL revocation logic here, for now simulate success
        self.revoke_status_label.setText(f"Revoking certificate '{cert_name}'...")

        # Paths
        issued_path = os.path.join(self.ca_base_path, ca_name, cert_name)
        revoked_path = os.path.join(self.ca_base_path, "Revoked Certs", ca_name, cert_name)

        try:
            os.makedirs(os.path.dirname(revoked_path), exist_ok=True)
            shutil.move(issued_path, revoked_path)
            self.revoke_status_label.setText(f"✅ Certificate '{cert_name}' revoked and moved to 'Revoked Certs'.")
        except Exception as e:
            self.revoke_status_label.setText(f"❌ Error moving revoked cert: {str(e)}")
            return

        # Refresh lists to reflect changes
        self.load_issued_certs()

    def info_tab(self):
        tab = QWidget()
        layout = QVBoxLayout()

        # ───── Logo ─────
        if hasattr(sys, "_MEIPASS"):
            base_dir = sys._MEIPASS  # Path where PyInstaller unpacks resources
        else:
            base_dir = os.path.abspath(".")

        logo_path = os.path.join(base_dir, "assets", "icons", "app_icon.png")
        logo_pixmap = QPixmap(logo_path)

        logo_label = QLabel()
        if logo_pixmap.isNull():
            print(f"❌ Failed to load logo from: {logo_path}")
            logo_label.setText("❌ Logo not found.")
        else:
            logo_label.setPixmap(logo_pixmap.scaledToWidth(120, Qt.SmoothTransformation))
            logo_label.setAlignment(Qt.AlignCenter)

        # ───── App Name and Description ─────
        title = QLabel("🔐 Local PKI Certificate Generator")
        title.setStyleSheet("font-size: 18pt; font-weight: bold;")
        title.setAlignment(Qt.AlignCenter)

        version = QLabel("Version 1.0.0")
        version.setAlignment(Qt.AlignCenter)

        author = QLabel("Created by Mark Gonzalez")
        author.setAlignment(Qt.AlignCenter)

        # ───── URL ─────
        website = QLabel('<a href="https://glitchcommand.dev" style="color: #00aaff;">https://glitchcommand.dev</a>')
        website.setOpenExternalLinks(True)
        website.setAlignment(Qt.AlignCenter)

        # ───── Changelog ─────
        changelog_label = QLabel("📝 Recent Changes:")
        changelog_label.setStyleSheet("font-weight: bold; margin-top: 10px;")

        changelog_content = QTextEdit()
        changelog_content.setReadOnly(True)
        changelog_content.setStyleSheet("""
            background-color: #121212;
            color: #00FF00;
            font-family: Consolas, monospace;
            font-size: 10pt;
            padding: 8px;
            border: 1px solid #00FF00;
        """)

        changelog_path = os.path.join(os.getcwd(), "CHANGELOG.md")
        if os.path.exists(changelog_path):
            with open(changelog_path, "r", encoding="utf-8") as f:
                changelog_content.setPlainText(f.read())
        else:
            changelog_content.setPlainText("No changelog file found.")

        # ───── Add to Layout ─────
        layout.addWidget(title)
        layout.addWidget(version)
        layout.addWidget(author)
        layout.addWidget(logo_label)
        layout.addWidget(website)
        layout.addWidget(changelog_label)
        layout.addWidget(changelog_content)
        layout.addStretch()

        tab.setLayout(layout)
        return tab


        ###----------------------------------------------- ###
                     # End of Main Tab Class #
        ###----------------------------------------------- ###


if __name__ == "__main__":
    import sys
    from PySide6.QtWidgets import QApplication

    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())