# tabs/review_tab.py
import random
import os
import re
import subprocess
import tempfile
import threading
import asyncio
from datetime import datetime
from PyQt6.QtWidgets import (
    QWidget, QVBoxLayout, QHBoxLayout, QLabel, 
    QLineEdit, QPushButton, QTableWidget, QTableWidgetItem, 
    QHeaderView, QFormLayout, QMessageBox, QFrame, QStackedWidget
)
from PyQt6.QtCore import Qt, pyqtSlot
import edge_tts
import database
import anki_exporter
from tts_utils import parse_text_for_edgetts, get_configured_tts_rate

class ReviewTab(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        
        # Core Review State Tracking
        self.all_cached_records = []
        self.filtered_review_pool = []
        self.current_index = -1
        self.is_flipped = False  # Track front vs back state of the active flashcard
        
        # Primary Main Layout
        main_layout = QVBoxLayout(self)
        main_layout.setContentsMargins(30, 20, 30, 20)
        main_layout.setSpacing(15)
        
        # --- SECTION 1: TOP REGION (Source Context & Tags Filters) ---
        top_container = QWidget()
        top_layout = QFormLayout(top_container)
        top_layout.setLabelAlignment(Qt.AlignmentFlag.AlignRight)
        top_layout.setSpacing(10)
        
        self.txt_review_context = QLineEdit()
        self.txt_review_context.setPlaceholderText("Filter deck by context (e.g., Camino 2027)...")
        self.txt_review_context.setStyleSheet("padding: 6px; font-size: 14px;")
        self.txt_review_context.textChanged.connect(self.handle_live_filter)
        
        self.txt_review_tags = QLineEdit()
        self.txt_review_tags.setPlaceholderText("Filter deck by tags (e.g., verb, greeting)...")
        self.txt_review_tags.setStyleSheet("padding: 6px; font-size: 14px;")
        self.txt_review_tags.textChanged.connect(self.handle_live_filter)
        
        top_layout.addRow(QLabel("<b>Source Context:</b>"), self.txt_review_context)
        top_layout.addRow(QLabel("<b>Tags:</b>"), self.txt_review_tags)
        
        main_layout.addWidget(top_container)
        
        # --- SECTION 2: MIDDLE REGION (Split Screen Workspace) ---
        split_layout = QHBoxLayout()
        split_layout.setSpacing(20)
        
        # Left Half: Live Translation Grid View Table
        self.table = QTableWidget()
        self.table.setColumnCount(2)
        self.table.setHorizontalHeaderLabels(["English Phrase", "Spanish Translation"])
        self.table.setSelectionBehavior(QTableWidget.SelectionBehavior.SelectRows)
        self.table.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers)
        
        header = self.table.horizontalHeader()
        header.setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch)
        header.setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch)
        
        split_layout.addWidget(self.table, stretch=1)
        
        # Right Half: Live Interactive Flashcard Review Panel container
        card_container = QWidget()
        card_vbox = QVBoxLayout(card_container)
        card_vbox.setContentsMargins(0, 0, 0, 0)
        card_vbox.setSpacing(12)
        
        # The Card Visual Canvas Frame
        self.card_frame = QFrame()
        self.card_frame.setStyleSheet("""
            QFrame { 
                background-color: #FAFAFA; 
                border: 2px solid #E5E7E9; 
                border-radius: 8px; 
            }
        """)
        card_frame_layout = QVBoxLayout(self.card_frame)
        card_frame_layout.setContentsMargins(25, 25, 25, 25)
        
        self.card_stack = QStackedWidget()
        
        # Card Front View (English Prompt)
        self.view_front = QWidget()
        front_layout = QVBoxLayout(self.view_front)
        front_prompt = QLabel("TRANSLATE TO SPANISH:")
        front_prompt.setStyleSheet("font-size: 11px; font-weight: bold; color: #BDC3C7; letter-spacing: 1px;")
        front_prompt.setAlignment(Qt.AlignmentFlag.AlignCenter)
        
        self.lbl_english = QLabel("No cards matching active filters.")
        self.lbl_english.setStyleSheet("font-size: 20px; color: #34495E; font-weight: 500; margin-top: 15px;")
        self.lbl_english.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.lbl_english.setWordWrap(True)
        
        front_layout.addWidget(front_prompt)
        front_layout.addWidget(self.lbl_english)
        front_layout.addStretch()
        
        # Card Back View (Spanish Answer Only)
        self.view_back = QWidget()
        back_layout = QVBoxLayout(self.view_back)
        
        self.lbl_spanish = QLabel("Spanish Answer Text")
        self.lbl_spanish.setStyleSheet("font-size: 24px; font-weight: bold; color: #2980B9; margin-top: 20px;")
        self.lbl_spanish.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.lbl_spanish.setWordWrap(True)
        
        back_layout.addWidget(self.lbl_spanish)
        back_layout.addStretch()
        
        self.card_stack.addWidget(self.view_front)
        self.card_stack.addWidget(self.view_back)
        card_frame_layout.addWidget(self.card_stack)
        
        card_vbox.addWidget(self.card_frame, stretch=1)
        
        # Buttons Row beneath the Flashcard
        card_buttons_layout = QHBoxLayout()
        card_buttons_layout.setSpacing(10)
        
        self.btn_play_audio = QPushButton("🔊 Play Voice")
        self.btn_play_audio.setCursor(Qt.CursorShape.PointingHandCursor)
        self.btn_play_audio.setStyleSheet("""
            QPushButton { background-color: #E67E22; color: white; font-weight: bold; font-size: 13px; padding: 10px; border-radius: 4px; }
            QPushButton:hover { background-color: #D35400; }
        """)
        self.btn_play_audio.clicked.connect(self.play_card_audio)
        
        self.btn_flip_next = QPushButton("Flip Card")
        self.btn_flip_next.setCursor(Qt.CursorShape.PointingHandCursor)
        self.btn_flip_next.setStyleSheet("""
            QPushButton { background-color: #34495E; color: white; font-weight: bold; font-size: 13px; padding: 10px; border-radius: 4px; }
            QPushButton:hover { background-color: #2C3E50; }
        """)
        self.btn_flip_next.clicked.connect(self.handle_card_interaction)
        
        card_buttons_layout.addWidget(self.btn_play_audio, stretch=1)
        card_buttons_layout.addWidget(self.btn_flip_next, stretch=2)
        card_vbox.addLayout(card_buttons_layout)
        
        split_layout.addWidget(card_container, stretch=1)
        main_layout.addLayout(split_layout)
        
        # --- SECTION 3: BOTTOM REGION (Action Control Panel) ---
        bottom_layout = QHBoxLayout()
        bottom_layout.setSpacing(15)
        
        self.btn_generate_deck = QPushButton("🗂️ Generate Deck")
        self.btn_generate_deck.setCursor(Qt.CursorShape.PointingHandCursor)
        self.btn_generate_deck.setStyleSheet("""
            QPushButton { background-color: #27AE60; color: white; font-weight: bold; font-size: 14px; padding: 10px 22px; border-radius: 5px; }
            QPushButton:hover { background-color: #219653; }
        """)
        self.btn_generate_deck.clicked.connect(self.generate_deck_action)
        
        self.btn_generate_video = QPushButton("🎬 Generate Video")
        self.btn_generate_video.setCursor(Qt.CursorShape.PointingHandCursor)
        self.btn_generate_video.setStyleSheet("""
            QPushButton { background-color: #2980B9; color: white; font-weight: bold; font-size: 14px; padding: 10px 22px; border-radius: 5px; }
            QPushButton:hover { background-color: #1F618D; }
        """)
        self.btn_generate_video.clicked.connect(self.generate_video_action)
        
        bottom_layout.addWidget(self.btn_generate_deck)
        bottom_layout.addWidget(self.btn_generate_video)
        bottom_layout.addStretch()
        
        main_layout.addLayout(bottom_layout)
        
        # Populate initial states from backend
        self.reload_review_pool()

    @pyqtSlot()
    def reload_review_pool(self):
        """Fetches consolidated text entries from the database and initializes cache."""
        try:
            self.all_cached_records = database.get_all_translations_explicit()
            self.handle_live_filter()
        except Exception as e:
            print(f"Error initializing flashcard review workspace: {e}")

    @pyqtSlot()
    def handle_live_filter(self):
        """Filters grid contents and generates a randomized matching queue for the card engine."""
        self.table.blockSignals(True)
        self.table.setRowCount(0)
        
        filter_ctx = self.txt_review_context.text().lower().strip()
        filter_tag = self.txt_review_tags.text().lower().strip()
        
        self.filtered_review_pool = []
        visible_row_index = 0
        
        for row in self.all_cached_records:
            val_ctx = (row["source_context"] or "").lower()
            val_tag = (row["tags"] or "").lower()
            
            if (filter_ctx in val_ctx) and (filter_tag in val_tag):
                self.filtered_review_pool.append(row)
                
                self.table.insertRow(visible_row_index)
                self.table.setItem(visible_row_index, 0, QTableWidgetItem(row["en_text"]))
                self.table.setItem(visible_row_index, 1, QTableWidgetItem(row["es_text"]))
                visible_row_index += 1
                
        self.table.blockSignals(False)
        
        # Reshuffle the active localized queue stack and reset card state tracking pointer
        random.shuffle(self.filtered_review_pool)
        self.current_index = 0 if self.filtered_review_pool else -1
        self.is_flipped = False
        self.display_current_card()

    def display_current_card(self):
        """Pushes current pool row data configurations to layout containers."""
        if not (0 <= self.current_index < len(self.filtered_review_pool)):
            self.lbl_english.setText("No phrases match current active criteria filters.")
            self.lbl_spanish.setText("")
            self.card_stack.setCurrentIndex(0)
            self.btn_flip_next.setText("Flip Card")
            self.btn_flip_next.setEnabled(False)
            self.btn_play_audio.setEnabled(False)
            return

        self.btn_flip_next.setEnabled(True)
        self.btn_play_audio.setEnabled(True)
        record = self.filtered_review_pool[self.current_index]

        # Setup front and back text labels
        self.lbl_english.setText(record["en_text"])
        self.lbl_spanish.setText(record["es_text"])

        # Sync visual widget indexing configurations
        if not self.is_flipped:
            self.card_stack.setCurrentIndex(0)
            self.btn_flip_next.setText("Flip Card")
        else:
            self.card_stack.setCurrentIndex(1)
            self.btn_flip_next.setText("Next Card ➔")

    @pyqtSlot()
    def handle_card_interaction(self):
        """State machine cycling through card flipped values or increments indices sequential steps."""
        if not self.filtered_review_pool:
            return

        if not self.is_flipped:
            # Transition State: Front -> Back
            self.is_flipped = True
            self.display_current_card()
        else:
            # Transition State: Advance to next index item row
            self.current_index += 1
            if self.current_index >= len(self.filtered_review_pool):
                self.current_index = 0
                random.shuffle(self.filtered_review_pool) # Rescramble on completion pass loops
                
            self.is_flipped = False
            self.display_current_card()

    def _async_edge_speech_worker(self, text, voice, rate_modifier):
        """Background thread worker to render neural speech with terminal debug logging."""
        async def stream_audio():
            temp_file = os.path.join(tempfile.gettempdir(), "review_card_audio.mp3")
            
            # Clean up old file if present
            if os.path.exists(temp_file):
                try:
                    os.remove(temp_file)
                except Exception:
                    pass

            try:
                print(f"[TTS Debug] Generating TTS -> Voice: {voice} | Rate: {rate_modifier} | Text: '{text}'")
                communicate = edge_tts.Communicate(text=text, voice=voice, rate=rate_modifier)
                await communicate.save(temp_file)
                
                if os.path.exists(temp_file) and os.path.getsize(temp_file) > 0:
                    print(f"[TTS Debug] Audio ready ({os.path.getsize(temp_file)} bytes). Playing via afplay...")
                    result = subprocess.run(["afplay", temp_file], capture_output=True, text=True)
                    if result.returncode != 0:
                        print(f"[TTS Debug] afplay failed: {result.stderr}")
                    else:
                        print("[TTS Debug] Playback finished successfully.")
                else:
                    print("[TTS Debug] Error: Audio file was not created or is 0 bytes.")
            except Exception as e:
                print(f"[TTS Debug] Exception during speech synthesis: {e}")

        # Explicitly set up and run a clean event loop for this thread
        try:
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            loop.run_until_complete(stream_audio())
            loop.close()
        except Exception as e:
            print(f"[TTS Debug] Event loop error: {e}")

    @pyqtSlot()
    def play_card_audio(self):
        """Auditions neural edge-tts voice based on active flashcard side."""
        if not (0 <= self.current_index < len(self.filtered_review_pool)):
            return
            
        record = self.filtered_review_pool[self.current_index]
        settings = database.load_all_settings() or {}
        rate_string = get_configured_tts_rate(settings)

        if not self.is_flipped:
            raw_text = record.get("en_text", "")
            spoken_text = parse_text_for_edgetts(raw_text)
            voice = "en-US-EmmaNeural"
        else:
            raw_text = record.get("es_text", "")
            spoken_text = parse_text_for_edgetts(raw_text)
            is_male = (record.get("gender") == "Male")
            voice = "es-ES-AlvaroNeural" if is_male else "es-ES-ElviraNeural"

        # Respect <meta sound-off> flags or empty entries
        if not spoken_text.strip():
            print("[TTS Debug] Skipped: Parsed text is empty or muted via sound-off tag.")
            return

        threading.Thread(
            target=self._async_edge_speech_worker,
            args=(spoken_text, voice, rate_string),
            daemon=True
        ).start()

    @pyqtSlot()
    def generate_deck_action(self):
        """Generates a specialized .apkg Anki deck matching active filter parameters, 
        prepending yyyy-mm-dd-hhmm timestamp and matching Sub-Deck-Namespace-Hierarchy without brackets/parentheses."""
        if not self.filtered_review_pool:
            QMessageBox.warning(self, "Export Aborted", "The current matching review deck queue is empty. Cannot compile an empty deck.")
            return

        try:
            # Load active settings dictionary directly from database configurations
            settings = database.load_all_settings() or {}

            # Extract configurations targeting exact database schema names found in settings
            target_dir = settings.get("anki_export_directory")
            root_deck_name = settings.get("anki_root_deck_name")
            sub_deck_hierarchy = settings.get("anki_sub_deck_name")

            # Fallback handling to verify directories exist safely
            if not target_dir or not os.path.isdir(str(target_dir)):
                target_dir = os.path.expanduser("~/Desktop")
            else:
                target_dir = str(target_dir)
                
            # --- Compile Full Namespace Tree Path ---
            deck_tree_parts = []
            
            if root_deck_name and str(root_deck_name).strip():
                deck_tree_parts.append(str(root_deck_name).strip())
                
            if sub_deck_hierarchy and str(sub_deck_hierarchy).strip():
                deck_tree_parts.append(str(sub_deck_hierarchy).strip())
            else:
                if not deck_tree_parts:
                    deck_tree_parts.append("DefaultDeck")

            # Join parts using Anki double-colon syntax (::) for internal Anki hierarchy
            full_deck_namespace = "::".join(deck_tree_parts)

            # --- Format Timestamp and Clean Filename ---
            # Format: YYYY-MM-DD-HHMM
            timestamp = datetime.now().strftime("%Y-%m-%d-%H%M")
            
            # Explicitly strip out parentheses/brackets before regex normalization
            clean_namespace = full_deck_namespace.replace('(', '').replace(')', '').replace('[', '').replace(']', '')
            clean_namespace = re.sub(r'[^a-zA-Z0-9]', '-', clean_namespace)
            clean_namespace = re.sub(r'-+', '-', clean_namespace).strip('-')

            # Complete output filename pattern: yyyy-mm-dd-hhmm-Sub-Deck-Namespace-Hierarchy.apkg
            filename = f"{timestamp}-{clean_namespace}.apkg"
            file_path = os.path.join(target_dir, filename)

            # Execute actual compilation algorithm pipeline mapping filtered records cleanly
            anki_exporter.compile_anki_package(self.filtered_review_pool, file_path, full_deck_namespace)
            
            QMessageBox.information(
                self, 
                "Export Complete", 
                f"Successfully exported Anki package!\n\n"
                f"<b>Deck Hierarchy:</b> {full_deck_namespace}\n"
                f"<b>File Name:</b> {filename}\n"
                f"<b>Path:</b> {file_path}"
            )
        except Exception as e:
            QMessageBox.critical(self, "Compiler Fault Safeguard", f"An exception occurred building your deck container package:\n{str(e)}")

    @pyqtSlot()
    def generate_video_action(self):
        """Placeholder function execution trigger for media compiler production automation."""
        QMessageBox.information(
            self, 
            "Media Generator Active", 
            f"Initiating background video asset production using the {len(self.filtered_review_pool)} visible phrases."
        )