import csv
import json
import os

import config
from PyQt5.QtWidgets import (
    QAbstractItemView,
    QDialog,
    QFrame,
    QHeaderView,
    QHBoxLayout,
    QInputDialog,
    QLabel,
    QLineEdit,
    QMessageBox,
    QPushButton,
    QTableWidget,
    QTableWidgetItem,
    QStyle,
    QStyleOptionViewItem,
    QVBoxLayout,
)
from PyQt5.QtCore import Qt, QEvent
from PyQt5.QtGui import QFont

class CustomNotificationDialog(QDialog):
    """Custom styled notification dialog for success, error, warning, and question messages"""
    
    def __init__(self, parent=None, dialog_type="success", title="Success!", message="Operation completed successfully", 
                 button_text="OK", show_cancel=False, cancel_text="Cancel", compact=False):
        super().__init__(parent)
        self.dialog_type = dialog_type  # "success", "error", "warning", "question"
        self.result_value = False
        self.compact = compact
        
        # Remove window frame and set flags
        self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint)
        self.setAttribute(Qt.WA_TranslucentBackground)
        
        self.initUI(title, message, button_text, show_cancel, cancel_text)
        
    def initUI(self, title, message, button_text, show_cancel, cancel_text):
        # Main layout with margins for shadow effect
        main_layout = QVBoxLayout()
        outer_margin = 14 if self.compact else 20
        main_layout.setContentsMargins(outer_margin, outer_margin, outer_margin, outer_margin)
        
        # Content widget (the card)
        content_widget = QDialog()
        content_widget.setFixedWidth(340 if self.compact else 450)
        
        # Set colors based on dialog type
        if self.dialog_type == "success":
            border_color = "#48bb78"  # Green
            icon_bg = "#48bb78"
            icon = "✓"
            button_color = "#48bb78"
            button_hover = "#38a169"
        elif self.dialog_type == "error":
            border_color = "#f56565"  # Red
            icon_bg = "#f56565"
            icon = "✕"
            button_color = "#f56565"
            button_hover = "#e53e3e"
        elif self.dialog_type == "warning":
            border_color = "#ed8936"  # Orange
            icon_bg = "#ed8936"
            icon = "!"
            button_color = "#ed8936"
            button_hover = "#dd6b20"
        else:  # question
            border_color = "#4299e1"  # Blue
            icon_bg = "#4299e1"
            icon = "?"
            button_color = "#4299e1"
            button_hover = "#3182ce"
        
        content_widget.setStyleSheet(f"""
            QDialog {{
                background-color: white;
                border-top: 4px solid {border_color};
                border-radius: 8px;
            }}
        """)
        
        content_layout = QVBoxLayout()
        if self.compact:
            content_layout.setContentsMargins(24, 28, 24, 24)
            content_layout.setSpacing(12)
        else:
            content_layout.setContentsMargins(40, 50, 40, 40)
            content_layout.setSpacing(20)
        
        # Icon at the top
        icon_label = QLabel(icon)
        icon_label.setAlignment(Qt.AlignCenter)
        icon_size = 54 if self.compact else 80
        icon_label.setFixedSize(icon_size, icon_size)
        icon_label.setStyleSheet(f"""
            QLabel {{
                background-color: {icon_bg};
                color: white;
                font-size: {34 if self.compact else 48}px;
                font-weight: bold;
                border-radius: {icon_size // 2}px;
                font-family: 'Segoe UI', Arial, sans-serif;
            }}
        """)
        
        # Create a container to center the icon
        icon_container = QHBoxLayout()
        icon_container.addStretch()
        icon_container.addWidget(icon_label)
        icon_container.addStretch()
        content_layout.addLayout(icon_container)
        
        # Title
        title_label = QLabel(title)
        title_label.setAlignment(Qt.AlignCenter)
        title_label.setStyleSheet(f"""
            QLabel {{
                font-size: {22 if self.compact else 32}px;
                font-weight: bold;
                color: {border_color if self.dialog_type != 'question' else '#2d3748'};
                margin-top: {4 if self.compact else 10}px;
                margin-bottom: {4 if self.compact else 10}px;
                font-family: 'Segoe UI', Arial, sans-serif;
            }}
        """)
        content_layout.addWidget(title_label)
        
        # Separator line
        separator = QLabel()
        separator.setFixedHeight(3)
        separator.setStyleSheet(f"background-color: {border_color}; border-radius: 1px;")
        separator.setFixedWidth(100)
        separator_container = QHBoxLayout()
        separator_container.addStretch()
        separator_container.addWidget(separator)
        separator_container.addStretch()
        content_layout.addLayout(separator_container)
        
        # Message
        message_label = QLabel(message)
        message_label.setAlignment(Qt.AlignCenter)
        message_label.setWordWrap(True)
        message_font_size = 17 if self.compact else 19
        message_label.setStyleSheet(f"""
            QLabel {{
                font-size: {message_font_size}px;
                color: #718096;
                line-height: 1.5;
                margin-top: 8px;
                margin-bottom: 12px;
                font-family: 'Segoe UI', Arial, sans-serif;
            }}
        """)
        content_layout.addWidget(message_label)
        
        # Buttons
        button_layout = QHBoxLayout()
        button_layout.setSpacing(10 if self.compact else 15)
        
        if show_cancel:
            # Cancel button (outlined)
            cancel_btn = QPushButton(cancel_text)
            cancel_btn.setFixedHeight(36 if self.compact else 45)
            cancel_btn.setFixedWidth(120 if self.compact else 150)
            cancel_btn.setCursor(Qt.PointingHandCursor)
            cancel_btn.setStyleSheet(f"""
                QPushButton {{
                    background-color: white;
                    color: #718096;
                    border: 2px solid #e2e8f0;
                    border-radius: {18 if self.compact else 25}px;
                    font-size: {13 if self.compact else 16}px;
                    font-weight: 600;
                    padding: {6 if self.compact else 8}px {16 if self.compact else 24}px;
                    font-family: 'Segoe UI', Arial, sans-serif;
                }}
                QPushButton:hover {{
                    background-color: #f7fafc;
                    border-color: #cbd5e0;
                    color: #4a5568;
                }}
                QPushButton:pressed {{
                    background-color: #edf2f7;
                }}
            """)
            cancel_btn.clicked.connect(self.on_cancel)
            button_layout.addWidget(cancel_btn)
        
        # Main button (filled)
        ok_btn = QPushButton(button_text)
        ok_btn.setFixedHeight(36 if self.compact else 45)
        ok_btn.setFixedWidth(120 if self.compact else 150)
        ok_btn.setCursor(Qt.PointingHandCursor)
        ok_btn.setStyleSheet(f"""
            QPushButton {{
                background-color: {button_color};
                color: white;
                border: none;
                border-radius: {18 if self.compact else 25}px;
                font-size: {13 if self.compact else 16}px;
                font-weight: 600;
                padding: {6 if self.compact else 8}px {16 if self.compact else 24}px;
                font-family: 'Segoe UI', Arial, sans-serif;
            }}
            QPushButton:hover {{
                background-color: {button_hover};
            }}
            QPushButton:pressed {{
                background-color: {button_hover};
                padding-top: 10px;
                padding-bottom: 6px;
            }}
        """)
        ok_btn.clicked.connect(self.on_ok)
        button_layout.addWidget(ok_btn)
        
        content_layout.addLayout(button_layout)
        content_widget.setLayout(content_layout)
        
        main_layout.addWidget(content_widget)
        self.setLayout(main_layout)
        
        # Apply shadow effect
        from PyQt5.QtWidgets import QGraphicsDropShadowEffect
        from PyQt5.QtGui import QColor
        shadow = QGraphicsDropShadowEffect()
        shadow.setBlurRadius(30)
        shadow.setXOffset(0)
        shadow.setYOffset(10)
        shadow.setColor(QColor(0, 0, 0, 60))
        content_widget.setGraphicsEffect(shadow)
    
    def on_ok(self):
        self.result_value = True
        self.accept()
    
    def on_cancel(self):
        self.result_value = False
        self.reject()
    
    @staticmethod
    def show_success(parent=None, title="Success!", message="Operation completed successfully", button_text="OK", compact=False):
        """Show a success notification"""
        dialog = CustomNotificationDialog(parent, "success", title, message, button_text, compact=compact)
        dialog.exec_()
    
    @staticmethod
    def show_error(parent=None, title="Error!", message="An error occurred", button_text="Try Again", compact=False):
        """Show an error notification"""
        dialog = CustomNotificationDialog(parent, "error", title, message, button_text, compact=compact)
        dialog.exec_()
    
    @staticmethod
    def show_warning(parent=None, title="Warning!", message="Please check your input", button_text="OK", compact=False):
        """Show a warning notification"""
        dialog = CustomNotificationDialog(parent, "warning", title, message, button_text, compact=compact)
        dialog.exec_()
    
    @staticmethod
    def show_question(parent=None, title="Confirm", message="Are you sure?", button_text="Yes", cancel_text="No", compact=False):
        """Show a question dialog and return True if user clicks Yes"""
        dialog = CustomNotificationDialog(parent, "question", title, message, button_text, True, cancel_text, compact=compact)
        dialog.exec_()
        return dialog.result_value


