# main.py
import sys
import os
from PyQt6.QtWidgets import (
    QApplication, QMainWindow, QWidget, QTabWidget, QVBoxLayout, 
    QHBoxLayout, QLabel, QPushButton, QLineEdit, QComboBox, 
    QTableWidget, QTableWidgetItem, QSlider, QFormLayout, QTextEdit, QFrame, QMessageBox
)
from PyQt6.QtCore import Qt, QUrl
from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutput
from PyQt6.QtGui import QFont

# Internal Project Module Imports
from database.connection import init_db, get_connection
from core.bulk_importer import BulkImporter
from core.clean_glossary import GlossaryCleaner

class SpanishTrainerApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Castilian Voice Trainer Pro")
        self.setMinimumSize(1200, 750)
        
        # 1. Initialize schema structures and check ingestion status
        self.ensure_database_populated()
        
        # Audio Player Architecture Setup
        self.media_player = QMediaPlayer()
        self.audio_output = QAudioOutput()
        self.media_player.setAudioOutput(self.audio_output)
        
        self.current_flashcard_id = None
        self.flashcard_ids_pool = []  # Tracks currently filtered study list IDs
        
        # Central Main Window Tabs Interface
        self.tabs = QTabWidget()
        self.setCentralWidget(self.tabs)
        
        self.init_phrase_sandbox_tab()
        self.init_flashcard_reviewer_tab()
        
        # 2. Populate both table grids on initialization
        self.refresh_crud_table()
        self.refresh_review_table()

    def ensure_database_populated(self):
        """Forces database configuration structure and triggers pipeline execution if empty."""
        print("🗄️ Verification Pass: Running schema configuration scripts...")
        init_db()
        
        conn = get_connection()
        cursor = conn.cursor()
        
        try:
            cursor.execute("SELECT COUNT(*) FROM translations")
            count = cursor.fetchone()[0]
            print(f"📊 Current Translation Pairs found in database: {count}")
        except Exception as e:
            print(f"⚠️ Table check encountered an issue (likely empty tables): {e}")
            count = 0
        finally:
            conn.close()
        
        if count == 0:
            print("🗄️ Database tables are empty. Triggering glossary reader pipeline...")
            pdf_file = "aula_int_plus_1_glos_en_alfa.pdf"
            
            if os.path.exists(pdf_file):
                importer = BulkImporter()
                importer.import_pdf_glossary(pdf_file, "Aula Internacional Plus 1")
                
                cleaner = GlossaryCleaner()
                cleaner.process_database_clean()
                print("✨ Ingestion pipeline processing sequence successfully completed.")
            else:
                print(f"❌ Error: Source document '{pdf_file}' is missing from the directory root.")

    # =====================================================================
    # 🗄️ TAB 1: TRANSLATION-CENTRIC PHRASE SANDBOX (CRUD)
    # =====================================================================
    def init_phrase_sandbox_tab(self):
        tab = QWidget()
        layout = QHBoxLayout(tab)
        
        left_panel = QVBoxLayout()
        
        filter_layout = QHBoxLayout()
        filter_layout.addWidget(QLabel("🔍 Text Filter:"))
        self.search_text_input = QLineEdit()
        self.search_text_input.setPlaceholderText("Search Spanish or English text blocks...")
        self.search_text_input.textChanged.connect(self.refresh_crud_table)
        filter_layout.addWidget(self.search_text_input)
        
        filter_layout.addWidget(QLabel("📂 Context:"))
        self.search_context_input = QLineEdit()
        self.search_context_input.setPlaceholderText("e.g. U2")
        self.search_context_input.setMaximumWidth(130)
        self.search_context_input.textChanged.connect(self.refresh_crud_table)
        filter_layout.addWidget(self.search_context_input)
        
        left_panel.addLayout(filter_layout)
        
        self.translation_table = QTableWidget()
        self.translation_table.setColumnCount(6)
        self.translation_table.setHorizontalHeaderLabels([
            "TX ID", "Spanish Phrase", "English Translation", "Type", "Source Context", "Deck Assignment"
        ])
        self.translation_table.itemSelectionChanged.connect(self.handle_table_row_select)
        left_panel.addWidget(self.translation_table)
        
        nav_layout = QHBoxLayout()
        self.btn_row_up = QPushButton("🔼 Previous Pair")
        self.btn_row_down = QPushButton("🔽 Next Pair")
        self.btn_row_up.clicked.connect(lambda: self.step_table_row(-1))
        self.btn_row_down.clicked.connect(lambda: self.step_table_row(1))
        nav_layout.addWidget(self.btn_row_up)
        nav_layout.addWidget(self.btn_row_down)
        left_panel.addLayout(nav_layout)
        
        right_panel = QVBoxLayout()
        form_frame = QFrame()
        form_frame.setFrameShape(QFrame.Shape.StyledPanel)
        form_layout = QFormLayout(form_frame)
        
        self.input_tx_id = QLineEdit()
        self.input_tx_id.setReadOnly(True)
        self.input_tx_id.setPlaceholderText("Auto-Increment ID")
        
        self.input_text_es = QTextEdit()
        self.input_text_es.setMaximumHeight(75)
        
        self.input_text_en = QTextEdit()
        self.input_text_en.setMaximumHeight(75)
        
        self.combo_type = QComboBox()
        self.combo_type.addItems(["phrase", "sentence", "noun", "verb", "adjective"])
        
        self.input_context = QLineEdit()
        self.input_context.setPlaceholderText("e.g., U8_5A")
        
        self.input_tags = QLineEdit()
        self.input_tags.setPlaceholderText("e.g., irregular_er boots_verb")
        
        self.input_deck_tag = QLineEdit()
        self.input_deck_tag.setPlaceholderText("Anki Sub-deck Hierarchy")
        
        button_qss = """
            QPushButton {
                background-color: #f0f0f0;
                border: 1px solid #c0c0c0;
                border-radius: 4px;
                font-size: 11px;
                font-weight: bold;
                color: #333333;
            }
            QPushButton:hover {
                background-color: #e0e0e0;
                border: 1px solid #a0a0a0;
            }
            QPushButton:pressed {
                background-color: #d0d0d0;
            }
        """
        
        es_header_layout = QHBoxLayout()
        es_header_layout.setContentsMargins(0, 5, 0, 5)
        es_header_layout.addWidget(QLabel("<b>🇪🇸 Castilian Spanish Text Element:</b>"))
        
        self.btn_play_sandbox_es = QPushButton("Play 🔊")
        self.btn_play_sandbox_es.setFixedWidth(75)
        self.btn_play_sandbox_es.setFixedHeight(24)
        self.btn_play_sandbox_es.setStyleSheet(button_qss)
        self.btn_play_sandbox_es.clicked.connect(self.handle_sandbox_play_es)
        es_header_layout.addWidget(self.btn_play_sandbox_es)
        
        self.combo_speed_es = QComboBox()
        self.combo_speed_es.addItems(["0.50x", "0.75x", "1.00x", "1.25x", "1.50x"])
        self.combo_speed_es.setCurrentText("1.00x")
        self.combo_speed_es.setFixedWidth(70)
        self.combo_speed_es.setFixedHeight(24)
        es_header_layout.addWidget(self.combo_speed_es)
        es_header_layout.addStretch()

        en_header_layout = QHBoxLayout()
        en_header_layout.setContentsMargins(0, 5, 0, 5)
        en_header_layout.addWidget(QLabel("<b>🇬🇧 English Target Translation:</b>"))
        
        self.btn_play_sandbox_en = QPushButton("Play 🔊")
        self.btn_play_sandbox_en.setFixedWidth(75)
        self.btn_play_sandbox_en.setFixedHeight(24)
        self.btn_play_sandbox_en.setStyleSheet(button_qss)
        self.btn_play_sandbox_en.clicked.connect(self.handle_sandbox_play_en)
        en_header_layout.addWidget(self.btn_play_sandbox_en)
        
        self.combo_speed_en = QComboBox()
        self.combo_speed_en.addItems(["0.50x", "0.75x", "1.00x", "1.25x", "1.50x"])
        self.combo_speed_en.setCurrentText("1.00x")
        self.combo_speed_en.setFixedWidth(70)
        self.combo_speed_en.setFixedHeight(24)
        en_header_layout.addWidget(self.combo_speed_en)
        en_header_layout.addStretch()

        grammar_header_layout = QHBoxLayout()
        grammar_header_layout.setContentsMargins(0, 5, 0, 5)
        grammar_header_layout.addWidget(QLabel("<b>📝 Grammar / Usage Note:</b>"))
        grammar_header_layout.addStretch()
        
        self.input_grammar_note = QTextEdit()
        self.input_grammar_note.setMaximumHeight(75)
        self.input_grammar_note.setPlaceholderText("e.g., feminine variant...")
        
        form_layout.addRow("<b>Translation Link ID:</b>", self.input_tx_id)
        form_layout.addRow(es_header_layout)
        form_layout.addRow(self.input_text_es)
        form_layout.addRow(en_header_layout)
        form_layout.addRow(self.input_text_en)
        form_layout.addRow(grammar_header_layout)
        form_layout.addRow(self.input_grammar_note)
        form_layout.addRow("Classification Profile:", self.combo_type)
        form_layout.addRow("Source Context ID (Raw):", self.input_context)
        form_layout.addRow("Anki Note Tags:", self.input_tags)
        form_layout.addRow("<b>Target Deck Scope:</b>", self.input_deck_tag)
        
        crud_buttons = QHBoxLayout()
        self.btn_save = QPushButton("➕ Create Pair")
        self.btn_update = QPushButton("💾 Update Node")
        self.btn_delete = QPushButton("🗑️ Sever Link")
        
        self.btn_save.clicked.connect(self.crud_create_pair)
        self.btn_update.clicked.connect(self.crud_update_pair)
        self.btn_delete.clicked.connect(self.crud_delete_pair)
        
        crud_buttons.addWidget(self.btn_save)
        crud_buttons.addWidget(self.btn_update)
        crud_buttons.addWidget(self.btn_delete)
        
        right_panel.addWidget(QLabel("<h3>Translation Node Management Matrix</h3>"))
        right_panel.addWidget(form_frame)
        right_panel.addLayout(crud_buttons)
        right_panel.addStretch()
        
        layout.addLayout(left_panel, stretch=4)
        layout.addLayout(right_panel, stretch=3)
        
        self.tabs.addTab(tab, "🗄️ Phrase Sandbox (CRUD)")

    # =====================================================================
    # 🃏 TAB 2: FLASHCARD STUDY MODULE (UPGRADED TWIN FRAME WINDOW)
    # =====================================================================
    def init_flashcard_reviewer_tab(self):
        tab = QWidget()
        layout = QHBoxLayout(tab)
        
        # --- LEFT SIDE PANEL: Symmetrical Review Filters & Substack Table ---
        left_panel = QVBoxLayout()
        
        filter_layout = QHBoxLayout()
        filter_layout.addWidget(QLabel("📂 Context:"))
        self.review_context_filter = QLineEdit()
        self.review_context_filter.setPlaceholderText("Filter Context...")
        self.review_context_filter.textChanged.connect(self.refresh_review_table)
        filter_layout.addWidget(self.review_context_filter)
        
        filter_layout.addWidget(QLabel("🏷️ Tag:"))
        self.review_tag_filter = QLineEdit()
        self.review_tag_filter.setPlaceholderText("Filter Tag...")
        self.review_tag_filter.textChanged.connect(self.refresh_review_table)
        filter_layout.addWidget(self.review_tag_filter)
        
        left_panel.addLayout(filter_layout)
        
        self.review_table = QTableWidget()
        self.review_table.setColumnCount(4)
        self.review_table.setHorizontalHeaderLabels(["Tx ID", "Spanish Phrase", "Context", "Tags"])
        self.review_table.itemSelectionChanged.connect(self.handle_review_table_select)
        left_panel.addWidget(self.review_table)
        
        layout.addLayout(left_panel, stretch=4)
        
        # --- RIGHT SIDE PANEL: Workspace Review Controls & Active Layout Flashcard Canvas ---
        right_panel = QVBoxLayout()
        
        # Large Display Flashcard Frame Window Canvas (Top Half Block)
        card_frame = QFrame()
        card_frame.setStyleSheet("background-color: #ffffff; border: 2px solid #bdc3c7; border-radius: 12px;")
        card_layout = QVBoxLayout(card_frame)
        card_frame.setMinimumHeight(280)
        
        self.lbl_card_text = QLabel("Select a row or click 'Next Card' to initiate...")
        self.lbl_card_text.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.lbl_card_text.setFont(QFont("Arial", 20, QFont.Weight.Bold))
        self.lbl_card_text.setWordWrap(True)
        self.lbl_card_text.setStyleSheet("color: #2c3e50; border: none; padding: 20px;")
        
        self.lbl_card_meta = QLabel("")
        self.lbl_card_meta.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.lbl_card_meta.setFont(QFont("Arial", 11))
        self.lbl_card_meta.setStyleSheet("color: #7f8c8d; border: none;")
        
        card_layout.addStretch()
        card_layout.addWidget(self.lbl_card_text)
        card_layout.addWidget(self.lbl_card_meta)
        card_layout.addStretch()
        right_panel.addWidget(card_frame, stretch=4)
        
        # Middle Operational Playback Stack Controls
        playback_layout = QHBoxLayout()
        playback_layout.addWidget(QLabel("🔊 Voice Speed:"))
        self.slider_review_speed = QSlider(Qt.Orientation.Horizontal)
        self.slider_review_speed.setMinimum(50)   
        self.slider_review_speed.setMaximum(150)  
        self.slider_review_speed.setValue(100)    
        self.lbl_review_speed = QLabel("1.00x")
        self.slider_review_speed.valueChanged.connect(self.handle_live_speed_change)
        playback_layout.addWidget(self.slider_review_speed)
        playback_layout.addWidget(self.lbl_review_speed)
        right_panel.addLayout(playback_layout)
        
        action_buttons = QHBoxLayout()
        self.btn_play_voice = QPushButton("🗣️ Play Voice Track")
        self.btn_flip_card = QPushButton("👁️ Reveal English Partner")
        
        self.btn_play_voice.clicked.connect(self.handle_play_voice)
        self.btn_flip_card.clicked.connect(self.handle_flip_card)
        
        action_buttons.addWidget(self.btn_play_voice)
        action_buttons.addWidget(self.btn_flip_card)
        right_panel.addLayout(action_buttons)
        
        right_panel.addSpacing(15)
        
        # Bottom Utility Navigation & Deployment Array
        bottom_utility_layout = QHBoxLayout()
        self.btn_export_anki = QPushButton("📦 Export Anki Deck")
        self.btn_export_video = QPushButton("🎬 Export Video")
        self.btn_load_next = QPushButton("➡️ Next Card")
        
        self.btn_export_anki.clicked.connect(self.handle_export_anki_deck)
        self.btn_export_video.clicked.connect(self.handle_export_video_assets)
        self.btn_load_next.clicked.connect(self.handle_load_next_card)
        
        # Style deployment array distinctively
        utility_qss = "QPushButton { font-weight: bold; background-color: #eaf2f8; padding: 6px; border-radius: 4px; }"
        self.btn_export_anki.setStyleSheet(utility_qss)
        self.btn_export_video.setStyleSheet(utility_qss)
        self.btn_load_next.setStyleSheet("QPushButton { font-weight: bold; background-color: #d5f5e3; padding: 6px; border-radius: 4px; }")
        
        bottom_utility_layout.addWidget(self.btn_export_anki)
        bottom_utility_layout.addWidget(self.btn_export_video)
        bottom_utility_layout.addStretch()
        bottom_utility_layout.addWidget(self.btn_load_next)
        right_panel.addLayout(bottom_utility_layout)
        
        layout.addLayout(right_panel, stretch=3)
        self.tabs.addTab(tab, "🃏 Flashcard Review")

    # =====================================================================
    # ⚡ DATA MATRIX CONTROL & SYNCHRONIZATION VIEWS
    # =====================================================================
    def refresh_crud_table(self):
        """Pulls unified translation nodes into pairs while enforcing context text matches."""
        conn = get_connection()
        cursor = conn.cursor()
        
        text_filter = self.search_text_input.text().strip()
        context_filter = self.search_context_input.text().strip()
        
        query = """
            SELECT t.translation_id, p1.text, p2.text, p1.word_type, p1.source_context, t.deck_name
            FROM translations t
            JOIN phrases p1 ON t.source_phrase_id = p1.id
            JOIN phrases p2 ON t.target_phrase_id = p2.id
            WHERE p1.language = 'es' AND p2.language = 'en'
        """
        params = []
        
        if text_filter:
            query += " AND (p1.text LIKE ? OR p2.text LIKE ?)"
            params.extend([f"%{text_filter}%", f"%{text_filter}%"])
            
        if context_filter:
            query += " AND p1.source_context LIKE ?"
            params.append(f"%{context_filter}%")
            
        query += " ORDER BY t.translation_id ASC LIMIT 250"
        
        cursor.execute(query, params)
        rows = cursor.fetchall()
        conn.close()
        
        self.translation_table.setRowCount(0)
        for row_idx, row_data in enumerate(rows):
            self.translation_table.insertRow(row_idx)
            for col_idx in range(6):
                val = row_data[col_idx]
                self.translation_table.setItem(row_idx, col_idx, QTableWidgetItem(str(val if val is not None else "")))

    def refresh_review_table(self):
        """Pulls and displays a targeted subset stack matching specific context or tag filters."""
        conn = get_connection()
        cursor = conn.cursor()
        
        context_filter = self.review_context_filter.text().strip()
        tag_filter = self.review_tag_filter.text().strip()
        
        query = """
            SELECT t.translation_id, p1.text, p1.source_context, t.tags, p1.id
            FROM translations t
            JOIN phrases p1 ON t.source_phrase_id = p1.id
            WHERE p1.language = 'es'
        """
        params = []
        if context_filter:
            query += " AND p1.source_context LIKE ?"
            params.append(f"%{context_filter}%")
        if tag_filter:
            query += " AND t.tags LIKE ?"
            params.append(f"%{tag_filter}%")
            
        query += " ORDER BY t.translation_id ASC"
        
        cursor.execute(query, params)
        rows = cursor.fetchall()
        conn.close()
        
        self.review_table.setRowCount(0)
        self.flashcard_ids_pool = []
        
        for row_idx, row_data in enumerate(rows):
            self.review_table.insertRow(row_idx)
            self.flashcard_ids_pool.append(row_data[4]) # Track active selection text key reference IDs
            for col_idx in range(4):
                val = row_data[col_idx]
                self.review_table.setItem(row_idx, col_idx, QTableWidgetItem(str(val if val is not None else "")))

    def handle_table_row_select(self):
        selected_ranges = self.translation_table.selectedRanges()
        if not selected_ranges:
            return
        row = selected_ranges[0].topRow()
        tx_id_item = self.translation_table.item(row, 0)
        if not tx_id_item:
            return
            
        tx_id = tx_id_item.text()
        
        conn = get_connection()
        cursor = conn.cursor()
        cursor.execute("""
            SELECT t.translation_id, p1.text, p2.text, p1.word_type, p1.source_context, t.deck_name, t.notes, t.tags
            FROM translations t
            JOIN phrases p1 ON t.source_phrase_id = p1.id
            JOIN phrases p2 ON t.target_phrase_id = p2.id
            WHERE t.translation_id = ?
        """, (tx_id,))
        record = cursor.fetchone()
        conn.close()
        
        if record:
            self.input_tx_id.setText(str(record[0]))
            self.input_text_es.setPlainText(str(record[1]))
            self.input_text_en.setPlainText(str(record[2]))
            self.combo_type.setCurrentText(str(record[3]) if record[3] else "phrase")
            self.input_context.setText(str(record[4]) if record[4] else "")
            self.input_tags.setText(str(record[7]) if record[7] is not None else "")
            self.input_deck_tag.setText(str(record[5]) if record[5] else "General")
            self.input_grammar_note.setPlainText(str(record[6]) if record[6] is not None else "")

    def handle_review_table_select(self):
        selected_ranges = self.review_table.selectedRanges()
        if not selected_ranges:
            return
        row = selected_ranges[0].topRow()
        
        phrase_id = self.flashcard_ids_pool[row]
        self.load_flashcard_by_id(phrase_id)

    def step_table_row(self, direction):
        current_row = self.translation_table.currentRow()
        next_row = current_row + direction
        if 0 <= next_row < self.translation_table.rowCount():
            self.translation_table.setCurrentCell(next_row, 0)

    # =====================================================================
    # ➕ ENGINE ATOMIC OPERATIONS LOGIC (CRUD MODIFIERS)
    # =====================================================================
    def crud_create_pair(self):
        conn = get_connection()
        cursor = conn.cursor()
        
        cursor.execute("""
            INSERT INTO phrases (text, language, word_type, source_context) VALUES (?, 'es', ?, ?)
        """, (self.input_text_es.toPlainText().strip(), self.combo_type.currentText(), self.input_context.text().strip()))
        es_id = cursor.lastrowid
        
        cursor.execute("""
            INSERT INTO phrases (text, language, word_type, source_context) VALUES (?, 'en', ?, ?)
        """, (self.input_text_en.toPlainText().strip(), self.combo_type.currentText(), self.input_context.text().strip()))
        en_id = cursor.lastrowid
        
        cursor.execute("""
            INSERT INTO translations (source_phrase_id, target_phrase_id, deck_name, notes, tags) VALUES (?, ?, ?, ?, ?)
        """, (es_id, en_id, self.input_deck_tag.text().strip() or "General", self.input_grammar_note.toPlainText().strip(), self.input_tags.text().strip()))
        
        conn.commit()
        conn.close()
        self.refresh_crud_table()
        self.refresh_review_table()
        QMessageBox.information(self, "Success", "Isolated phrase pairs created and relational link bound.")

    def crud_update_pair(self):
        tx_id = self.input_tx_id.text()
        if not tx_id:
            return
            
        conn = get_connection()
        cursor = conn.cursor()
        cursor.execute("SELECT source_phrase_id, target_phrase_id FROM translations WHERE translation_id = ?", (tx_id,))
        ids = cursor.fetchone()
        
        if ids:
            es_id, en_id = ids
            cursor.execute("UPDATE phrases SET text=?, word_type=?, source_context=? WHERE id=?", 
                           (self.input_text_es.toPlainText().strip(), self.combo_type.currentText(), self.input_context.text().strip(), es_id))
            cursor.execute("UPDATE phrases SET text=?, word_type=?, source_context=? WHERE id=?", 
                           (self.input_text_en.toPlainText().strip(), self.combo_type.currentText(), self.input_context.text().strip(), en_id))
            cursor.execute("UPDATE translations SET deck_name=?, notes=?, tags=? WHERE translation_id=?", 
                           (self.input_deck_tag.text().strip() or "General", self.input_grammar_note.toPlainText().strip(), self.input_tags.text().strip(), tx_id))
            conn.commit()
            
        conn.close()
        self.refresh_crud_table()
        self.refresh_review_table()
        QMessageBox.information(self, "Success", "Relational node structural update complete.")

    def crud_delete_pair(self):
        tx_id = self.input_tx_id.text()
        if not tx_id:
            return
        conn = get_connection()
        cursor = conn.cursor()
        cursor.execute("SELECT source_phrase_id, target_phrase_id FROM translations WHERE translation_id = ?", (tx_id,))
        ids = cursor.fetchone()
        if ids:
            es_id, en_id = ids
            cursor.execute("DELETE FROM translations WHERE translation_id=?", (tx_id,))
            cursor.execute("DELETE FROM phrases WHERE id=?", (es_id,))
            cursor.execute("DELETE FROM phrases WHERE id=?", (en_id,))
            conn.commit()
        conn.close()
        self.refresh_crud_table()
        self.refresh_review_table()
        self.input_tx_id.clear()
        self.input_text_es.clear()
        self.input_text_en.clear()
        self.input_grammar_note.clear()
        self.input_tags.clear()

    # =====================================================================
    # 🔊 AUDIO ENGINE & EXPANDED FLASHCARD ACTIONS
    # =====================================================================
    def load_flashcard_by_id(self, phrase_id):
        """Sets internal execution contexts cleanly targeting a specific card reference."""
        conn = get_connection()
        cursor = conn.cursor()
        cursor.execute("""
            SELECT t.translation_id, p1.text, p1.source_context, t.deck_name, p1.id, t.tags
            FROM translations t
            JOIN phrases p1 ON t.source_phrase_id = p1.id
            WHERE p1.id = ?
        """, (phrase_id,))
        record = cursor.fetchone()
        conn.close()
        
        if record:
            self.current_flashcard_id = record[4]
            self.lbl_card_text.setText(record[1])
            self.lbl_card_meta.setText(f"Link ID: {record[0]} • Context: {record[2]} • Tag: {record[5]} • Deck: {record[3]}")

    def handle_load_next_card(self):
        """Picks a random track row selection pulled directly from the current filtered list pool."""
        if not self.flashcard_ids_pool:
            QMessageBox.information(self, "Empty Pool", "No flashcards match your selected filter configurations.")
            return
            
        import random
        target_id = random.choice(self.flashcard_ids_pool)
        
        # Highlight matching row inside left panel layout matrix for context tracking
        try:
            matched_idx = self.flashcard_ids_pool.index(target_id)
            self.review_table.setCurrentCell(matched_idx, 0)
        except ValueError:
            pass
            
        self.load_flashcard_by_id(target_id)

    def handle_play_voice(self):
        """Plays or generates the Castilian neural track dynamically on fallback request loops."""
        if not self.current_flashcard_id:
            return
        conn = get_connection()
        cursor = conn.cursor()
        cursor.execute("SELECT text, language FROM phrases WHERE id = ?", (self.current_flashcard_id,))
        row = cursor.fetchone()
        conn.close()
        
        if row:
            text_str, lang = row
            safe_name = "".join([c for c in text_str if c.isalnum() or c in (" ", "_")]).strip().replace(" ", "_").lower()
            os.makedirs("media", exist_ok=True)
            target_file = f"media/{safe_name}_{lang}_female.mp3"
            
            # Fault-Tolerance Loop: If Sandbox pass missed this track, generate it seamlessly right here
            if not os.path.exists(target_file):
                print(f"🔊 Review Fallback: Synthesizing missing audio asset on the fly for '{text_str}'...")
                try:
                    import asyncio
                    import edge_tts
                    communicate = edge_tts.Communicate(text_str, "es-ES-ElviraNeural")
                    asyncio.run(communicate.save(target_file))
                except Exception as tts_err:
                    QMessageBox.critical(self, "TTS Error", f"Review pipeline failed to synthesize track:\n{tts_err}")
                    return
            
            if os.path.exists(target_file):
                self.media_player.setSource(QUrl.fromLocalFile(os.path.abspath(target_file)))
                self.media_player.setPlaybackRate(self.slider_review_speed.value() / 100.0)
                self.media_player.play()

    def handle_flip_card(self):
        if not self.current_flashcard_id:
            return
        conn = get_connection()
        cursor = conn.cursor()
        cursor.execute("""
            SELECT p2.text, t.notes FROM translations t
            JOIN phrases p1 ON t.source_phrase_id = p1.id
            JOIN phrases p2 ON t.target_phrase_id = p2.id
            WHERE p1.id = ?
        """, (self.current_flashcard_id,))
        row = cursor.fetchone()
        conn.close()
        
        if row:
            clean_es = self.lbl_card_text.text().split("\n\n👉")[0]
            display_text = f"{clean_es}\n\n👉 [ {row[0]} ]"
            if row[1]:
                display_text += f"\n\n💡 Note: {row[1]}"
            self.lbl_card_text.setText(display_text)

    def handle_sandbox_play_es(self):
        text_str = self.input_text_es.toPlainText().strip()
        if not text_str:
            return
        safe_name = "".join([c for c in text_str if c.isalnum() or c in (" ", "_")]).strip().replace(" ", "_").lower()
        os.makedirs("media", exist_ok=True)
        target_file = f"media/{safe_name}_es_female.mp3"
        
        if not os.path.exists(target_file):
            try:
                import asyncio
                import edge_tts
                communicate = edge_tts.Communicate(text_str, "es-ES-ElviraNeural")
                asyncio.run(communicate.save(target_file))
            except Exception as e:
                QMessageBox.critical(self, "TTS Error", str(e))
                return

        self.media_player.setSource(QUrl.fromLocalFile(os.path.abspath(target_file)))
        self.media_player.setPlaybackRate(float(self.combo_speed_es.currentText().replace("x", "")))
        self.media_player.play()

    def handle_sandbox_play_en(self):
        text_str = self.input_text_en.toPlainText().strip()
        if not text_str:
            return
        safe_name = "".join([c for c in text_str if c.isalnum() or c in (" ", "_")]).strip().replace(" ", "_").lower()
        os.makedirs("media", exist_ok=True)
        target_file = f"media/{safe_name}_en_female.mp3"
        
        if not os.path.exists(target_file):
            try:
                import asyncio
                import edge_tts
                communicate = edge_tts.Communicate(text_str, "en-GB-SoniaNeural")
                asyncio.run(communicate.save(target_file))
            except Exception as e:
                QMessageBox.critical(self, "TTS Error", str(e))
                return

        self.media_player.setSource(QUrl.fromLocalFile(os.path.abspath(target_file)))
        self.media_player.setPlaybackRate(float(self.combo_speed_en.currentText().replace("x", "")))
        self.media_player.play()

    def handle_live_speed_change(self, value):
        rate = value / 100.0
        self.lbl_review_speed.setText(f"{rate:.2f}x")
        if self.media_player.playbackState() == QMediaPlayer.PlaybackState.PlayingState:
            self.media_player.setPlaybackRate(rate)

    # =====================================================================
    # 📦 ARTIFACT EXPORT GATEWAYS (ANKI & DEPLOYMENT CODES)
    # =====================================================================
    def handle_export_anki_deck(self):
        """Action handler placeholder loop for your upcoming genanki package deployment modules."""
        QMessageBox.information(
            self, "Anki Export Engine", 
            f"Staging packaging manifest for active view subset!\n\n"
            f"Currently Filtered Scope count: {len(self.flashcard_ids_pool)} cards.\n"
            f"Target Destination output: Castilian_Spanish_Workspace.apkg"
        )

    def handle_export_video_assets(self):
        """Action handler placeholder loop for compiling video cards."""
        QMessageBox.information(
            self, "Video Synthesis Suite", 
            f"Staging visual timeline render frames loop!\n\n"
            f"Currently Filtered Scope count: {len(self.flashcard_ids_pool)} sequences.\n"
            f"Target Audio Tracks bound: edge-tts neural assets map."
        )

# =====================================================================
# 🚀 DIAGNOSTIC STARTUP FRAMEWORK WRAPPER
# =====================================================================
if __name__ == "__main__":
    print("🚀 Initializing PyQt6 Application Framework...")
    try:
        app = QApplication(sys.argv)
        window = SpanishTrainerApp()
        window.show()
        sys.exit(app.exec())
    except Exception as fatal_error:
        import traceback
        print("\n❌ CRITICAL CRASH DETECTED ON CORE STARTUP THREAD!")
        traceback.print_exc()
        sys.exit(1)