#!/usr/bin/env python3
"""
Auto-Match Panel for PunimTag Dashboard
Embeds the full auto-match GUI functionality into the dashboard frame
"""

import os
import tkinter as tk
from tkinter import ttk, messagebox
from PIL import Image, ImageTk
from typing import List, Dict, Tuple, Optional

from src.core.config import DEFAULT_FACE_TOLERANCE
from src.core.database import DatabaseManager
from src.core.face_processing import FaceProcessor
from src.gui.gui_core import GUICore


class AutoMatchPanel:
    """Integrated auto-match panel that embeds the full auto-match GUI functionality into the dashboard"""
    
    def __init__(self, parent_frame: ttk.Frame, db_manager: DatabaseManager, 
                 face_processor: FaceProcessor, gui_core: GUICore, on_navigate_home=None, verbose: int = 0):
        """Initialize the auto-match panel"""
        self.parent_frame = parent_frame
        self.db = db_manager
        self.face_processor = face_processor
        self.gui_core = gui_core
        self.on_navigate_home = on_navigate_home
        self.verbose = verbose
        
        # Panel state
        self.is_active = False
        self.matches_by_matched = {}
        self.data_cache = {}
        self.current_matched_index = 0
        self.matched_ids = []
        self.filtered_matched_ids = None
        self.identified_faces_per_person = {}
        self.checkbox_states_per_person = {}
        self.original_checkbox_states_per_person = {}
        self.identified_count = 0
        
        # GUI components
        self.components = {}
        self.main_frame = None
        
    def create_panel(self) -> ttk.Frame:
        """Create the auto-match panel with all GUI components"""
        self.main_frame = ttk.Frame(self.parent_frame)
        
        # Configure grid weights for full screen responsiveness
        self.main_frame.columnconfigure(0, weight=1)  # Left panel
        self.main_frame.columnconfigure(1, weight=1)  # Right panel
        self.main_frame.rowconfigure(0, weight=0)  # Configuration row - fixed height
        self.main_frame.rowconfigure(1, weight=1)  # Main panels row - expandable
        self.main_frame.rowconfigure(2, weight=0)  # Control buttons row - fixed height
        
        # Create all GUI components
        self._create_gui_components()
        
        # Create main content panels
        self._create_main_panels()
        
        return self.main_frame
    
    def _create_gui_components(self):
        """Create all GUI components for the auto-match interface"""
        # Configuration frame
        config_frame = ttk.LabelFrame(self.main_frame, text="Configuration", padding="10")
        config_frame.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10))
        # Don't give weight to any column to prevent stretching
        
        # Start button (moved to the left)
        start_btn = ttk.Button(config_frame, text="🚀 Run Auto-Match", command=self._start_auto_match)
        start_btn.grid(row=0, column=0, padx=(0, 20))
        
        # Tolerance setting
        ttk.Label(config_frame, text="Tolerance:").grid(row=0, column=1, sticky=tk.W, padx=(0, 2))
        self.components['tolerance_var'] = tk.StringVar(value=str(DEFAULT_FACE_TOLERANCE))
        tolerance_entry = ttk.Entry(config_frame, textvariable=self.components['tolerance_var'], width=8)
        tolerance_entry.grid(row=0, column=2, sticky=tk.W, padx=(0, 10))
        ttk.Label(config_frame, text="(lower = stricter matching)").grid(row=0, column=3, sticky=tk.W)
    
    def _create_main_panels(self):
        """Create the main left and right panels"""
        # Left panel for identified person
        self.components['left_panel'] = ttk.LabelFrame(self.main_frame, text="Identified Person", padding="10")
        self.components['left_panel'].grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(0, 5))
        
        # Right panel for unidentified faces
        self.components['right_panel'] = ttk.LabelFrame(self.main_frame, text="Unidentified Faces to Match", padding="10")
        self.components['right_panel'].grid(row=1, column=1, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(5, 0))
        
        # Create left panel content
        self._create_left_panel_content()
        
        # Create right panel content
        self._create_right_panel_content()
        
        # Create control buttons
        self._create_control_buttons()
    
    def _create_left_panel_content(self):
        """Create the left panel content for identified person"""
        left_panel = self.components['left_panel']
        
        # Search controls for filtering people by last name
        search_frame = ttk.Frame(left_panel)
        search_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=(0, 10))
        search_frame.columnconfigure(0, weight=1)
        
        # Search input
        self.components['search_var'] = tk.StringVar()
        self.components['search_entry'] = ttk.Entry(search_frame, textvariable=self.components['search_var'], width=20, state='disabled')
        self.components['search_entry'].grid(row=0, column=0, sticky=(tk.W, tk.E), padx=(0, 5))
        
        # Search buttons
        self.components['search_btn'] = ttk.Button(search_frame, text="Search", width=8, command=self._apply_search_filter, state='disabled')
        self.components['search_btn'].grid(row=0, column=1, padx=(0, 5))
        
        self.components['clear_btn'] = ttk.Button(search_frame, text="Clear", width=6, command=self._clear_search_filter, state='disabled')
        self.components['clear_btn'].grid(row=0, column=2)
        
        # Search help label
        self.components['search_help_label'] = ttk.Label(search_frame, text="Search disabled - click 'Start Auto-Match' first", 
                                                       font=("Arial", 8), foreground="gray")
        self.components['search_help_label'].grid(row=1, column=0, columnspan=3, sticky=tk.W, pady=(2, 0))
        
        # Person info label
        self.components['person_info_label'] = ttk.Label(left_panel, text="", font=("Arial", 10, "bold"))
        self.components['person_info_label'].grid(row=1, column=0, pady=(0, 10), sticky=tk.W)
        
        # Person image canvas
        style = ttk.Style()
        canvas_bg_color = style.lookup('TFrame', 'background') or '#d9d9d9'
        self.components['person_canvas'] = tk.Canvas(left_panel, width=300, height=300, 
                                                   bg=canvas_bg_color, highlightthickness=0)
        self.components['person_canvas'].grid(row=2, column=0, pady=(0, 10))
        
        # Save button
        self.components['save_btn'] = ttk.Button(left_panel, text="💾 Save Changes", 
                                               command=self._save_changes, state='disabled')
        self.components['save_btn'].grid(row=3, column=0, pady=(0, 10), sticky=(tk.W, tk.E))
    
    def _create_right_panel_content(self):
        """Create the right panel content for unidentified faces"""
        right_panel = self.components['right_panel']
        
        # Control buttons for matches (Select All / Clear All)
        matches_controls_frame = ttk.Frame(right_panel)
        matches_controls_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=(0, 10))
        
        self.components['select_all_btn'] = ttk.Button(matches_controls_frame, text="☑️ Select All", 
                                                     command=self._select_all_matches, state='disabled')
        self.components['select_all_btn'].pack(side=tk.LEFT, padx=(0, 5))
        
        self.components['clear_all_btn'] = ttk.Button(matches_controls_frame, text="☐ Clear All", 
                                                    command=self._clear_all_matches, state='disabled')
        self.components['clear_all_btn'].pack(side=tk.LEFT)
        
        # Create scrollable frame for matches
        matches_frame = ttk.Frame(right_panel)
        matches_frame.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        matches_frame.columnconfigure(0, weight=1)
        matches_frame.rowconfigure(0, weight=1)
        
        # Create canvas and scrollbar for matches
        style = ttk.Style()
        canvas_bg_color = style.lookup('TFrame', 'background') or '#d9d9d9'
        self.components['matches_canvas'] = tk.Canvas(matches_frame, bg=canvas_bg_color, highlightthickness=0)
        self.components['matches_canvas'].grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        
        scrollbar = ttk.Scrollbar(matches_frame, orient="vertical", command=self.components['matches_canvas'].yview)
        scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))
        self.components['matches_canvas'].configure(yscrollcommand=scrollbar.set)
        
        # Configure right panel grid weights
        right_panel.columnconfigure(0, weight=1)
        right_panel.rowconfigure(1, weight=1)
    
    def _create_control_buttons(self):
        """Create the control buttons for navigation"""
        control_frame = ttk.Frame(self.main_frame)
        control_frame.grid(row=2, column=0, columnspan=2, pady=(10, 0))
        
        self.components['back_btn'] = ttk.Button(control_frame, text="⏮️ Back", 
                                               command=self._go_back, state='disabled')
        self.components['back_btn'].grid(row=0, column=0, padx=(0, 5))
        
        self.components['next_btn'] = ttk.Button(control_frame, text="⏭️ Next", 
                                               command=self._go_next, state='disabled')
        self.components['next_btn'].grid(row=0, column=1, padx=5)
        
        self.components['quit_btn'] = ttk.Button(control_frame, text="❌ Exit Auto-Match", 
                                               command=self._quit_auto_match)
        self.components['quit_btn'].grid(row=0, column=2, padx=(5, 0))
    
    def _start_auto_match(self):
        """Start the auto-match process"""
        try:
            tolerance = float(self.components['tolerance_var'].get().strip())
            if tolerance < 0 or tolerance > 1:
                raise ValueError
        except Exception:
            messagebox.showerror("Error", "Please enter a valid tolerance value between 0.0 and 1.0.")
            return
        
        include_same_photo = False  # Always exclude same photo matching
        
        # Get all identified faces (one per person) to use as reference faces
        with self.db.get_db_connection() as conn:
            cursor = conn.cursor()
            cursor.execute('''
                SELECT f.id, f.person_id, f.photo_id, f.location, p.filename, f.quality_score,
                       f.face_confidence, f.detector_backend, f.model_name
                FROM faces f
                JOIN photos p ON f.photo_id = p.id
                WHERE f.person_id IS NOT NULL AND f.quality_score >= 0.3
                ORDER BY f.person_id, f.quality_score DESC
            ''')
            identified_faces = cursor.fetchall()
        
        if not identified_faces:
            messagebox.showinfo("No Identified Faces", "🔍 No identified faces found for auto-matching")
            return
        
        # Group by person and get the best quality face per person
        person_faces = {}
        for face in identified_faces:
            person_id = face[1]
            if person_id not in person_faces:
                person_faces[person_id] = face
        
        # Convert to ordered list to ensure consistent ordering
        person_faces_list = []
        for person_id, face in person_faces.items():
            # Get person name for ordering
            with self.db.get_db_connection() as conn:
                cursor = conn.cursor()
                cursor.execute('SELECT first_name, last_name FROM people WHERE id = ?', (person_id,))
                result = cursor.fetchone()
                if result:
                    first_name, last_name = result
                    if last_name and first_name:
                        person_name = f"{last_name}, {first_name}"
                    elif last_name:
                        person_name = last_name
                    elif first_name:
                        person_name = first_name
                    else:
                        person_name = "Unknown"
                else:
                    person_name = "Unknown"
            person_faces_list.append((person_id, face, person_name))
        
        # Sort by person name for consistent, user-friendly ordering
        person_faces_list.sort(key=lambda x: x[2])  # Sort by person name (index 2)
        
        # Find similar faces for each identified person
        self.matches_by_matched = {}
        for person_id, reference_face, person_name in person_faces_list:
            reference_face_id = reference_face[0]
            
            # Use the same filtering and sorting logic as identify
            similar_faces = self.face_processor._get_filtered_similar_faces(
                reference_face_id, tolerance, include_same_photo, face_status=None)
            
            # Convert to auto-match format
            person_matches = []
            for similar_face in similar_faces:
                match = {
                    'unidentified_id': similar_face['face_id'],
                    'unidentified_photo_id': similar_face['photo_id'],
                    'unidentified_filename': similar_face['filename'],
                    'unidentified_location': similar_face['location'],
                    'matched_id': reference_face_id,
                    'matched_photo_id': reference_face[2],
                    'matched_filename': reference_face[4],
                    'matched_location': reference_face[3],
                    'person_id': person_id,
                    'distance': similar_face['distance'],
                    'quality_score': similar_face['quality_score'],
                    'adaptive_tolerance': similar_face.get('adaptive_tolerance', tolerance)
                }
                person_matches.append(match)
            
            self.matches_by_matched[person_id] = person_matches
        
        # Flatten all matches for counting
        all_matches = []
        for person_matches in self.matches_by_matched.values():
            all_matches.extend(person_matches)
        
        if not all_matches:
            messagebox.showinfo("No Matches", "🔍 No similar faces found for auto-identification")
            return
        
        # Pre-fetch all needed data
        self.data_cache = self._prefetch_auto_match_data(self.matches_by_matched)
        
        # Initialize state
        self.matched_ids = [person_id for person_id, _, _ in person_faces_list 
                          if person_id in self.matches_by_matched and self.matches_by_matched[person_id]]
        self.filtered_matched_ids = None
        self.current_matched_index = 0
        self.identified_faces_per_person = {}
        self.checkbox_states_per_person = {}
        self.original_checkbox_states_per_person = {}
        self.identified_count = 0
        
        # Enable search controls now that auto-match has started
        self.components['search_entry'].config(state='normal')
        self.components['search_btn'].config(state='normal')
        self.components['clear_btn'].config(state='normal')
        
        # Check if there's only one person - disable search if so
        has_only_one_person = len(self.matched_ids) == 1
        if has_only_one_person:
            self.components['search_var'].set("")
            self.components['search_entry'].config(state='disabled')
            self.components['search_btn'].config(state='disabled')
            self.components['clear_btn'].config(state='disabled')
            self.components['search_help_label'].config(text="(Search disabled - only one person found)")
        else:
            self.components['search_help_label'].config(text="Type Last Name")
        
        # Enable controls
        self._update_control_states()
        
        # Show the first person
        self._update_display()
        
        self.is_active = True
    
    def _prefetch_auto_match_data(self, matches_by_matched: Dict) -> Dict:
        """Pre-fetch all needed data to avoid repeated database queries"""
        data_cache = {}
        
        with self.db.get_db_connection() as conn:
            cursor = conn.cursor()
            
            # Pre-fetch all person names and details
            person_ids = list(matches_by_matched.keys())
            if person_ids:
                placeholders = ','.join('?' * len(person_ids))
                cursor.execute(f'SELECT id, first_name, last_name, middle_name, maiden_name, date_of_birth FROM people WHERE id IN ({placeholders})', person_ids)
                data_cache['person_details'] = {}
                for row in cursor.fetchall():
                    person_id = row[0]
                    first_name = row[1] or ''
                    last_name = row[2] or ''
                    middle_name = row[3] or ''
                    maiden_name = row[4] or ''
                    date_of_birth = row[5] or ''
                    
                    # Create full name display
                    name_parts = []
                    if first_name:
                        name_parts.append(first_name)
                    if middle_name:
                        name_parts.append(middle_name)
                    if last_name:
                        name_parts.append(last_name)
                    if maiden_name:
                        name_parts.append(f"({maiden_name})")
                    
                    full_name = ' '.join(name_parts)
                    data_cache['person_details'][person_id] = {
                        'full_name': full_name,
                        'first_name': first_name,
                        'last_name': last_name,
                        'middle_name': middle_name,
                        'maiden_name': maiden_name,
                        'date_of_birth': date_of_birth
                    }
            
            # Pre-fetch all photo paths (both matched and unidentified)
            all_photo_ids = set()
            for person_matches in matches_by_matched.values():
                for match in person_matches:
                    all_photo_ids.add(match['matched_photo_id'])
                    all_photo_ids.add(match['unidentified_photo_id'])
            
            if all_photo_ids:
                photo_ids_list = list(all_photo_ids)
                placeholders = ','.join('?' * len(photo_ids_list))
                cursor.execute(f'SELECT id, path FROM photos WHERE id IN ({placeholders})', photo_ids_list)
                data_cache['photo_paths'] = {row[0]: row[1] for row in cursor.fetchall()}
        
        return data_cache
    
    def _update_display(self):
        """Update the display for the current person"""
        active_ids = self.filtered_matched_ids if self.filtered_matched_ids is not None else self.matched_ids
        
        if self.current_matched_index >= len(active_ids):
            self._finish_auto_match()
            return
        
        matched_id = active_ids[self.current_matched_index]
        matches_for_this_person = self.matches_by_matched[matched_id]
        
        # Update button states
        self._update_control_states()
        
        # Update save button text with person name
        self._update_save_button_text()
        
        # Get the first match to get matched person info
        if not matches_for_this_person:
            print(f"❌ Error: No matches found for current person {matched_id}")
            # Skip to next person if available
            if self.current_matched_index < len(active_ids) - 1:
                self.current_matched_index += 1
                self._update_display()
            else:
                self._finish_auto_match()
            return
        
        first_match = matches_for_this_person[0]
        
        # Use cached data instead of database queries
        person_details = self.data_cache['person_details'].get(first_match['person_id'], {})
        person_name = person_details.get('full_name', "Unknown")
        date_of_birth = person_details.get('date_of_birth', '')
        matched_photo_path = self.data_cache['photo_paths'].get(first_match['matched_photo_id'], None)
        
        # Create detailed person info display
        person_info_lines = [f"👤 Person: {person_name}"]
        if date_of_birth:
            person_info_lines.append(f"📅 Born: {date_of_birth}")
        person_info_lines.extend([
            f"📁 Photo: {first_match['matched_filename']}",
            f"📍 Face location: {first_match['matched_location']}"
        ])
        
        # Update matched person info
        self.components['person_info_label'].config(text="\n".join(person_info_lines))
        
        # Display matched person face
        self.components['person_canvas'].delete("all")
        if matched_photo_path:
            matched_crop_path = self.face_processor._extract_face_crop(
                matched_photo_path, 
                first_match['matched_location'], 
                f"matched_{first_match['person_id']}"
            )
            
            if matched_crop_path and os.path.exists(matched_crop_path):
                try:
                    pil_image = Image.open(matched_crop_path)
                    pil_image.thumbnail((300, 300), Image.Resampling.LANCZOS)
                    photo = ImageTk.PhotoImage(pil_image)
                    self.components['person_canvas'].create_image(150, 150, image=photo)
                    self.components['person_canvas'].image = photo
                    
                    # Add photo icon to the matched person face
                    actual_width, actual_height = pil_image.size
                    top_left_x = 150 - (actual_width // 2)
                    top_left_y = 150 - (actual_height // 2)
                    self.gui_core.create_photo_icon(self.components['person_canvas'], matched_photo_path, icon_size=20,
                                          face_x=top_left_x, face_y=top_left_y, 
                                          face_width=actual_width, face_height=actual_height,
                                          canvas_width=300, canvas_height=300)
                except Exception as e:
                    self.components['person_canvas'].create_text(150, 150, text=f"❌ Could not load image: {e}", fill="red")
            else:
                self.components['person_canvas'].create_text(150, 150, text="🖼️ No face crop available", fill="gray")
        
        # Clear and populate unidentified faces
        self._update_matches_display(matches_for_this_person, matched_id)
    
    def _update_matches_display(self, matches_for_this_person, matched_id):
        """Update the matches display for the current person"""
        # Clear existing matches
        self.components['matches_canvas'].delete("all")
        self.match_checkboxes = []
        self.match_vars = []
        
        # Create frame for unidentified faces inside canvas
        matches_inner_frame = ttk.Frame(self.components['matches_canvas'])
        self.components['matches_canvas'].create_window((0, 0), window=matches_inner_frame, anchor="nw")
        
        # Use cached photo paths
        photo_paths = self.data_cache['photo_paths']
        
        # Create all checkboxes
        for i, match in enumerate(matches_for_this_person):
            # Get unidentified face info from cached data
            unidentified_photo_path = photo_paths.get(match['unidentified_photo_id'], '')
            
            # Calculate calibrated confidence (actual match probability)
            confidence_pct, confidence_desc = self.face_processor._get_calibrated_confidence(match['distance'])
            
            # Create match frame
            match_frame = ttk.Frame(matches_inner_frame)
            match_frame.grid(row=i, column=0, sticky=(tk.W, tk.E), pady=5)
            
            # Checkbox for this match
            match_var = tk.BooleanVar()
            
            # Restore previous checkbox state if available
            unique_key = f"{matched_id}_{match['unidentified_id']}"
            if matched_id in self.checkbox_states_per_person and unique_key in self.checkbox_states_per_person[matched_id]:
                saved_state = self.checkbox_states_per_person[matched_id][unique_key]
                match_var.set(saved_state)
            # Otherwise, pre-select if this face was previously identified for this person
            elif matched_id in self.identified_faces_per_person and match['unidentified_id'] in self.identified_faces_per_person[matched_id]:
                match_var.set(True)
            
            self.match_vars.append(match_var)
            
            # Capture original state at render time
            if matched_id not in self.original_checkbox_states_per_person:
                self.original_checkbox_states_per_person[matched_id] = {}
            if unique_key not in self.original_checkbox_states_per_person[matched_id]:
                self.original_checkbox_states_per_person[matched_id][unique_key] = match_var.get()
            
            # Add callback to save state immediately when checkbox changes
            def on_checkbox_change(var, person_id, face_id):
                unique_key = f"{person_id}_{face_id}"
                if person_id not in self.checkbox_states_per_person:
                    self.checkbox_states_per_person[person_id] = {}
                
                current_value = var.get()
                self.checkbox_states_per_person[person_id][unique_key] = current_value
            
            # Bind the callback to the variable
            current_person_id = matched_id
            current_face_id = match['unidentified_id']
            match_var.trace('w', lambda *args, var=match_var, person_id=current_person_id, face_id=current_face_id: on_checkbox_change(var, person_id, face_id))
            
            # Configure match frame for grid layout
            match_frame.columnconfigure(0, weight=0)  # Checkbox column - fixed width
            match_frame.columnconfigure(1, weight=0)  # Image column - fixed width
            match_frame.columnconfigure(2, weight=1)  # Text column - expandable
            
            # Checkbox
            checkbox = ttk.Checkbutton(match_frame, variable=match_var)
            checkbox.grid(row=0, column=0, rowspan=2, sticky=(tk.W, tk.N), padx=(0, 5))
            self.match_checkboxes.append(checkbox)
            
            # Unidentified face image
            match_canvas = None
            if unidentified_photo_path:
                style = ttk.Style()
                canvas_bg_color = style.lookup('TFrame', 'background') or '#d9d9d9'
                match_canvas = tk.Canvas(match_frame, width=100, height=100, bg=canvas_bg_color, highlightthickness=0)
                match_canvas.grid(row=0, column=1, rowspan=2, sticky=(tk.W, tk.N), padx=(5, 10))
                
                unidentified_crop_path = self.face_processor._extract_face_crop(
                    unidentified_photo_path,
                    match['unidentified_location'],
                    f"unid_{match['unidentified_id']}"
                )
                
                if unidentified_crop_path and os.path.exists(unidentified_crop_path):
                    try:
                        pil_image = Image.open(unidentified_crop_path)
                        pil_image.thumbnail((100, 100), Image.Resampling.LANCZOS)
                        photo = ImageTk.PhotoImage(pil_image)
                        match_canvas.create_image(50, 50, image=photo)
                        match_canvas.image = photo
                        
                        # Add photo icon
                        self.gui_core.create_photo_icon(match_canvas, unidentified_photo_path, icon_size=15,
                                              face_x=0, face_y=0, 
                                              face_width=100, face_height=100,
                                              canvas_width=100, canvas_height=100)
                    except Exception:
                        match_canvas.create_text(50, 50, text="❌", fill="red")
                else:
                    match_canvas.create_text(50, 50, text="🖼️", fill="gray")
            
            # Confidence badge and filename
            info_container = ttk.Frame(match_frame)
            info_container.grid(row=0, column=2, rowspan=2, sticky=(tk.W, tk.E))
            
            badge = self.gui_core.create_confidence_badge(info_container, confidence_pct)
            badge.pack(anchor=tk.W)
            
            filename_label = ttk.Label(info_container, text=f"📁 {match['unidentified_filename']}", 
                                     font=("Arial", 8), foreground="gray")
            filename_label.pack(anchor=tk.W, pady=(2, 0))
        
        # Update Select All / Clear All button states
        self._update_match_control_buttons_state()
        
        # Update scroll region
        self.components['matches_canvas'].update_idletasks()
        self.components['matches_canvas'].configure(scrollregion=self.components['matches_canvas'].bbox("all"))
    
    def _update_control_states(self):
        """Update control button states based on current position"""
        active_ids = self.filtered_matched_ids if self.filtered_matched_ids is not None else self.matched_ids
        
        # Enable/disable Back button
        if self.current_matched_index > 0:
            self.components['back_btn'].config(state='normal')
        else:
            self.components['back_btn'].config(state='disabled')
        
        # Enable/disable Next button
        if self.current_matched_index < len(active_ids) - 1:
            self.components['next_btn'].config(state='normal')
        else:
            self.components['next_btn'].config(state='disabled')
        
        # Enable save button if we have matches
        if active_ids and self.current_matched_index < len(active_ids):
            self.components['save_btn'].config(state='normal')
        else:
            self.components['save_btn'].config(state='disabled')
    
    def _update_save_button_text(self):
        """Update save button text with current person name"""
        active_ids = self.filtered_matched_ids if self.filtered_matched_ids is not None else self.matched_ids
        if self.current_matched_index < len(active_ids):
            matched_id = active_ids[self.current_matched_index]
            matches_for_current_person = self.matches_by_matched[matched_id]
            if matches_for_current_person:
                person_id = matches_for_current_person[0]['person_id']
                person_details = self.data_cache['person_details'].get(person_id, {})
                person_name = person_details.get('full_name', "Unknown")
                self.components['save_btn'].config(text=f"💾 Save changes for {person_name}")
            else:
                self.components['save_btn'].config(text="💾 Save Changes")
        else:
            self.components['save_btn'].config(text="💾 Save Changes")
    
    def _update_match_control_buttons_state(self):
        """Enable/disable Select All / Clear All based on matches presence"""
        if hasattr(self, 'match_vars') and self.match_vars:
            self.components['select_all_btn'].config(state='normal')
            self.components['clear_all_btn'].config(state='normal')
        else:
            self.components['select_all_btn'].config(state='disabled')
            self.components['clear_all_btn'].config(state='disabled')
    
    def _select_all_matches(self):
        """Select all match checkboxes"""
        if hasattr(self, 'match_vars'):
            for var in self.match_vars:
                var.set(True)
    
    def _clear_all_matches(self):
        """Clear all match checkboxes"""
        if hasattr(self, 'match_vars'):
            for var in self.match_vars:
                var.set(False)
    
    def _save_changes(self):
        """Save changes for the current person"""
        active_ids = self.filtered_matched_ids if self.filtered_matched_ids is not None else self.matched_ids
        if self.current_matched_index < len(active_ids):
            matched_id = active_ids[self.current_matched_index]
            matches_for_this_person = self.matches_by_matched[matched_id]
            
            # Show saving message
            self.components['save_btn'].config(text="💾 Saving...", state='disabled')
            self.main_frame.update_idletasks()
            
            # Initialize identified faces for this person if not exists
            if matched_id not in self.identified_faces_per_person:
                self.identified_faces_per_person[matched_id] = set()
            
            # Count changes for feedback
            changes_made = 0
            
            with self.db.get_db_connection() as conn:
                cursor = conn.cursor()
                
                # Process all matches (both checked and unchecked)
                for i, (match, var) in enumerate(zip(matches_for_this_person, self.match_vars)):
                    if var.get():
                        # Face is checked - assign to person
                        cursor.execute(
                            'UPDATE faces SET person_id = ? WHERE id = ?',
                            (match['person_id'], match['unidentified_id'])
                        )
                        
                        # Use cached person name
                        person_details = self.data_cache['person_details'].get(match['person_id'], {})
                        person_name = person_details.get('full_name', "Unknown")
                        
                        # Track this face as identified for this person
                        self.identified_faces_per_person[matched_id].add(match['unidentified_id'])
                        
                        print(f"✅ Identified as: {person_name}")
                        self.identified_count += 1
                        changes_made += 1
                    else:
                        # Face is unchecked - check if it was previously identified for this person
                        if match['unidentified_id'] in self.identified_faces_per_person[matched_id]:
                            # This face was previously identified for this person, now unchecking it
                            cursor.execute(
                                'UPDATE faces SET person_id = NULL WHERE id = ?',
                                (match['unidentified_id'],)
                            )
                            
                            # Remove from identified faces for this person
                            self.identified_faces_per_person[matched_id].discard(match['unidentified_id'])
                            
                            print(f"❌ Unidentified: {match['unidentified_filename']}")
                            changes_made += 1
                
                # Commit changes first
                conn.commit()
            
            # Update person encodings for all affected persons (outside of transaction)
            # This can be slow, so we show progress
            affected_person_ids = set(match['person_id'] for match in matches_for_this_person if match['person_id'])
            if affected_person_ids:
                self.components['save_btn'].config(text="💾 Updating encodings...")
                self.main_frame.update_idletasks()
                
                for person_id in affected_person_ids:
                    self.face_processor.update_person_encodings(person_id)
            
            # After saving, set original states to the current UI states
            current_snapshot = {}
            for match, var in zip(matches_for_this_person, self.match_vars):
                unique_key = f"{matched_id}_{match['unidentified_id']}"
                current_snapshot[unique_key] = var.get()
            self.checkbox_states_per_person[matched_id] = dict(current_snapshot)
            self.original_checkbox_states_per_person[matched_id] = dict(current_snapshot)
            
            # Show completion message
            if changes_made > 0:
                print(f"✅ Saved {changes_made} change(s)")
            
            # Restore button text and update state
            self._update_save_button_text()
            self.components['save_btn'].config(state='normal')
            self.main_frame.update_idletasks()
    
    def _go_back(self):
        """Go back to the previous person"""
        if self.current_matched_index > 0:
            self.current_matched_index -= 1
            self._update_display()
    
    def _go_next(self):
        """Go to the next person"""
        active_ids = self.filtered_matched_ids if self.filtered_matched_ids is not None else self.matched_ids
        if self.current_matched_index < len(active_ids) - 1:
            self.current_matched_index += 1
            self._update_display()
        else:
            self._finish_auto_match()
    
    def _apply_search_filter(self):
        """Filter people by last name and update navigation"""
        query = self.components['search_var'].get().strip().lower()
        if query:
            # Filter person_faces_list by last name
            filtered_people = []
            for person_id in self.matched_ids:
                # Get person name from cache
                person_details = self.data_cache['person_details'].get(person_id, {})
                person_name = person_details.get('full_name', '')
                
                # Extract last name from person_name
                if ',' in person_name:
                    last_name = person_name.split(',')[0].strip().lower()
                else:
                    # Try to extract last name from full name
                    name_parts = person_name.strip().split()
                    if name_parts:
                        last_name = name_parts[-1].lower()
                    else:
                        last_name = ''
                
                if query in last_name:
                    filtered_people.append(person_id)
            
            self.filtered_matched_ids = filtered_people
        else:
            self.filtered_matched_ids = None
        
        # Reset to first person in filtered list
        self.current_matched_index = 0
        if self.filtered_matched_ids:
            self._update_display()
        else:
            # No matches - clear display
            self.components['person_info_label'].config(text="No people match filter")
            self.components['person_canvas'].delete("all")
            self.components['person_canvas'].create_text(150, 150, text="No matches found", fill="gray")
            self.components['matches_canvas'].delete("all")
            self._update_control_states()
    
    def _clear_search_filter(self):
        """Clear filter and show all people"""
        self.components['search_var'].set("")
        self.filtered_matched_ids = None
        self.current_matched_index = 0
        self._update_display()
    
    def _finish_auto_match(self):
        """Finish the auto-match process"""
        print(f"\n✅ Auto-identified {self.identified_count} faces")
        messagebox.showinfo("Auto-Match Complete", f"Auto-identified {self.identified_count} faces")
        self._cleanup()
    
    def _quit_auto_match(self):
        """Quit the auto-match process"""
        # Check for unsaved changes before quitting
        if self._has_unsaved_changes():
            result = self.gui_core.create_large_messagebox(
                self.main_frame,
                "Unsaved Changes",
                "You have unsaved changes that will be lost if you quit.\n\n"
                "Yes: Save current changes and quit\n"
                "No: Quit without saving\n"
                "Cancel: Return to auto-match",
                "askyesnocancel"
            )
            if result is None:
                # Cancel
                return
            if result:
                # Save current person's changes, then quit
                self._save_changes()
        
        self._cleanup()
        
        # Navigate to home if callback is available (dashboard mode)
        if self.on_navigate_home:
            self.on_navigate_home()
    
    def _has_unsaved_changes(self):
        """Check if there are any unsaved changes"""
        for person_id, current_states in self.checkbox_states_per_person.items():
            if person_id in self.original_checkbox_states_per_person:
                original_states = self.original_checkbox_states_per_person[person_id]
                # Check if any checkbox state differs from its original state
                for key, current_value in current_states.items():
                    if key not in original_states or original_states[key] != current_value:
                        return True
            else:
                # If person has current states but no original states, there are changes
                if any(current_states.values()):
                    return True
        return False
    
    def _cleanup(self):
        """Clean up resources and reset state"""
        # Clean up face crops
        self.face_processor.cleanup_face_crops()
        
        # Reset state
        self.matches_by_matched = {}
        self.data_cache = {}
        self.current_matched_index = 0
        self.matched_ids = []
        self.filtered_matched_ids = None
        self.identified_faces_per_person = {}
        self.checkbox_states_per_person = {}
        self.original_checkbox_states_per_person = {}
        self.identified_count = 0
        
        # Clear displays
        self.components['person_info_label'].config(text="")
        self.components['person_canvas'].delete("all")
        self.components['matches_canvas'].delete("all")
        
        # Disable controls
        self.components['back_btn'].config(state='disabled')
        self.components['next_btn'].config(state='disabled')
        self.components['save_btn'].config(state='disabled')
        self.components['select_all_btn'].config(state='disabled')
        self.components['clear_all_btn'].config(state='disabled')
        
        # Clear search and disable search controls
        self.components['search_var'].set("")
        self.components['search_entry'].config(state='disabled')
        self.components['search_btn'].config(state='disabled')
        self.components['clear_btn'].config(state='disabled')
        self.components['search_help_label'].config(text="Search disabled - click 'Start Auto-Match' first")
        
        self.is_active = False
    
    def activate(self):
        """Activate the panel"""
        self.is_active = True
    
    def deactivate(self):
        """Deactivate the panel"""
        if self.is_active:
            self._cleanup()
            self.is_active = False