class CustomPredictionDialog(QDialog):
    """Custom styled dialog for showing prediction results with save option"""
    
    def __init__(self, parent=None, nama="", prediction="", paket_info=None):
        super().__init__(parent)
        self.result_save = False
        
        # Remove window frame and set flags
        self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint)
        self.setAttribute(Qt.WA_TranslucentBackground)
        
        self.initUI(nama, prediction, paket_info)
        
    def initUI(self, nama, prediction, paket_info):
        # Main layout with margins for shadow effect
        main_layout = QVBoxLayout()
        main_layout.setContentsMargins(20, 20, 20, 20)
        
        # Content widget (the card)
        content_widget = QDialog()
        content_widget.setFixedWidth(500)
        
        border_color = "#4299e1"  # Blue
        
        content_widget.setStyleSheet(f"""
            QDialog {{
                background-color: white;
                border-top: 4px solid {border_color};
                border-radius: 8px;
            }}
        """)
        
        content_layout = QVBoxLayout()
        content_layout.setContentsMargins(40, 50, 40, 40)
        content_layout.setSpacing(15)
        
        # Icon at the top
        icon_label = QLabel("🎓")
        icon_label.setAlignment(Qt.AlignCenter)
        icon_label.setFixedSize(80, 80)
        icon_label.setStyleSheet(f"""
            QLabel {{
                background-color: {border_color};
                color: white;
                font-size: 48px;
                font-weight: bold;
                border-radius: 40px;
                font-family: 'Segoe UI', Arial, sans-serif;
            }}
        """)
        
        # Create a container to center the icon
        icon_container = QHBoxLayout()
        icon_container.addStretch()
        icon_container.addWidget(icon_label)
        icon_container.addStretch()
        content_layout.addLayout(icon_container)
        
        # Title
        title_label = QLabel("Hasil Prediksi")
        title_label.setAlignment(Qt.AlignCenter)
        title_label.setStyleSheet(f"""
            QLabel {{
                font-size: 32px;
                font-weight: bold;
                color: #2d3748;
                margin-top: 10px;
                margin-bottom: 10px;
                font-family: 'Segoe UI', Arial, sans-serif;
            }}
        """)
        content_layout.addWidget(title_label)
        
        # Separator line
        separator = QLabel()
        separator.setFixedHeight(3)
        separator.setStyleSheet(f"background-color: {border_color}; border-radius: 1px;")
        separator.setFixedWidth(100)
        separator_container = QHBoxLayout()
        separator_container.addStretch()
        separator_container.addWidget(separator)
        separator_container.addStretch()
        content_layout.addLayout(separator_container)
        
        # Student name
        nama_label = QLabel(f"Nama Siswa: {nama}")
        nama_label.setAlignment(Qt.AlignCenter)
        nama_label.setStyleSheet("""
            QLabel {
                font-size: 18px;
                color: #2d3748;
                font-weight: bold;
                margin-top: 10px;
                font-family: 'Segoe UI', Arial, sans-serif;
            }
        """)
        content_layout.addWidget(nama_label)
        
        # Prediction result
        pred_label = QLabel(f"Paket Terpilih: {prediction}")
        pred_label.setAlignment(Qt.AlignCenter)
        pred_label.setStyleSheet("""
            QLabel {
                font-size: 20px;
                color: #4299e1;
                font-weight: bold;
                margin-top: 5px;
                font-family: 'Segoe UI', Arial, sans-serif;
            }
        """)
        content_layout.addWidget(pred_label)
        
        # Paket detail
        if paket_info:
            kelas_info = f" ({paket_info.get('kelas_tersedia', 1)} kelas tersedia)"
            detail_text = f"{paket_info.get('nama', 'Unknown')}{kelas_info}"
            detail_label = QLabel(detail_text)
            detail_label.setAlignment(Qt.AlignCenter)
            detail_label.setWordWrap(True)
            detail_label.setStyleSheet("""
                QLabel {
                    font-size: 16px;
                    color: #718096;
                    margin-top: 5px;
                    margin-bottom: 10px;
                    font-family: 'Segoe UI', Arial, sans-serif;
                }
            """)
            content_layout.addWidget(detail_label)
        
        # Question
        question_label = QLabel("Apakah Anda ingin menyimpan hasil prediksi ini?")
        question_label.setAlignment(Qt.AlignCenter)
        question_label.setWordWrap(True)
        question_label.setStyleSheet("""
            QLabel {
                font-size: 16px;
                color: #4a5568;
                margin-top: 10px;
                margin-bottom: 20px;
                font-family: 'Segoe UI', Arial, sans-serif;
            }
        """)
        content_layout.addWidget(question_label)
        
        # Buttons
        button_layout = QHBoxLayout()
        button_layout.setSpacing(15)
        
        # Cancel button (outlined)
        cancel_btn = QPushButton("Batal")
        cancel_btn.setFixedHeight(45)
        cancel_btn.setFixedWidth(150)
        cancel_btn.setCursor(Qt.PointingHandCursor)
        cancel_btn.setStyleSheet(f"""
            QPushButton {{
                background-color: white;
                color: #718096;
                border: 2px solid #e2e8f0;
                border-radius: 25px;
                font-size: 16px;
                font-weight: 600;
                padding: 8px 24px;
                font-family: 'Segoe UI', Arial, sans-serif;
            }}
            QPushButton:hover {{
                background-color: #f7fafc;
                border-color: #cbd5e0;
                color: #4a5568;
            }}
            QPushButton:pressed {{
                background-color: #edf2f7;
            }}
        """)
        cancel_btn.clicked.connect(self.on_cancel)
        button_layout.addWidget(cancel_btn)
        
        # Save button (filled)
        save_btn = QPushButton("💾 Simpan")
        save_btn.setFixedHeight(45)
        save_btn.setFixedWidth(150)
        save_btn.setCursor(Qt.PointingHandCursor)
        save_btn.setStyleSheet(f"""
            QPushButton {{
                background-color: {border_color};
                color: white;
                border: none;
                border-radius: 25px;
                font-size: 16px;
                font-weight: 600;
                padding: 8px 24px;
                font-family: 'Segoe UI', Arial, sans-serif;
            }}
            QPushButton:hover {{
                background-color: #3182ce;
            }}
            QPushButton:pressed {{
                background-color: #3182ce;
                padding-top: 10px;
                padding-bottom: 6px;
            }}
        """)
        save_btn.clicked.connect(self.on_save)
        button_layout.addWidget(save_btn)
        
        content_layout.addLayout(button_layout)
        content_widget.setLayout(content_layout)
        
        main_layout.addWidget(content_widget)
        self.setLayout(main_layout)
        
        # Apply shadow effect
        from PyQt5.QtWidgets import QGraphicsDropShadowEffect
        from PyQt5.QtGui import QColor
        shadow = QGraphicsDropShadowEffect()
        shadow.setBlurRadius(30)
        shadow.setXOffset(0)
        shadow.setYOffset(10)
        shadow.setColor(QColor(0, 0, 0, 60))
        content_widget.setGraphicsEffect(shadow)
    
    def on_save(self):
        self.result_save = True
        self.accept()
    
    def on_cancel(self):
        self.result_save = False
        self.reject()
    
    @staticmethod
    def show_prediction(parent=None, nama="", prediction="", paket_info=None):
        """Show a prediction result dialog and return True if user wants to save"""
        dialog = CustomPredictionDialog(parent, nama, prediction, paket_info)
        dialog.exec_()
        return dialog.result_save


class StudentNamePickerDialog(QDialog):
    """Dialog untuk memilih nama siswa dari dataset final."""

    def __init__(self, parent=None, initial_name=""):
        super().__init__(parent)
        self.selected_name = ""
        self.initial_name = initial_name.strip()
        self.base_names = self._load_base_names()
        self.catalog_state = self._load_catalog_state()
        self.all_names = self._build_name_list()
        self.filtered_names = list(self.all_names)
        self.current_page = 1
        self.items_per_page = 10
        self.visible_table_rows = 6

        self.setWindowTitle("Pilih Nama Siswa")
        self.setWindowFlag(Qt.WindowContextHelpButtonHint, False)
        self.setModal(True)
        self.setMinimumSize(760, 620)
        self.resize(780, 640)
        self.initUI()
        self.apply_filter()
        self._select_initial_name()
        # Set window position to center of parent
        if self.parent():
            parent_rect = self.parent().frameGeometry()
            self.move(parent_rect.center() - self.rect().center())

    def showEvent(self, event):
        super().showEvent(event)
        # Prevent QLineEdit from receiving focus automatically when dialog opens.
        # Clear focus from search/add inputs and set focus to the dialog itself.
        try:
            if hasattr(self, 'search_input'):
                self.search_input.clearFocus()
        except Exception:
            pass
        try:
            if hasattr(self, 'add_input'):
                self.add_input.clearFocus()
        except Exception:
            pass
        try:
            self.setFocus()
        except Exception:
            pass

    def _dataset_path(self):
        return config.get_resource_path(os.path.join("data", "dataset_smakom_final.csv"))

    def _catalog_path(self):
        return config.get_writable_path(os.path.join("data", "student_name_catalog.json"))

    def _normalize_name(self, name):
        return " ".join(str(name).strip().split()).casefold()

    def _display_name(self, raw_name):
        return " ".join(str(raw_name).replace("_", " ").split())

    def _load_base_names(self):
        names = []
        seen = set()
        dataset_path = self._dataset_path()

        if not os.path.exists(dataset_path):
            return names

        try:
            with open(dataset_path, "r", encoding="utf-8-sig", newline="") as csv_file:
                reader = csv.DictReader(csv_file)
                for row in reader:
                    raw_name = row.get("nama_siswa", "")
                    display_name = self._display_name(raw_name)
                    if not display_name:
                        continue
                    normalized = self._normalize_name(display_name)
                    if normalized in seen:
                        continue
                    seen.add(normalized)
                    names.append(display_name)
        except Exception:
            return names

        return names

    def _load_catalog_state(self):
        default_state = {"added": [], "deleted": []}
        catalog_path = self._catalog_path()

        if not os.path.exists(catalog_path):
            return default_state

        try:
            with open(catalog_path, "r", encoding="utf-8") as json_file:
                data = json.load(json_file)
        except Exception:
            return default_state

        if isinstance(data, list):
            return {"added": [str(item).strip() for item in data if str(item).strip()], "deleted": []}

        if isinstance(data, dict):
            added = data.get("added") or data.get("names") or []
            deleted = data.get("deleted") or []
            return {
                "added": [str(item).strip() for item in added if str(item).strip()],
                "deleted": [str(item).strip() for item in deleted if str(item).strip()],
            }

        return default_state

    def _save_catalog_state(self):
        catalog_path = self._catalog_path()
        catalog_dir = os.path.dirname(catalog_path)
        if catalog_dir:
            os.makedirs(catalog_dir, exist_ok=True)

        payload = {
            "added": self.catalog_state.get("added", []),
            "deleted": self.catalog_state.get("deleted", []),
        }

        with open(catalog_path, "w", encoding="utf-8") as json_file:
            json.dump(payload, json_file, ensure_ascii=False, indent=2)

    def _build_name_list(self):
        deleted = {self._normalize_name(name) for name in self.catalog_state.get("deleted", [])}
        result = []
        seen = set()

        for name in self.catalog_state.get("added", []):
            normalized = self._normalize_name(name)
            if not normalized or normalized in deleted or normalized in seen:
                continue
            seen.add(normalized)
            result.append(name)

        for name in self.base_names:
            normalized = self._normalize_name(name)
            if normalized in deleted or normalized in seen:
                continue
            seen.add(normalized)
            result.append(name)

        return result

    def initUI(self):
        main_layout = QVBoxLayout(self)
        main_layout.setContentsMargins(12, 12, 12, 12)

        card = QFrame()
        card.setStyleSheet("""
            QFrame {
                background: white;
                border-radius: 16px;
            }
        """)
        card_layout = QVBoxLayout(card)
        card_layout.setContentsMargins(20, 18, 20, 16)
        card_layout.setSpacing(10)

        header_layout = QVBoxLayout()
        header_layout.setSpacing(0)
        header_layout.setContentsMargins(0, 0, 0, 0)

        title = QLabel("Pilih Nama Siswa")
        title.setContentsMargins(0, 0, 0, 0)
        title.setStyleSheet("font-size: 28px; font-weight: 700; color: #1f3b66; font-family: 'Segoe UI', Arial, sans-serif; margin: 0; padding: 0; line-height: 1;")
        header_layout.addWidget(title)

        subtitle = QLabel("Cari, tambah, hapus, lalu pilih nama yang akan dimasukkan ke form.")
        subtitle.setWordWrap(True)
        subtitle.setContentsMargins(0, 0, 0, 0)
        subtitle.setStyleSheet("font-size: 17px; color: #56708f; font-family: 'Segoe UI', Arial, sans-serif; margin: 0; padding: 0; line-height: 1;")
        header_layout.addWidget(subtitle)

        card_layout.addLayout(header_layout)

        search_row = QHBoxLayout()
        search_row.setSpacing(10)

        self.search_input = QLineEdit()
        self.search_input.setPlaceholderText("Cari nama siswa...")
        self.search_input.setMinimumHeight(42)
        self.search_input.setStyleSheet("""
            QLineEdit {
                border: 1px solid #d6deea;
                border-radius: 10px;
                padding: 9px 14px;
                font-size: 17px;
                background: #f8fbff;
                color: #1f2937;
                font-family: 'Segoe UI', Arial, sans-serif;
            }
            QLineEdit:focus {
                border-color: #2c7be5;
                background: white;
            }
        """)
        self.search_input.textChanged.connect(self.apply_filter)
        self.search_input.returnPressed.connect(self.apply_filter)
        search_row.addWidget(self.search_input, 1)

        self.add_input = QLineEdit()
        self.add_input.setPlaceholderText("Tambah nama baru...")
        self.add_input.setMinimumHeight(42)
        self.add_input.setStyleSheet(self.search_input.styleSheet())
        self.add_input.returnPressed.connect(self.add_name)
        search_row.addWidget(self.add_input, 1)

        card_layout.addLayout(search_row)

        action_row = QHBoxLayout()
        action_row.setContentsMargins(0, 2, 0, 0)
        action_row.setSpacing(12)

        add_btn = QPushButton("Tambah")
        add_btn.setFixedHeight(40)
        add_btn.setFixedWidth(110)
        add_btn.setCursor(Qt.PointingHandCursor)
        add_btn.setStyleSheet("""
            QPushButton {
                background: #118a7e;
                color: white;
                border: none;
                border-radius: 10px;
                padding: 0 16px;
                    font-size: 16px;
                    font-weight: 600;
                font-family: 'Segoe UI', Arial, sans-serif;
            }
            QPushButton:hover {
                background: #0e7369;
            }
        """)
        add_btn.clicked.connect(self.add_name)
        action_row.addWidget(add_btn)

        self.delete_btn = QPushButton("Hapus Terpilih")
        self.delete_btn.setFixedHeight(40)
        self.delete_btn.setFixedWidth(160)
        self.delete_btn.setCursor(Qt.PointingHandCursor)
        self.delete_btn.setStyleSheet("""
            QPushButton {
                background: #ef5a5a;
                color: white;
                border: none;
                border-radius: 10px;
                padding: 0 14px;
                font-size: 16px;
                font-weight: 600;
                font-family: 'Segoe UI', Arial, sans-serif;
            }
            QPushButton:hover {
                background: #d94b4b;
            }
            QPushButton:disabled {
                background: #c8d3e2;
                color: #7b8aa0;
            }
        """)
        self.delete_btn.clicked.connect(self.delete_selected_names)
        action_row.addWidget(self.delete_btn)

        self.pilih_btn = QPushButton("Pilih")
        self.pilih_btn.setFixedHeight(40)
        self.pilih_btn.setFixedWidth(96)
        self.pilih_btn.setCursor(Qt.PointingHandCursor)
        self.pilih_btn.setStyleSheet("""
            QPushButton {
                background: #1f3b66;
                color: white;
                border: none;
                border-radius: 10px;
                padding: 0 14px;
                font-size: 16px;
                font-weight: 600;
                font-family: 'Segoe UI', Arial, sans-serif;
            }
            QPushButton:hover {
                background: #182f53;
            }
            QPushButton:disabled {
                background: #c8d3e2;
                color: #7b8aa0;
            }
        """)
        self.pilih_btn.clicked.connect(self.pick_selected_name)
        action_row.addWidget(self.pilih_btn)

        self.edit_btn = QPushButton("Edit")
        self.edit_btn.setFixedHeight(40)
        self.edit_btn.setFixedWidth(96)
        self.edit_btn.setCursor(Qt.PointingHandCursor)
        self.edit_btn.setStyleSheet("""
            QPushButton {
                background: #16a34a;
                color: white;
                border: none;
                border-radius: 10px;
                padding: 0 14px;
                font-size: 16px;
                font-weight: 600;
                font-family: 'Segoe UI', Arial, sans-serif;
            }
            QPushButton:hover {
                background: #15803d;
            }
            QPushButton:disabled {
                background: #c8d3e2;
                color: #7b8aa0;
            }
        """)
        self.edit_btn.clicked.connect(self.edit_selected_name)
        action_row.addWidget(self.edit_btn)

        tutup_btn = QPushButton("Tutup")
        tutup_btn.setFixedHeight(40)
        tutup_btn.setFixedWidth(96)
        tutup_btn.setCursor(Qt.PointingHandCursor)
        tutup_btn.setStyleSheet("""
            QPushButton {
                background: #6c7a92;
                color: white;
                border: none;
                border-radius: 10px;
                padding: 0 14px;
                font-size: 16px;
                font-weight: 600;
                font-family: 'Segoe UI', Arial, sans-serif;
            }
            QPushButton:hover {
                background: #5d6a7f;
            }
        """)
        tutup_btn.clicked.connect(self.reject)
        action_row.addWidget(tutup_btn)

        action_row.addStretch()

        card_layout.addLayout(action_row)

        self.table = QTableWidget(0, 3)
        self.table.setHorizontalHeaderLabels(["No", "Nama Siswa", "Hapus"])
        self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.table.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.table.setAlternatingRowColors(True)
        self.table.verticalHeader().setVisible(False)
        self.table.setShowGrid(False)
        self.table.setStyleSheet("""
            QTableWidget {
                border: 1px solid #dbe4f0;
                border-radius: 12px;
                background: white;
                alternate-background-color: #f7faff;
                font-family: 'Segoe UI', Arial, sans-serif;
                font-size: 17px;
                color: #1f2937;
            }
            QTableWidget::item {
                padding: 8px 10px;
            }
            QHeaderView::section {
                background: #eef4fb;
                color: #173458;
                font-weight: 700;
                border: none;
                border-bottom: 1px solid #dbe4f0;
                padding: 8px 3px;
                font-size: 17px;
            }
        """)
        self.table.verticalHeader().setDefaultSectionSize(42)
        header_height = self.table.horizontalHeader().sizeHint().height()
        table_frame = self.table.frameWidth() * 2
        visible_height = header_height + (self.visible_table_rows * self.table.verticalHeader().defaultSectionSize()) + table_frame + 2
        self.table.setFixedHeight(visible_height)
        header = self.table.horizontalHeader()
        header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.Stretch)
        header.setSectionResizeMode(2, QHeaderView.ResizeToContents)
        header_item = self.table.horizontalHeaderItem(0)
        if header_item:
            header_item.setTextAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        header_item = self.table.horizontalHeaderItem(1)
        if header_item:
            header_item.setTextAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        header_item = self.table.horizontalHeaderItem(2)
        if header_item:
            header_item.setTextAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        self.table.cellDoubleClicked.connect(self._handle_cell_double_clicked)
        self.table.cellClicked.connect(self._handle_cell_clicked)
        self.table.itemSelectionChanged.connect(self._sync_action_state)
        self.table.itemChanged.connect(self._sync_action_state)
        self.table.viewport().installEventFilter(self)
        card_layout.addWidget(self.table)

        pagination_row = QHBoxLayout()
        pagination_row.setSpacing(16)
        pagination_row.setContentsMargins(2, 8, 2, 0)
        pagination_row.addStretch()

        self.prev_btn = QPushButton("◀ Previous")
        self.prev_btn.setFixedHeight(40)
        self.prev_btn.setFixedWidth(132)
        self.prev_btn.setCursor(Qt.PointingHandCursor)
        self.prev_btn.setStyleSheet("""
            QPushButton {
                background: #e8eef6;
                color: #38506d;
                border: none;
                border-radius: 10px;
                padding: 0 14px;
                font-size: 16px;
                font-weight: 600;
                font-family: 'Segoe UI', Arial, sans-serif;
            }
            QPushButton:hover {
                background: #dce6f1;
            }
            QPushButton:disabled {
                background: #edf2f7;
                color: #a0aec0;
            }
        """)
        self.prev_btn.clicked.connect(self.previous_page)
        pagination_row.addWidget(self.prev_btn)

        self.page_label = QLabel()
        self.page_label.setAlignment(Qt.AlignCenter)
        self.page_label.setStyleSheet("font-size: 16px; color: #3d5574; font-weight: 600; font-family: 'Segoe UI', Arial, sans-serif;")
        pagination_row.addWidget(self.page_label)

        self.next_btn = QPushButton("Next ▶")
        self.next_btn.setFixedHeight(40)
        self.next_btn.setFixedWidth(132)
        self.next_btn.setCursor(Qt.PointingHandCursor)
        self.next_btn.setStyleSheet(self.prev_btn.styleSheet())
        self.next_btn.clicked.connect(self.next_page)
        pagination_row.addWidget(self.next_btn)
        pagination_row.addStretch()

        card_layout.addLayout(pagination_row)
        main_layout.addWidget(card)

    def _sync_action_state(self):
        checked = self._has_checked_rows()
        selected = bool(self.get_selected_name())

        if checked and selected:
            self.table.blockSignals(True)
            self.table.clearSelection()
            self.table.setCurrentCell(-1, -1)
            self.table.blockSignals(False)
            selected = False

        if hasattr(self, "pilih_btn"):
            self.pilih_btn.setEnabled(selected and not checked)
        if hasattr(self, "edit_btn"):
            self.edit_btn.setEnabled(selected and not checked)
        if hasattr(self, "delete_btn"):
            self.delete_btn.setEnabled(checked)

    def _clear_checked_rows(self):
        for row in range(self.table.rowCount()):
            item = self.table.item(row, 2)
            if item and item.checkState() == Qt.Checked:
                item.setCheckState(Qt.Unchecked)

    def _handle_cell_clicked(self, row, column):
        del row
        if column == 2:
            self.table.blockSignals(True)
            self.table.clearSelection()
            self.table.setCurrentCell(-1, -1)
            self.table.blockSignals(False)
            self._sync_action_state()
            return

    def _handle_cell_double_clicked(self, row, column):
        # Ignore double-clicks on the checkbox column so they don't trigger name picking
        if column == 2:
            return
        self.pick_selected_name()

        if column in (0, 1) and self._has_checked_rows():
            self.table.blockSignals(True)
            self._clear_checked_rows()
            self.table.blockSignals(False)

        self._sync_action_state()

    def _has_checked_rows(self):
        for row in range(self.table.rowCount()):
            item = self.table.item(row, 2)
            if item and item.checkState() == Qt.Checked:
                return True
        return False

    def _select_initial_name(self):
        if not self.initial_name:
            return
        normalized_initial = self._normalize_name(self.initial_name)
        for row, name in enumerate(self.filtered_names):
            if self._normalize_name(name) == normalized_initial:
                self.current_page = row // self.items_per_page + 1
                self.render_table()
                self.table.selectRow(row % self.items_per_page)
                break

    def clear_search(self):
        self.search_input.clear()
        self.apply_filter()

    def apply_filter(self):
        search_text = self.search_input.text().strip().casefold()
        if search_text:
            self.filtered_names = [name for name in self.all_names if search_text in name.casefold()]
        else:
            self.filtered_names = list(self.all_names)
        self.current_page = 1
        self.render_table()

    def render_table(self):
        self.table.blockSignals(True)
        self.table.clearContents()
        self.table.clearSpans()

        total_items = len(self.filtered_names)
        total_pages = max(1, (total_items + self.items_per_page - 1) // self.items_per_page)
        self.current_page = min(max(1, self.current_page), total_pages)
        start_index = (self.current_page - 1) * self.items_per_page
        end_index = min(start_index + self.items_per_page, total_items)
        page_items = self.filtered_names[start_index:end_index]

        self.table.setRowCount(len(page_items) if page_items else 1)
        self.table.setSpan(0, 0, 1, 1)

        if not page_items:
            empty_item = QTableWidgetItem("Tidak ada nama ditemukan")
            empty_item.setTextAlignment(Qt.AlignCenter)
            empty_item.setFlags(Qt.ItemIsEnabled)
            self.table.setItem(0, 0, empty_item)
            self.table.setSpan(0, 0, 1, 3)
        else:
            for row, name in enumerate(page_items):
                no_item = QTableWidgetItem(str(start_index + row + 1))
                no_item.setTextAlignment(Qt.AlignCenter)
                no_item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
                self.table.setItem(row, 0, no_item)

                name_item = QTableWidgetItem(name)
                name_item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
                self.table.setItem(row, 1, name_item)

                delete_item = QTableWidgetItem()
                delete_item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable)
                delete_item.setCheckState(Qt.Unchecked)
                delete_item.setTextAlignment(Qt.AlignCenter)
                self.table.setItem(row, 2, delete_item)

        self.table.blockSignals(False)
        self.table.clearSelection()
        self.table.setCurrentCell(-1, -1)
        self.update_pagination_controls()
        self._sync_action_state()

    def eventFilter(self, obj, event):
        if obj is getattr(self, "table", None).viewport():
            if event.type() in (QEvent.MouseButtonPress, QEvent.MouseButtonRelease):
                index = self.table.indexAt(event.pos())
                if index.isValid() and index.column() == 2:
                    # Toggle checkbox state on mouse release and consume the event so
                    # the table's default selection behavior doesn't interfere.
                    if event.type() == QEvent.MouseButtonRelease:
                        item = self.table.item(index.row(), 2)
                        if item:
                            new_state = Qt.Unchecked if item.checkState() == Qt.Checked else Qt.Checked
                            item.setCheckState(new_state)
                            self._sync_action_state()
                    return True
        return super().eventFilter(obj, event)

    def update_pagination_controls(self):
        total_items = len(self.filtered_names)
        total_pages = max(1, (total_items + self.items_per_page - 1) // self.items_per_page)
        self.page_label.setText(f"Halaman {self.current_page} dari {total_pages} | {total_items} data")
        self.prev_btn.setEnabled(self.current_page > 1)
        self.next_btn.setEnabled(self.current_page < total_pages)

    def previous_page(self):
        if self.current_page > 1:
            self.current_page -= 1
            self.render_table()

    def next_page(self):
        total_items = len(self.filtered_names)
        total_pages = max(1, (total_items + self.items_per_page - 1) // self.items_per_page)
        if self.current_page < total_pages:
            self.current_page += 1
            self.render_table()

    def get_selected_name(self):
        current_row = self.table.currentRow()
        if current_row < 0:
            return ""
        name_item = self.table.item(current_row, 1)
        if not name_item:
            return ""
        return name_item.text().strip()

    def add_name(self):
        new_name = " ".join(self.add_input.text().strip().split())
        if not new_name:
            QMessageBox.warning(self, "Validasi", "Nama baru masih kosong.")
            return

        normalized_new = self._normalize_name(new_name)
        if any(self._normalize_name(name) == normalized_new for name in self.all_names):
            QMessageBox.warning(self, "Validasi", "Nama sudah ada di daftar.")
            return

        deleted = self.catalog_state.setdefault("deleted", [])
        deleted[:] = [name for name in deleted if self._normalize_name(name) != normalized_new]

        added = self.catalog_state.setdefault("added", [])
        # Insert new names at the beginning so the newest appears at the top
        added.insert(0, new_name)
        self.add_input.clear()
        self._save_catalog_state()
        self.all_names = self._build_name_list()
        self.apply_filter()

    def delete_selected_names(self):
        checked_names = []
        for row in range(self.table.rowCount()):
            name_item = self.table.item(row, 1)
            delete_item = self.table.item(row, 2)
            if not name_item or not delete_item:
                continue
            if delete_item.checkState() == Qt.Checked:
                checked_names.append(name_item.text().strip())

        if not checked_names:
            QMessageBox.information(self, "Info", "Centang minimal satu nama untuk dihapus.")
            return

        deleted = self.catalog_state.setdefault("deleted", [])
        added = self.catalog_state.setdefault("added", [])
        checked_normalized = {self._normalize_name(name) for name in checked_names}

        for name in checked_names:
            normalized = self._normalize_name(name)
            if any(self._normalize_name(item) == normalized for item in added):
                added[:] = [item for item in added if self._normalize_name(item) != normalized]
            elif not any(self._normalize_name(item) == normalized for item in deleted):
                deleted.append(name)

        if self.selected_name and self._normalize_name(self.selected_name) in checked_normalized:
            self.selected_name = ""

        self._save_catalog_state()
        self.all_names = self._build_name_list()
        self.apply_filter()

    class EditNameDialog(QDialog):
        """Custom dialog to edit a student name with improved styling."""
        def __init__(self, parent=None, initial_name=""):
            super().__init__(parent)
            self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint)
            self.setAttribute(Qt.WA_TranslucentBackground)
            self.initial_name = initial_name
            self.result_name = None
            self.initUI()

        def initUI(self):
            main_layout = QVBoxLayout()
            main_layout.setContentsMargins(16, 16, 16, 16)

            card = QDialog()
            card.setFixedWidth(420)
            card.setStyleSheet("""
                QDialog {
                    background: white;
                    border-radius: 10px;
                }
            """)

            card_layout = QVBoxLayout(card)
            card_layout.setContentsMargins(20, 18, 20, 18)
            card_layout.setSpacing(10)

            title = QLabel("Edit Nama")
            title.setStyleSheet("font-size: 24px; font-weight: 700; color: #1f3b66; font-family: 'Segoe UI', Arial, sans-serif;")
            title.setAlignment(Qt.AlignLeft)
            card_layout.addWidget(title)

            label = QLabel("Nama baru:")
            label.setStyleSheet("font-size: 15px; color: #334155; font-weight: 600;")
            card_layout.addWidget(label)

            self.input = QLineEdit()
            self.input.setText(self.initial_name)
            self.input.setMinimumHeight(42)
            self.input.setStyleSheet("""
                QLineEdit {
                    border: 1px solid #d6deea;
                    border-radius: 8px;
                    padding: 7px 10px;
                    font-size: 16px;
                    color: #0f172a;
                    background: #fbfdff;
                    font-family: 'Segoe UI', Arial, sans-serif;
                }
                QLineEdit:focus {
                    border-color: #2c7be5;
                    background: white;
                }
            """)
            self.input.selectAll()
            card_layout.addWidget(self.input)

            # Buttons: Close on the left, Edit on the right (Close placed to left of Edit)
            btn_row = QHBoxLayout()
            btn_row.addStretch()

            cancel_btn = QPushButton("Tutup")
            cancel_btn.setFixedHeight(38)
            cancel_btn.setFixedWidth(96)
            cancel_btn.setCursor(Qt.PointingHandCursor)
            cancel_btn.setStyleSheet("""
                QPushButton {
                    background-color: white;
                    color: #718096;
                    border: 2px solid #e2e8f0;
                    border-radius: 20px;
                    font-size: 14px;
                    font-weight: 600;
                }
                QPushButton:hover {
                    background-color: #f7fafc;
                }
            """)
            cancel_btn.clicked.connect(self.on_cancel)
            btn_row.addWidget(cancel_btn)

            btn_row.addSpacing(10)

            edit_btn = QPushButton("Edit")
            edit_btn.setFixedHeight(38)
            edit_btn.setFixedWidth(96)
            edit_btn.setCursor(Qt.PointingHandCursor)
            edit_btn.setStyleSheet("""
                QPushButton {
                    background-color: #16a34a;
                    color: white;
                    border: none;
                    border-radius: 20px;
                    font-size: 16px;
                    font-weight: 600;
                }
                QPushButton:hover {
                    background-color: #15803d;
                }
            """)
            edit_btn.clicked.connect(self.on_edit)
            btn_row.addWidget(edit_btn)

            card_layout.addLayout(btn_row)

            from PyQt5.QtWidgets import QGraphicsDropShadowEffect
            from PyQt5.QtGui import QColor
            shadow = QGraphicsDropShadowEffect()
            shadow.setBlurRadius(24)
            shadow.setXOffset(0)
            shadow.setYOffset(8)
            shadow.setColor(QColor(0, 0, 0, 60))
            card.setGraphicsEffect(shadow)

            main_layout.addWidget(card)
            self.setLayout(main_layout)

        def on_edit(self):
            text = " ".join(self.input.text().strip().split())
            if not text:
                QMessageBox.warning(self, "Validasi", "Nama baru tidak boleh kosong.")
                return
            self.result_name = text
            self.accept()

        def on_cancel(self):
            self.result_name = None
            self.reject()

    def edit_selected_name(self):
        old_name = self.get_selected_name()
        if not old_name:
            QMessageBox.information(self, "Info", "Pilih satu nama terlebih dahulu.")
            return

        dialog = StudentNamePickerDialog.EditNameDialog(self, initial_name=old_name)
        result = dialog.exec_()
        if result != QDialog.Accepted or not dialog.result_name:
            return

        new_name = " ".join(dialog.result_name.strip().split())
        if not new_name:
            QMessageBox.warning(self, "Validasi", "Nama baru tidak boleh kosong.")
            return

        normalized_new = self._normalize_name(new_name)
        normalized_old = self._normalize_name(old_name)

        if normalized_new == normalized_old:
            return

        if any(self._normalize_name(name) == normalized_new for name in self.all_names):
            QMessageBox.warning(self, "Validasi", "Nama baru sudah ada di daftar.")
            return

        added = self.catalog_state.setdefault("added", [])
        deleted = self.catalog_state.setdefault("deleted", [])

        if any(self._normalize_name(item) == normalized_old for item in added):
            added[:] = [item for item in added if self._normalize_name(item) != normalized_old]
        else:
            if not any(self._normalize_name(item) == normalized_old for item in deleted):
                deleted.append(old_name)

        deleted[:] = [item for item in deleted if self._normalize_name(item) != normalized_new]
        added[:] = [item for item in added if self._normalize_name(item) != normalized_new]
        added.insert(0, new_name)

        self.selected_name = new_name
        self._save_catalog_state()
        self.all_names = self._build_name_list()
        self.apply_filter()

    def pick_selected_name(self, *_):
        name = self.get_selected_name()
        if not name:
            QMessageBox.information(self, "Info", "Pilih nama dari tabel terlebih dahulu.")
            return

        self.selected_name = name
        self.accept()

    def accept(self):
        if not self.selected_name:
            self.selected_name = self.get_selected_name()
        super().accept()

    @staticmethod
    def show_picker(parent=None, initial_name=""):
        dialog = StudentNamePickerDialog(parent, initial_name=initial_name)
        result = dialog.exec_()
        if result == QDialog.Accepted and dialog.selected_name:
            return dialog.selected_name
        return None
