```
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import os
import re
from collections import defaultdict

from highlight_files import highlight_files  # Ensure this module is available


class DuplicateFinder:
    """
    Handles the logic for finding duplicate files based on naming conventions.
    """

    def __init__(self):
        self.dir_cache = {}  # Cache to store directory listings
        # Default regex pattern to parse filenames (updated to include letters in extensions)
        self.pattern = r'^(?P<base>.+?)\s*(?:\((?P<index>\d{1,3})\))?\s*(?P<ext>(\.[a-zA-Z]+)+)?$'

    def set_pattern(self, pattern):
        """
        Sets a new regex pattern for parsing filenames.
        """
        try:
            re.compile(pattern)
            self.pattern = pattern
            return True, "Pattern updated successfully."
        except re.error as e:
            return False, f"Invalid regex pattern: {e}"

    def parse_filename(self, filename):
        """
        Parses the filename into base, index (if any), and extension(s).
        Returns a tuple: (base, index, extension)
        Updated to handle chained extensions like .bar.baz.qux.
        """
        pattern = re.compile(self.pattern)
        match = pattern.match(filename)
        if match:
            base = match.group('base')
            index = match.group('index')
            ext = match.group('ext') if match.group('ext') else ''
            return base, index, ext
        else:
            return filename, None, ''

    def find_duplicates(self, paths):
        """
        Given a list of file paths, returns a list of tuples containing
        the original file path, its existence status, the number of duplicates,
        and the list of duplicates.
        """
        results = []

        # Reset directory cache for each run
        self.dir_cache.clear()

        # Group paths by their directory to utilize caching
        dir_to_files = defaultdict(list)
        for path in paths:
            directory, _ = os.path.split(path)
            dir_to_files[directory].append(path)

        # Process each directory
        for directory, dir_paths in dir_to_files.items():
            if directory in self.dir_cache:
                siblings = self.dir_cache[directory]
            else:
                try:
                    siblings = os.listdir(directory)
                    self.dir_cache[directory] = siblings
                except Exception:
                    # If directory can't be read, mark duplicates as error
                    for path in dir_paths:
                        results.append((path, False, "Error reading directory", []))
                    continue  # Skip processing files in this directory

            for path in dir_paths:
                try:
                    exists = os.path.exists(path)
                    match_count, duplicates, base_file_path = self.get_duplicate_count(path, siblings, directory)
                    results.append((path, exists, match_count, duplicates, base_file_path))
                except Exception:
                    results.append((path, False, "Error processing file", [], ""))
        return results

    def get_duplicate_count(self, path, siblings, directory):
        """
        Given a file path and the list of sibling files, returns:
        - The number of duplicates
        - A list of duplicate file paths
        - The base file path (e.g., foo.pdf for foo(1).pdf)
        """
        _, filename = os.path.split(path)
        base, index, ext = self.parse_filename(filename)

        # Construct base file path
        base_filename = f"{base}{ext}"
        base_file_path = os.path.join(directory, base_filename)

        # Escape base and extension to handle any special characters
        escaped_base = re.escape(base)
        escaped_ext = re.escape(ext)

        # Pattern to match duplicates: basename + optional spaces + (index) + optional extension(s)
        dup_pattern = re.compile(
            rf'^{escaped_base}\s*(?:\(\d{{1,3}}\))?\s*{escaped_ext}$', re.IGNORECASE)

        duplicates = []

        for sibling in siblings:
            if sibling == filename:
                continue  # Skip the original file itself

            if dup_pattern.match(sibling):
                dup_file_path = os.path.join(directory, sibling)
                duplicates.append(dup_file_path)

        match_count = len(duplicates)
        return match_count, duplicates, base_file_path


class DuplicateFinderApp(tk.Tk):
    """
    The main application class that handles the UI and user interactions.
    """

    def __init__(self, finder):
        super().__init__()
        self.finder = finder
        self.title("Duplicate File Finder")
        self.geometry("1600x900")  # Increased width and height to accommodate new features
        self.row_data = {}  # Map Treeview row IDs to their data
        self.create_widgets()

    def create_widgets(self):
        # Frame for input, buttons, pattern input, and help
        input_frame = ttk.Frame(self)
        input_frame.pack(fill=tk.BOTH, expand=False, padx=10, pady=10)

        # Label for input file paths
        input_label = ttk.Label(input_frame, text="Enter file paths (one per line):")
        input_label.grid(row=0, column=0, sticky=tk.W)

        # Text widget for multiline input with scrollbar
        self.text_input = tk.Text(input_frame, height=10, width=120, wrap=tk.NONE)
        self.text_input.grid(row=1, column=0, columnspan=4, sticky="nsew")

        text_scrollbar = ttk.Scrollbar(input_frame, orient=tk.VERTICAL, command=self.text_input.yview)
        text_scrollbar.grid(row=1, column=4, sticky="ns")
        self.text_input.configure(yscrollcommand=text_scrollbar.set)

        # Label for regex pattern
        pattern_label = ttk.Label(input_frame, text="Filename Parsing Regex Pattern:")
        pattern_label.grid(row=2, column=0, sticky=tk.W, pady=(10, 0))

        # Entry widget for regex pattern
        self.pattern_input = tk.Entry(input_frame, width=120)
        self.pattern_input.grid(row=3, column=0, columnspan=4, sticky="nsew")
        self.pattern_input.insert(0, self.finder.pattern)  # Set default pattern

        # Button to update pattern
        update_pattern_button = ttk.Button(input_frame, text="Update Pattern", command=self.update_pattern)
        update_pattern_button.grid(row=3, column=4, padx=(5, 0), pady=5, sticky=tk.W)

        input_frame.columnconfigure(0, weight=1)
        input_frame.columnconfigure(1, weight=1)
        input_frame.columnconfigure(2, weight=1)
        input_frame.columnconfigure(3, weight=1)
        input_frame.columnconfigure(4, weight=0)

        # Frame for action buttons
        button_frame = ttk.Frame(input_frame)
        button_frame.grid(row=4, column=0, columnspan=5, pady=10, sticky=tk.W)

        # Button to process input
        process_button = ttk.Button(button_frame, text="Find Duplicates", command=self.process_paths)
        process_button.pack(side=tk.LEFT, padx=(0, 5))

        # Import button
        import_button = ttk.Button(button_frame, text="Import", command=self.import_paths)
        import_button.pack(side=tk.LEFT, padx=(0, 5))

        # Clear and Import button
        clear_import_button = ttk.Button(button_frame, text="Clear and Import", command=self.clear_and_import_paths)
        clear_import_button.pack(side=tk.LEFT, padx=(0, 5))

        # Deduplicate All button
        self.deduplicate_all_button = ttk.Button(button_frame, text="Deduplicate All", command=self.deduplicate_all_files)
        self.deduplicate_all_button.pack(side=tk.LEFT, padx=(0, 5))
        self.deduplicate_all_button.state(['disabled'])  # Initially disabled

        # Help button
        help_button = ttk.Button(button_frame, text="Help", command=self.show_help)
        help_button.pack(side=tk.LEFT)

        # Separator
        separator = ttk.Separator(self, orient='horizontal')
        separator.pack(fill=tk.X, padx=10, pady=10)

        # Frame for the table and deduplicate selected button
        table_frame = ttk.Frame(self)
        table_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        # Deduplicate Selected button
        dedup_selected_button = ttk.Button(table_frame, text="Deduplicate Selected", command=self.deduplicate_selected_files)
        dedup_selected_button.pack(anchor=tk.W, pady=(0, 5))

        # Define columns: File Path, Match Count, Deduplicate
        columns = ("file_path", "match_count", "deduplicate")
        self.tree = ttk.Treeview(table_frame, columns=columns, show='headings')

        # Define headings
        self.tree.heading("file_path", text="File Path")
        self.tree.heading("match_count", text="Match Count")
        self.tree.heading("deduplicate", text="Deduplicate")

        # Define column widths
        self.tree.column("file_path", width=1200, anchor=tk.W)
        self.tree.column("match_count", width=150, anchor=tk.CENTER)
        self.tree.column("deduplicate", width=200, anchor=tk.CENTER)

        self.tree.pack(fill=tk.BOTH, expand=True, side=tk.LEFT)

        # Bind double-click event
        self.tree.bind("<Double-1>", self.on_row_double_click)
        self.tree.bind("<Button-1>", self.on_single_click)

        # Add vertical scrollbar to the table
        table_scrollbar_y = ttk.Scrollbar(table_frame, orient=tk.VERTICAL, command=self.tree.yview)
        self.tree.configure(yscroll=table_scrollbar_y.set)
        table_scrollbar_y.pack(side=tk.RIGHT, fill=tk.Y)

        # Add horizontal scrollbar to the table
        table_scrollbar_x = ttk.Scrollbar(self, orient=tk.HORIZONTAL, command=self.tree.xview)
        self.tree.configure(xscroll=table_scrollbar_x.set)
        table_scrollbar_x.pack(fill=tk.X, padx=10)

        # Status bar
        self.status_var = tk.StringVar()
        self.status_var.set("Ready")
        status_bar = ttk.Label(self, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
        status_bar.pack(fill=tk.X, side=tk.BOTTOM, ipady=2)

    def update_pattern(self):
        """
        Updates the regex pattern used for parsing filenames based on user input.
        """
        new_pattern = self.pattern_input.get().strip()
        success, message = self.finder.set_pattern(new_pattern)
        if success:
            self.status_var.set(message)
        else:
            messagebox.showerror("Invalid Pattern", message)
            self.status_var.set(message)

    def process_paths(self):
        """
        Callback for the 'Find Duplicates' button.
        """
        # Clear existing table entries and row_data
        for item in self.tree.get_children():
            self.tree.delete(item)
        self.row_data.clear()

        # Get input paths
        input_text = self.text_input.get("1.0", tk.END)
        paths = [line.strip() for line in input_text.strip().splitlines() if line.strip()]

        if not paths:
            self.status_var.set("No file paths entered.")
            return

        # Process the paths using DuplicateFinder
        results = self.finder.find_duplicates(paths)

        # Populate the table with results and map row data
        for file_path, exists, match_count, duplicates, base_file_path in results:
            if isinstance(match_count, int):
                if match_count == 1:
                    # Check if the only duplicate is the base form
                    if duplicates and duplicates[0].lower() == base_file_path.lower():
                        deduplicate_text = "Already exact"
                    else:
                        deduplicate_text = "Click"
                else:
                    deduplicate_text = "N/A"
            else:
                deduplicate_text = "N/A"

            row_id = self.tree.insert("", tk.END, values=(file_path, match_count, deduplicate_text))
            self.row_data[row_id] = {
                "file_path": file_path,
                "exists": exists,
                "match_count": match_count if isinstance(match_count, int) else 0,
                "duplicates": duplicates,
                "base_file_path": base_file_path
            }

        self.status_var.set("Duplicate analysis completed.")
        self.update_deduplicate_buttons()

    def import_paths(self):
        """
        Callback for the 'Import' button.
        Appends the content of a selected .txt file to the input textbox.
        """
        file_path = filedialog.askopenfilename(
            title="Select a Text File to Import",
            filetypes=[("Text Files", "*.txt")]
        )
        if not file_path:
            self.status_var.set("Import canceled.")
            return  # User canceled the dialog

        try:
            with open(file_path, 'r', encoding='utf-8') as file:
                content = file.read()
            current_text = self.text_input.get("1.0", tk.END)
            if not current_text.endswith('\n'):
                content = '\n' + content
            self.text_input.insert(tk.END, content)
            self.status_var.set(f"Contents of '{os.path.basename(file_path)}' have been appended.")
        except Exception as e:
            self.status_var.set(f"Import error: {e}")

    def clear_and_import_paths(self):
        """
        Callback for the 'Clear and Import' button.
        Clears the input textbox and inserts the content of a selected .txt file.
        """
        file_path = filedialog.askopenfilename(
            title="Select a Text File to Import",
            filetypes=[("Text Files", "*.txt")]
        )
        if not file_path:
            self.status_var.set("Clear and import canceled.")
            return  # User canceled the dialog

        try:
            with open(file_path, 'r', encoding='utf-8') as file:
                content = file.read()
            self.text_input.delete("1.0", tk.END)
            self.text_input.insert(tk.END, content)
            self.status_var.set(f"Contents of '{os.path.basename(file_path)}' have been loaded.")
        except Exception as e:
            self.status_var.set(f"Clear and Import error: {e}")

    def deduplicate_all_files(self):
        """
        Deduplicate all eligible files by renaming them based on deduplication conditions.
        """
        # Collect eligible files: those with exactly one duplicate and have an index
        eligible_files = []
        for item, data in self.row_data.items():
            file_path = data.get("file_path")
            exists = data.get("exists")
            match_count = data.get("match_count")
            duplicates = data.get("duplicates")
            base_file_path = data.get("base_file_path")

            if not exists or not isinstance(match_count, int):
                continue
            if match_count != 1:
                continue

            _, index, _ = self.finder.parse_filename(os.path.basename(file_path))
            if index:
                # Check if the only duplicate is the base form
                if duplicates and duplicates[0].lower() != base_file_path.lower():
                    eligible_files.append(item)

        if not eligible_files:
            self.status_var.set("No eligible files for deduplication.")
            return

        rename_success = []
        rename_fail = []

        for item in eligible_files:
            data = self.row_data.get(item, {})
            original = data.get("file_path")
            duplicates = data.get("duplicates")
            base_file_path = data.get("base_file_path")

            if not duplicates:
                rename_fail.append((original, "No duplicate found."))
                continue

            duplicate = duplicates[0]
            directory, filename = os.path.split(original)
            base, index, ext = self.finder.parse_filename(filename)

            # New filename without index
            new_filename = f"{base}{ext}"
            new_file_path = os.path.join(directory, new_filename)

            # Ensure base file does not exist
            if os.path.exists(new_file_path):
                rename_fail.append((original, "Base file already exists."))
                continue

            # Proceed to rename
            try:
                os.rename(original, new_file_path)
                rename_success.append((original, new_file_path))

                # Update the treeview
                self.tree.item(item, values=(new_file_path, 0, "N/A"))
                self.row_data[item]["file_path"] = new_file_path
                self.row_data[item]["match_count"] = 0
                self.row_data[item]["duplicates"] = []
            except Exception as e:
                rename_fail.append((original, f"Rename failed: {e}"))

        # Prepare summary
        summary = f"Deduplication Complete. Successfully renamed {len(rename_success)} file(s)."
        if rename_fail:
            summary += f" Failed to rename {len(rename_fail)} file(s)."
            for fail_file, reason in rename_fail:
                summary += f"\n- {fail_file}: {reason}"

        # Display summary in status bar
        self.status_var.set(summary)
        self.update_deduplicate_buttons()

    def deduplicate_selected_files(self):
        """
        Callback for the 'Deduplicate Selected' button.
        Renames selected eligible files based on deduplication conditions.
        """
        selected_items = self.tree.selection()
        if not selected_items:
            self.status_var.set("No files selected for deduplication.")
            return

        rename_success = []
        rename_fail = []

        for item in selected_items:
            data = self.row_data.get(item, {})
            file_path = data.get("file_path")
            exists = data.get("exists")
            match_count = data.get("match_count")
            duplicates = data.get("duplicates")
            base_file_path = data.get("base_file_path")

            if not exists or not isinstance(match_count, int):
                continue
            if match_count != 1:
                continue

            _, index, _ = self.finder.parse_filename(os.path.basename(file_path))
            if index:
                # Check if the only duplicate is the base form
                if duplicates and duplicates[0].lower() != base_file_path.lower():
                    base, index, ext = self.finder.parse_filename(os.path.basename(file_path))
                    # New filename without index
                    new_filename = f"{base}{ext}"
                    new_file_path = os.path.join(os.path.dirname(file_path), new_filename)

                    # Ensure base file does not exist
                    if os.path.exists(new_file_path):
                        rename_fail.append((file_path, "Base file already exists."))
                        continue

                    # Proceed to rename
                    try:
                        os.rename(file_path, new_file_path)
                        rename_success.append((file_path, new_file_path))

                        # Update the treeview
                        self.tree.item(item, values=(new_file_path, 0, "N/A"))
                        self.row_data[item]["file_path"] = new_file_path
                        self.row_data[item]["match_count"] = 0
                        self.row_data[item]["duplicates"] = []
                    except Exception as e:
                        rename_fail.append((file_path, f"Rename failed: {e}"))

        # Prepare summary
        summary = f"Deduplication Complete. Successfully renamed {len(rename_success)} file(s)."
        if rename_fail:
            summary += f" Failed to rename {len(rename_fail)} file(s)."
            for fail_file, reason in rename_fail:
                summary += f"\n- {fail_file}: {reason}"

        # Display summary in status bar
        self.status_var.set(summary)
        self.update_deduplicate_buttons()

    def update_deduplicate_buttons(self):
        """
        Enables or disables deduplicate buttons based on eligible files.
        """
        any_eligible = False
        for data in self.row_data.values():
            exists = data.get("exists")
            match_count = data.get("match_count")
            if exists and isinstance(match_count, int) and match_count == 1 and data.get("deduplicate_text", "N/A") == "Click":
                any_eligible = True
                break

        if any_eligible:
            self.deduplicate_all_button.state(['!disabled'])
        else:
            self.deduplicate_all_button.state(['disabled'])

    def on_row_double_click(self, event):
        """
        Callback for double-clicking a table row.
        Differentiates between 'File Path' and 'Match Count' columns.
        """
        # Identify the row and column
        region = self.tree.identify("region", event.x, event.y)
        if region != "cell":
            return  # Only act on cell double-clicks

        row_id = self.tree.identify_row(event.y)
        column = self.tree.identify_column(event.x)

        if not row_id:
            return  # Click was outside any row

        column_name = self.tree.heading(column)["text"]

        data = self.row_data.get(row_id, {})
        file_path = data.get("file_path")
        exists = data.get("exists")
        duplicates = data.get("duplicates")
        match_count = data.get("match_count")
        base_file_path = data.get("base_file_path")

        if column_name == "File Path":
            if not exists:
                # File does not exist
                paths_to_highlight = []
            else:
                # Highlight the original file
                paths_to_highlight = [file_path]

            # Add existing duplicates to highlight
            if duplicates:
                existing_duplicates = [dup for dup in duplicates if os.path.exists(dup)]
                paths_to_highlight.extend(existing_duplicates)

            if paths_to_highlight:
                highlight_files(paths_to_highlight)  # Highlight in Explorer
            # If no files to highlight, do nothing

        elif column_name == "Match Count":
            if isinstance(match_count, int) and match_count > 0:
                duplicates = data.get("duplicates", [])
                # Determine if base file exists
                exact_match_exists = os.path.exists(base_file_path)
                display_data = []
                if exact_match_exists:
                    display_data.append((os.path.basename(base_file_path), "Yes", base_file_path))
                for dup in duplicates:
                    if dup.lower() != base_file_path.lower():
                        dup_exists = os.path.exists(dup)
                        display_data.append((os.path.basename(dup), "Yes" if dup_exists else "No", dup))
                if display_data:
                    self.show_match_dialog(file_path, display_data)
                else:
                    self.status_var.set("No matches to display.")
            else:
                self.status_var.set("No matches to display.")

    def on_single_click(self, event):
        """
        Callback for single-clicking a table row.
        Detects clicks on the 'Deduplicate' column to perform renaming.
        """
        row_id = self.tree.identify_row(event.y)
        column = self.tree.identify_column(event.x)

        if not row_id:
            return  # Click was outside any row

        column_number = int(column.replace("#", ""))
        column_name = self.tree.heading(column)["text"]

        if column_name == "Deduplicate":
            dedup_text = self.tree.set(row_id, "deduplicate")
            if dedup_text == "Click":
                self.on_row_deduplicate_click(row_id)
            elif dedup_text == "Already exact":
                messagebox.showinfo("Already Exact", "This file is already the exact match. No action needed.")
                self.status_var.set("No action needed: Already exact.")
            else:
                messagebox.showinfo("Rename Not Applicable", "This file does not meet deduplication criteria.")
                self.status_var.set("Rename not applicable for the selected file.")

    def on_row_deduplicate_click(self, item):
        """
        Callback to deduplicate a single file when the 'Deduplicate' cell is clicked.
        """
        data = self.row_data.get(item, {})
        file_path = data.get("file_path")
        exists = data.get("exists")
        duplicates = data.get("duplicates")
        match_count = data.get("match_count")
        base_file_path = data.get("base_file_path")

        if isinstance(match_count, int) and match_count == 1:
            base, index, ext = self.finder.parse_filename(os.path.basename(file_path))
            if index:
                # New filename without index
                new_filename = f"{base}{ext}"
                directory = os.path.dirname(file_path)
                new_file_path = os.path.join(directory, new_filename)

                # Check if only duplicate is the base form
                if duplicates and duplicates[0].lower() == base_file_path.lower():
                    # "Already exact" case
                    messagebox.showinfo("Already Exact", "This file is already the exact match. No action needed.")
                    self.status_var.set("No action needed: Already exact.")
                    return

                # Ensure base file does not exist
                if os.path.exists(new_file_path):
                    messagebox.showerror("Rename Error", f"Base file '{new_filename}' already exists.")
                    self.status_var.set(f"Rename failed for '{file_path}': Base file already exists.")
                    return

                # Proceed to rename
                try:
                    os.rename(file_path, new_file_path)
                    self.tree.item(item, values=(new_file_path, 0, "N/A"))
                    self.row_data[item]["file_path"] = new_file_path
                    self.row_data[item]["match_count"] = 0
                    self.row_data[item]["duplicates"] = []
                    self.status_var.set(f"Successfully renamed '{file_path}' to '{new_file_path}'.")
                    self.update_deduplicate_buttons()
                except Exception as e:
                    messagebox.showerror("Rename Error", f"Failed to rename '{file_path}': {e}")
                    self.status_var.set(f"Rename failed for '{file_path}': {e}")
        else:
            # Rename not applicable
            messagebox.showinfo("Rename Not Applicable", "This file does not meet deduplication criteria.")
            self.status_var.set("Rename not applicable for the selected file.")

    def show_match_dialog(self, original_path, matches):
        """
        Opens a dialog displaying duplicate matches in a table format.
        Shows basename, parent directory, and a 'Browse' button.
        """
        dialog = tk.Toplevel(self)
        dialog.title("Duplicate Matches")
        dialog.geometry("800x400")
        dialog.resizable(False, False)

        # Center the dialog relative to the main window
        self.center_window(dialog, 800, 400)

        # Frame for content
        content_frame = ttk.Frame(dialog, padding=10)
        content_frame.pack(fill=tk.BOTH, expand=True)

        # Labels for original file
        orig_label = ttk.Label(content_frame, text=f"Original File: {original_path}")
        orig_label.pack(anchor=tk.W, pady=(0, 5))

        # Treeview to display matches
        columns = ("basename", "parent_directory", "browse")
        tree = ttk.Treeview(content_frame, columns=columns, show='headings')

        tree.heading("basename", text="Basename")
        tree.heading("parent_directory", text="Parent Directory")
        tree.heading("browse", text="Browse")

        tree.column("basename", width=300, anchor=tk.W)
        tree.column("parent_directory", width=400, anchor=tk.W)
        tree.column("browse", width=100, anchor=tk.CENTER)

        tree.pack(fill=tk.BOTH, expand=True)

        # Insert match data
        for dup_basename, is_exact, dup_full_path in matches:
            parent_directory = os.path.dirname(dup_full_path)
            tree.insert("", tk.END, values=(dup_basename, parent_directory, "Browse"))

        # Scrollbar for the treeview
        scrollbar = ttk.Scrollbar(content_frame, orient=tk.VERTICAL, command=tree.yview)
        tree.configure(yscroll=scrollbar.set)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # Bind single-click event for 'Browse' button
        tree.bind("<Button-1>", lambda event: self.on_browse_click(event, tree))

        # OK button to close the dialog
        button_frame = ttk.Frame(dialog, padding=10)
        button_frame.pack(fill=tk.X, pady=5)

        ok_button = ttk.Button(button_frame, text="OK", command=dialog.destroy)
        ok_button.pack()

    def on_browse_click(self, event, tree):
        """
        Callback for clicking the 'Browse' button in the match dialog.
        """
        region = tree.identify("region", event.x, event.y)
        if region != "cell":
            return  # Only act on cell clicks

        row_id = tree.identify_row(event.y)
        column = tree.identify_column(event.x)

        if not row_id:
            return  # Click was outside any row

        column_number = int(column.replace("#", ""))
        column_name = tree.heading(column)["text"]

        if column_name == "Browse":
            # Get the full path from the parent directory and basename
            basename = tree.set(row_id, "basename")
            parent_directory = tree.set(row_id, "parent_directory")
            file_path = os.path.join(parent_directory, basename)
            if os.path.exists(file_path):
                highlight_files([file_path])
                self.status_var.set(f"Highlighted: {file_path}")
            else:
                messagebox.showerror("File Not Found", f"The file '{file_path}' does not exist.")
                self.status_var.set(f"File not found: {file_path}")

    def show_help(self):
        """
        Opens a Help window displaying information about the application.
        """
        help_text = (
            "Duplicate File Finder\n\n"
            "This application helps you identify duplicate files based on their naming conventions. "
            "You can enter multiple file paths, each on a new line, and the tool will analyze "
            "the specified directories to find duplicates with different numbered suffixes.\n\n"

            "**Input Format:**\n"
            " - Each file path should be on a separate line.\n"
            " - Example:\n"
            "   ```\n"
            "   C:/folder/file.txt\n"
            "   C:/folder/file(1).txt\n"
            "   C:/folder/foo (1).bar.baz.qux\n"
            "   ```\n\n"

            "**Duplicate Detection Logic:**\n"
            " - For a given file path, the application searches for duplicates in the same directory "
            "   following the pattern defined by the regex. By default, it looks for filenames like `basename<optional spaces><optional (index)><optional spaces><chained extensions>`.\n\n"

            "**How to Use:**\n"
            "1. Enter one or more valid file paths in the text area, each on a new line.\n"
            "2. Use the 'Import' button to append file paths from a `.txt` file.\n"
            "3. Use the 'Clear and Import' button to replace current file paths with those from a `.txt` file.\n"
            "4. (Optional) Modify the regex pattern if needed and click 'Update Pattern'.\n"
            "5. Click the 'Find Duplicates' button to start the analysis.\n"
            "6. Results will be displayed in the table below, showing each file and its match count.\n"
            "7. Double-click on a 'File Path' cell to highlight the file and its existing duplicates.\n"
            "8. Double-click on a 'Match Count' cell to view detailed duplicate information in a table.\n"
            "9. Click on the 'Deduplicate' cell to rename eligible files individually by clicking 'Click'.\n"
            "10. Select files and use the 'Deduplicate Selected' button to rename them.\n"
            "11. Use the 'Deduplicate All' button to rename all eligible files at once.\n\n"

            "**Deduplication Conditions:**\n"
            "- The base file (`basename<chained extensions>`) must not already exist.\n"
            "- Only files with an index (e.g., `foo (1).bar.baz.qux`) are eligible for deduplication.\n"
            "- Deduplication renames `foo (1).bar.baz.qux` to `foo.bar.baz.qux` if conditions are met.\n\n"

            "**Notes:**\n"
            "- Ensure you have the necessary permissions to rename files in the target directories.\n"
            "- The application uses the provided regex pattern to parse filenames. Make sure it's correctly configured for your filename structure.\n"
            "- Be cautious when performing deduplication to avoid accidental data loss.\n\n"

            "For any issues or further assistance, please refer to the documentation or contact support."
        )

        self.show_text_dialog("Help - Duplicate File Finder", help_text)

    def show_text_dialog(self, title, content):
        """
        Generic method to show a dialog with a read-only text area and an OK button.
        """
        dialog = tk.Toplevel(self)
        dialog.title(title)
        dialog.geometry("800x600")
        dialog.resizable(False, False)

        # Center the dialog relative to the main window
        self.center_window(dialog, 800, 600)

        # Frame for content
        content_frame = ttk.Frame(dialog, padding=10)
        content_frame.pack(fill=tk.BOTH, expand=True)

        # Scrollable Text widget for content
        content_frame.columnconfigure(0, weight=1)
        content_frame.rowconfigure(0, weight=1)

        text_widget = tk.Text(content_frame, wrap=tk.WORD, state='normal')
        text_widget.grid(row=0, column=0, sticky="nsew")

        # Insert content and make it read-only
        text_widget.insert(tk.END, content)
        text_widget.configure(state='disabled')

        # Scrollbar for the text widget
        scrollbar = ttk.Scrollbar(content_frame, orient=tk.VERTICAL, command=text_widget.yview)
        scrollbar.grid(row=0, column=1, sticky="ns")
        text_widget.configure(yscrollcommand=scrollbar.set)

        # OK button to close the dialog
        button_frame = ttk.Frame(dialog, padding=10)
        button_frame.pack(fill=tk.X, pady=5)

        ok_button = ttk.Button(button_frame, text="OK", command=dialog.destroy)
        ok_button.pack()

    def center_window(self, win, width, height):
        """
        Centers a window on the screen.
        """
        screen_width = win.winfo_screenwidth()
        screen_height = win.winfo_screenheight()

        x = (screen_width // 2) - (width // 2)
        y = (screen_height // 2) - (height // 2)

        win.geometry(f'{width}x{height}+{x}+{y}')

    def deduplicate_all_files(self):
        """
        Deduplicate all eligible files by renaming them based on deduplication conditions.
        """
        # Collect eligible files: those with exactly one duplicate and have an index
        eligible_files = []
        for item, data in self.row_data.items():
            file_path = data.get("file_path")
            exists = data.get("exists")
            match_count = data.get("match_count")
            duplicates = data.get("duplicates")
            base_file_path = data.get("base_file_path")

            if not exists or not isinstance(match_count, int):
                continue
            if match_count != 1:
                continue

            _, index, _ = self.finder.parse_filename(os.path.basename(file_path))
            if index:
                # Check if the only duplicate is the base form
                if duplicates and duplicates[0].lower() != base_file_path.lower():
                    eligible_files.append(item)

        if not eligible_files:
            self.status_var.set("No eligible files for deduplication.")
            return

        rename_success = []
        rename_fail = []

        for item in eligible_files:
            data = self.row_data.get(item, {})
            original = data.get("file_path")
            duplicates = data.get("duplicates")
            base_file_path = data.get("base_file_path")

            if not duplicates:
                rename_fail.append((original, "No duplicate found."))
                continue

            duplicate = duplicates[0]
            directory, filename = os.path.split(original)
            base, index, ext = self.finder.parse_filename(filename)

            # New filename without index
            new_filename = f"{base}{ext}"
            new_file_path = os.path.join(directory, new_filename)

            # Ensure base file does not exist
            if os.path.exists(new_file_path):
                rename_fail.append((original, "Base file already exists."))
                continue

            # Proceed to rename
            try:
                os.rename(original, new_file_path)
                rename_success.append((original, new_file_path))

                # Update the treeview
                self.tree.item(item, values=(new_file_path, 0, "N/A"))
                self.row_data[item]["file_path"] = new_file_path
                self.row_data[item]["match_count"] = 0
                self.row_data[item]["duplicates"] = []
            except Exception as e:
                rename_fail.append((original, f"Rename failed: {e}"))

        # Prepare summary
        summary = f"Deduplication Complete. Successfully renamed {len(rename_success)} file(s)."
        if rename_fail:
            summary += f" Failed to rename {len(rename_fail)} file(s)."
            for fail_file, reason in rename_fail:
                summary += f"\n- {fail_file}: {reason}"

        # Display summary in status bar
        self.status_var.set(summary)
        self.update_deduplicate_buttons()

    def deduplicate_selected_files(self):
        """
        Callback for the 'Deduplicate Selected' button.
        Renames selected eligible files based on deduplication conditions.
        """
        selected_items = self.tree.selection()
        if not selected_items:
            self.status_var.set("No files selected for deduplication.")
            return

        rename_success = []
        rename_fail = []

        for item in selected_items:
            data = self.row_data.get(item, {})
            file_path = data.get("file_path")
            exists = data.get("exists")
            match_count = data.get("match_count")
            duplicates = data.get("duplicates")
            base_file_path = data.get("base_file_path")

            if not exists or not isinstance(match_count, int):
                continue
            if match_count != 1:
                continue

            _, index, _ = self.finder.parse_filename(os.path.basename(file_path))
            if index:
                # Check if the only duplicate is the base form
                if duplicates and duplicates[0].lower() != base_file_path.lower():
                    base, index, ext = self.finder.parse_filename(os.path.basename(file_path))
                    # New filename without index
                    new_filename = f"{base}{ext}"
                    new_file_path = os.path.join(os.path.dirname(file_path), new_filename)

                    # Ensure base file does not exist
                    if os.path.exists(new_file_path):
                        rename_fail.append((file_path, "Base file already exists."))
                        continue

                    # Proceed to rename
                    try:
                        os.rename(file_path, new_file_path)
                        rename_success.append((file_path, new_file_path))

                        # Update the treeview
                        self.tree.item(item, values=(new_file_path, 0, "N/A"))
                        self.row_data[item]["file_path"] = new_file_path
                        self.row_data[item]["match_count"] = 0
                        self.row_data[item]["duplicates"] = []
                    except Exception as e:
                        rename_fail.append((file_path, f"Rename failed: {e}"))

        # Prepare summary
        summary = f"Deduplication Complete. Successfully renamed {len(rename_success)} file(s)."
        if rename_fail:
            summary += f" Failed to rename {len(rename_fail)} file(s)."
            for fail_file, reason in rename_fail:
                summary += f"\n- {fail_file}: {reason}"

        # Display summary in status bar
        self.status_var.set(summary)
        self.update_deduplicate_buttons()

    def update_deduplicate_buttons(self):
        """
        Enables or disables deduplicate buttons based on eligible files.
        """
        any_eligible = False
        for data in self.row_data.values():
            exists = data.get("exists")
            match_count = data.get("match_count")
            dedup_text = data.get("deduplicate_text", "N/A")
            if exists and isinstance(match_count, int) and match_count == 1 and dedup_text == "Click":
                any_eligible = True
                break

        if any_eligible:
            self.deduplicate_all_button.state(['!disabled'])
        else:
            self.deduplicate_all_button.state(['disabled'])

    def on_row_double_click(self, event):
        """
        Callback for double-clicking a table row.
        Differentiates between 'File Path' and 'Match Count' columns.
        """
        # Identify the row and column
        region = self.tree.identify("region", event.x, event.y)
        if region != "cell":
            return  # Only act on cell double-clicks

        row_id = self.tree.identify_row(event.y)
        column = self.tree.identify_column(event.x)

        if not row_id:
            return  # Click was outside any row

        column_name = self.tree.heading(column)["text"]

        data = self.row_data.get(row_id, {})
        file_path = data.get("file_path")
        exists = data.get("exists")
        duplicates = data.get("duplicates")
        match_count = data.get("match_count")
        base_file_path = data.get("base_file_path")

        if column_name == "File Path":
            if not exists:
                # File does not exist
                paths_to_highlight = []
            else:
                # Highlight the original file
                paths_to_highlight = [file_path]

            # Add existing duplicates to highlight
            if duplicates:
                existing_duplicates = [dup for dup in duplicates if os.path.exists(dup)]
                paths_to_highlight.extend(existing_duplicates)

            if paths_to_highlight:
                highlight_files(paths_to_highlight)  # Highlight in Explorer
            # If no files to highlight, do nothing

        elif column_name == "Match Count":
            if isinstance(match_count, int) and match_count > 0:
                duplicates = data.get("duplicates", [])
                base_file_path = data.get("base_file_path")
                exact_match_exists = os.path.exists(base_file_path)
                display_data = []
                if exact_match_exists:
                    display_data.append((os.path.basename(base_file_path), "Yes", base_file_path))
                for dup in duplicates:
                    if dup.lower() != base_file_path.lower():
                        dup_exists = os.path.exists(dup)
                        display_data.append((os.path.basename(dup), "Yes" if dup_exists else "No", dup))
                if display_data:
                    self.show_match_dialog(file_path, display_data)
                else:
                    self.status_var.set("No matches to display.")
            else:
                self.status_var.set("No matches to display.")

    def on_single_click(self, event):
        """
        Callback for single-clicking a table row.
        Detects clicks on the 'Deduplicate' column to perform renaming.
        """
        row_id = self.tree.identify_row(event.y)
        column = self.tree.identify_column(event.x)

        if not row_id:
            return  # Click was outside any row

        column_number = int(column.replace("#", ""))
        column_name = self.tree.heading(column)["text"]

        if column_name == "Deduplicate":
            dedup_text = self.tree.set(row_id, "deduplicate")
            if dedup_text == "Click":
                self.on_row_deduplicate_click(row_id)
            elif dedup_text == "Already exact":
                messagebox.showinfo("Already Exact", "This file is already the exact match. No action needed.")
                self.status_var.set("No action needed: Already exact.")
            else:
                messagebox.showinfo("Rename Not Applicable", "This file does not meet deduplication criteria.")
                self.status_var.set("Rename not applicable for the selected file.")

    def on_row_deduplicate_click(self, item):
        """
        Callback to deduplicate a single file when the 'Deduplicate' cell is clicked.
        """
        data = self.row_data.get(item, {})
        file_path = data.get("file_path")
        exists = data.get("exists")
        duplicates = data.get("duplicates")
        match_count = data.get("match_count")
        base_file_path = data.get("base_file_path")

        if isinstance(match_count, int) and match_count == 1:
            base, index, ext = self.finder.parse_filename(os.path.basename(file_path))
            if index:
                # New filename without index
                new_filename = f"{base}{ext}"
                directory = os.path.dirname(file_path)
                new_file_path = os.path.join(directory, new_filename)

                # Check if the only duplicate is the base form
                if duplicates and duplicates[0].lower() == base_file_path.lower():
                    # "Already exact" case
                    messagebox.showinfo("Already Exact", "This file is already the exact match. No action needed.")
                    self.status_var.set("No action needed: Already exact.")
                    return

                # Ensure base file does not exist
                if os.path.exists(new_file_path):
                    messagebox.showerror("Rename Error", f"Base file '{new_filename}' already exists.")
                    self.status_var.set(f"Rename failed for '{file_path}': Base file already exists.")
                    return

                # Proceed to rename
                try:
                    os.rename(file_path, new_file_path)
                    self.tree.item(item, values=(new_file_path, 0, "N/A"))
                    self.row_data[item]["file_path"] = new_file_path
                    self.row_data[item]["match_count"] = 0
                    self.row_data[item]["duplicates"] = []
                    self.status_var.set(f"Successfully renamed '{file_path}' to '{new_file_path}'.")
                    self.update_deduplicate_buttons()
                except Exception as e:
                    messagebox.showerror("Rename Error", f"Failed to rename '{file_path}': {e}")
                    self.status_var.set(f"Rename failed for '{file_path}': {e}")
        else:
            # Rename not applicable
            messagebox.showinfo("Rename Not Applicable", "This file does not meet deduplication criteria.")
            self.status_var.set("Rename not applicable for the selected file.")

    def show_match_dialog(self, original_path, matches):
        """
        Opens a dialog displaying duplicate matches in a table format.
        Shows basename, parent directory, and a 'Browse' button.
        """
        dialog = tk.Toplevel(self)
        dialog.title("Duplicate Matches")
        dialog.geometry("800x400")
        dialog.resizable(False, False)

        # Center the dialog relative to the main window
        self.center_window(dialog, 800, 400)

        # Frame for content
        content_frame = ttk.Frame(dialog, padding=10)
        content_frame.pack(fill=tk.BOTH, expand=True)

        # Labels for original file
        orig_label = ttk.Label(content_frame, text=f"Original File: {os.path.basename(original_path)}")
        orig_label.pack(anchor=tk.W, pady=(0, 5))

        # Treeview to display matches
        columns = ("basename", "parent_directory", "browse")
        tree = ttk.Treeview(content_frame, columns=columns, show='headings')

        tree.heading("basename", text="Basename")
        tree.heading("parent_directory", text="Parent Directory")
        tree.heading("browse", text="Browse")

        tree.column("basename", width=300, anchor=tk.W)
        tree.column("parent_directory", width=400, anchor=tk.W)
        tree.column("browse", width=100, anchor=tk.CENTER)

        tree.pack(fill=tk.BOTH, expand=True)

        # Insert match data
        for dup_basename, is_exact, dup_full_path in matches:
            parent_directory = os.path.dirname(dup_full_path)
            tree.insert("", tk.END, values=(dup_basename, parent_directory, "Browse"))

        # Scrollbar for the treeview
        scrollbar = ttk.Scrollbar(content_frame, orient=tk.VERTICAL, command=tree.yview)
        tree.configure(yscroll=scrollbar.set)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # Bind single-click event for 'Browse' button
        tree.bind("<Button-1>", lambda event: self.on_browse_click(event, tree))

        # OK button to close the dialog
        button_frame = ttk.Frame(dialog, padding=10)
        button_frame.pack(fill=tk.X, pady=5)

        ok_button = ttk.Button(button_frame, text="OK", command=dialog.destroy)
        ok_button.pack()

    def on_browse_click(self, event, tree):
        """
        Callback for clicking the 'Browse' button in the match dialog.
        """
        region = tree.identify("region", event.x, event.y)
        if region != "cell":
            return  # Only act on cell clicks

        row_id = tree.identify_row(event.y)
        column = tree.identify_column(event.x)

        if not row_id:
            return  # Click was outside any row

        column_number = int(column.replace("#", ""))
        column_name = tree.heading(column)["text"]

        if column_name == "Browse":
            # Get the full path from the parent directory and basename
            basename = tree.set(row_id, "basename")
            parent_directory = tree.set(row_id, "parent_directory")
            file_path = os.path.join(parent_directory, basename)
            if os.path.exists(file_path):
                highlight_files([file_path])
                self.status_var.set(f"Highlighted: {file_path}")
            else:
                messagebox.showerror("File Not Found", f"The file '{file_path}' does not exist.")
                self.status_var.set(f"File not found: {file_path}")

    def show_help(self):
        """
        Opens a Help window displaying information about the application.
        """
        help_text = (
            "Duplicate File Finder\n\n"
            "This application helps you identify duplicate files based on their naming conventions. "
            "You can enter multiple file paths, each on a new line, and the tool will analyze "
            "the specified directories to find duplicates with different numbered suffixes.\n\n"

            "**Input Format:**\n"
            " - Each file path should be on a separate line.\n"
            " - Example:\n"
            "   ```\n"
            "   C:/folder/file.txt\n"
            "   C:/folder/file(1).txt\n"
            "   C:/folder/foo (1).bar.baz.qux\n"
            "   ```\n\n"

            "**Duplicate Detection Logic:**\n"
            " - For a given file path, the application searches for duplicates in the same directory "
            "   following the pattern defined by the regex. By default, it looks for filenames like `basename<optional spaces><optional (index)><optional spaces><chained extensions>`.\n\n"

            "**How to Use:**\n"
            "1. Enter one or more valid file paths in the text area, each on a new line.\n"
            "2. Use the 'Import' button to append file paths from a `.txt` file.\n"
            "3. Use the 'Clear and Import' button to replace current file paths with those from a `.txt` file.\n"
            "4. (Optional) Modify the regex pattern if needed and click 'Update Pattern'.\n"
            "5. Click the 'Find Duplicates' button to start the analysis.\n"
            "6. Results will be displayed in the table below, showing each file and its match count.\n"
            "7. Double-click on a 'File Path' cell to highlight the file and its existing duplicates.\n"
            "8. Double-click on a 'Match Count' cell to view detailed duplicate information in a table.\n"
            "9. Click on the 'Deduplicate' cell to rename eligible files individually by clicking 'Click'.\n"
            "10. Select files and use the 'Deduplicate Selected' button to rename them.\n"
            "11. Use the 'Deduplicate All' button to rename all eligible files at once.\n\n"

            "**Deduplication Conditions:**\n"
            "- The base file (`basename<chained extensions>`) must not already exist.\n"
            "- Only files with an index (e.g., `foo (1).bar.baz.qux`) are eligible for deduplication.\n"
            "- Deduplication renames `foo (1).bar.baz.qux` to `foo.bar.baz.qux` if conditions are met.\n\n"

            "**Notes:**\n"
            "- Ensure you have the necessary permissions to rename files in the target directories.\n"
            "- The application uses the provided regex pattern to parse filenames. Make sure it's correctly configured for your filename structure.\n"
            "- Be cautious when performing deduplication to avoid accidental data loss.\n\n"

            "For any issues or further assistance, please refer to the documentation or contact support."
        )

        self.show_text_dialog("Help - Duplicate File Finder", help_text)

    def show_text_dialog(self, title, content):
        """
        Generic method to show a dialog with a read-only text area and an OK button.
        """
        dialog = tk.Toplevel(self)
        dialog.title(title)
        dialog.geometry("800x600")
        dialog.resizable(False, False)

        # Center the dialog relative to the main window
        self.center_window(dialog, 800, 600)

        # Frame for content
        content_frame = ttk.Frame(dialog, padding=10)
        content_frame.pack(fill=tk.BOTH, expand=True)

        # Scrollable Text widget for content
        content_frame.columnconfigure(0, weight=1)
        content_frame.rowconfigure(0, weight=1)

        text_widget = tk.Text(content_frame, wrap=tk.WORD, state='normal')
        text_widget.grid(row=0, column=0, sticky="nsew")

        # Insert content and make it read-only
        text_widget.insert(tk.END, content)
        text_widget.configure(state='disabled')

        # Scrollbar for the text widget
        scrollbar = ttk.Scrollbar(content_frame, orient=tk.VERTICAL, command=text_widget.yview)
        scrollbar.grid(row=0, column=1, sticky="ns")
        text_widget.configure(yscrollcommand=scrollbar.set)

        # OK button to close the dialog
        button_frame = ttk.Frame(dialog, padding=10)
        button_frame.pack(fill=tk.X, pady=5)

        ok_button = ttk.Button(button_frame, text="OK", command=dialog.destroy)
        ok_button.pack()

    def center_window(self, win, width, height):
        """
        Centers a window on the screen.
        """
        screen_width = win.winfo_screenwidth()
        screen_height = win.winfo_screenheight()

        x = (screen_width // 2) - (width // 2)
        y = (screen_height // 2) - (height // 2)

        win.geometry(f'{width}x{height}+{x}+{y}')

    def deduplicate_all_files(self):
        """
        Deduplicate all eligible files by renaming them based on deduplication conditions.
        """
        # Collect eligible files: those with exactly one duplicate and have an index
        eligible_files = []
        for item, data in self.row_data.items():
            file_path = data.get("file_path")
            exists = data.get("exists")
            match_count = data.get("match_count")
            duplicates = data.get("duplicates")
            base_file_path = data.get("base_file_path")

            if not exists or not isinstance(match_count, int):
                continue
            if match_count != 1:
                continue

            _, index, _ = self.finder.parse_filename(os.path.basename(file_path))
            if index:
                # Check if the only duplicate is the base form
                if duplicates and duplicates[0].lower() != base_file_path.lower():
                    eligible_files.append(item)

        if not eligible_files:
            self.status_var.set("No eligible files for deduplication.")
            return

        rename_success = []
        rename_fail = []

        for item in eligible_files:
            data = self.row_data.get(item, {})
            original = data.get("file_path")
            duplicates = data.get("duplicates")
            base_file_path = data.get("base_file_path")

            if not duplicates:
                rename_fail.append((original, "No duplicate found."))
                continue

            duplicate = duplicates[0]
            directory, filename = os.path.split(original)
            base, index, ext = self.finder.parse_filename(filename)

            # New filename without index
            new_filename = f"{base}{ext}"
            new_file_path = os.path.join(directory, new_filename)

            # Ensure base file does not exist
            if os.path.exists(new_file_path):
                rename_fail.append((original, "Base file already exists."))
                continue

            # Proceed to rename
            try:
                os.rename(original, new_file_path)
                rename_success.append((original, new_file_path))

                # Update the treeview
                self.tree.item(item, values=(new_file_path, 0, "N/A"))
                self.row_data[item]["file_path"] = new_file_path
                self.row_data[item]["match_count"] = 0
                self.row_data[item]["duplicates"] = []
            except Exception as e:
                rename_fail.append((original, f"Rename failed: {e}"))

        # Prepare summary
        summary = f"Deduplication Complete. Successfully renamed {len(rename_success)} file(s)."
        if rename_fail:
            summary += f" Failed to rename {len(rename_fail)} file(s)."
            for fail_file, reason in rename_fail:
                summary += f"\n- {fail_file}: {reason}"

        # Display summary in status bar
        self.status_var.set(summary)
        self.update_deduplicate_buttons()

    def deduplicate_selected_files(self):
        """
        Callback for the 'Deduplicate Selected' button.
        Renames selected eligible files based on deduplication conditions.
        """
        selected_items = self.tree.selection()
        if not selected_items:
            self.status_var.set("No files selected for deduplication.")
            return

        rename_success = []
        rename_fail = []

        for item in selected_items:
            data = self.row_data.get(item, {})
            file_path = data.get("file_path")
            exists = data.get("exists")
            match_count = data.get("match_count")
            duplicates = data.get("duplicates")
            base_file_path = data.get("base_file_path")

            if not exists or not isinstance(match_count, int):
                continue
            if match_count != 1:
                continue

            _, index, _ = self.finder.parse_filename(os.path.basename(file_path))
            if index:
                # Check if the only duplicate is the base form
                if duplicates and duplicates[0].lower() != base_file_path.lower():
                    base, index, ext = self.finder.parse_filename(os.path.basename(file_path))
                    # New filename without index
                    new_filename = f"{base}{ext}"
                    new_file_path = os.path.join(os.path.dirname(file_path), new_filename)

                    # Ensure base file does not exist
                    if os.path.exists(new_file_path):
                        rename_fail.append((file_path, "Base file already exists."))
                        continue

                    # Proceed to rename
                    try:
                        os.rename(file_path, new_file_path)
                        rename_success.append((file_path, new_file_path))

                        # Update the treeview
                        self.tree.item(item, values=(new_file_path, 0, "N/A"))
                        self.row_data[item]["file_path"] = new_file_path
                        self.row_data[item]["match_count"] = 0
                        self.row_data[item]["duplicates"] = []
                    except Exception as e:
                        rename_fail.append((file_path, f"Rename failed: {e}"))

        # Prepare summary
        summary = f"Deduplication Complete. Successfully renamed {len(rename_success)} file(s)."
        if rename_fail:
            summary += f" Failed to rename {len(rename_fail)} file(s)."
            for fail_file, reason in rename_fail:
                summary += f"\n- {fail_file}: {reason}"

        # Display summary in status bar
        self.status_var.set(summary)
        self.update_deduplicate_buttons()

    def update_deduplicate_buttons(self):
        """
        Enables or disables deduplicate buttons based on eligible files.
        """
        any_eligible = False
        for data in self.row_data.values():
            exists = data.get("exists")
            match_count = data.get("match_count")
            dedup_text = data.get("deduplicate_text", "N/A")
            if exists and isinstance(match_count, int) and match_count == 1 and dedup_text == "Click":
                any_eligible = True
                break

        if any_eligible:
            self.deduplicate_all_button.state(['!disabled'])
        else:
            self.deduplicate_all_button.state(['disabled'])

    def on_row_double_click(self, event):
        """
        Callback for double-clicking a table row.
        Differentiates between 'File Path' and 'Match Count' columns.
        """
        # Identify the row and column
        region = self.tree.identify("region", event.x, event.y)
        if region != "cell":
            return  # Only act on cell double-clicks

        row_id = self.tree.identify_row(event.y)
        column = self.tree.identify_column(event.x)

        if not row_id:
            return  # Click was outside any row

        column_name = self.tree.heading(column)["text"]

        data = self.row_data.get(row_id, {})
        file_path = data.get("file_path")
        exists = data.get("exists")
        duplicates = data.get("duplicates")
        match_count = data.get("match_count")
        base_file_path = data.get("base_file_path")

        if column_name == "File Path":
            if not exists:
                # File does not exist
                paths_to_highlight = []
            else:
                # Highlight the original file
                paths_to_highlight = [file_path]

            # Add existing duplicates to highlight
            if duplicates:
                existing_duplicates = [dup for dup in duplicates if os.path.exists(dup)]
                paths_to_highlight.extend(existing_duplicates)

            if paths_to_highlight:
                highlight_files(paths_to_highlight)  # Highlight in Explorer
            # If no files to highlight, do nothing

        elif column_name == "Match Count":
            if isinstance(match_count, int) and match_count > 0:
                duplicates = data.get("duplicates", [])
                base_file_path = data.get("base_file_path")
                exact_match_exists = os.path.exists(base_file_path)
                display_data = []
                if exact_match_exists:
                    display_data.append((os.path.basename(base_file_path), "Yes", base_file_path))
                for dup in duplicates:
                    if dup.lower() != base_file_path.lower():
                        dup_exists = os.path.exists(dup)
                        display_data.append((os.path.basename(dup), "Yes" if dup_exists else "No", dup))
                if display_data:
                    self.show_match_dialog(file_path, display_data)
                else:
                    self.status_var.set("No matches to display.")
            else:
                self.status_var.set("No matches to display.")

    def on_single_click(self, event):
        """
        Callback for single-clicking a table row.
        Detects clicks on the 'Deduplicate' column to perform renaming.
        """
        row_id = self.tree.identify_row(event.y)
        column = self.tree.identify_column(event.x)

        if not row_id:
            return  # Click was outside any row

        column_number = int(column.replace("#", ""))
        column_name = self.tree.heading(column)["text"]

        if column_name == "Deduplicate":
            dedup_text = self.tree.set(row_id, "deduplicate")
            if dedup_text == "Click":
                self.on_row_deduplicate_click(row_id)
            elif dedup_text == "Already exact":
                messagebox.showinfo("Already Exact", "This file is already the exact match. No action needed.")
                self.status_var.set("No action needed: Already exact.")
            else:
                messagebox.showinfo("Rename Not Applicable", "This file does not meet deduplication criteria.")
                self.status_var.set("Rename not applicable for the selected file.")

    def show_match_dialog(self, original_path, matches):
        """
        Opens a dialog displaying duplicate matches in a table format.
        Shows basename, parent directory, and a 'Browse' button.
        """
        dialog = tk.Toplevel(self)
        dialog.title("Duplicate Matches")
        dialog.geometry("800x400")
        dialog.resizable(False, False)

        # Center the dialog relative to the main window
        self.center_window(dialog, 800, 400)

        # Frame for content
        content_frame = ttk.Frame(dialog, padding=10)
        content_frame.pack(fill=tk.BOTH, expand=True)

        # Labels for original file
        orig_label = ttk.Label(content_frame, text=f"Original File: {os.path.basename(original_path)}")
        orig_label.pack(anchor=tk.W, pady=(0, 5))

        # Treeview to display matches
        columns = ("basename", "parent_directory", "browse")
        tree = ttk.Treeview(content_frame, columns=columns, show='headings')

        tree.heading("basename", text="Basename")
        tree.heading("parent_directory", text="Parent Directory")
        tree.heading("browse", text="Browse")

        tree.column("basename", width=300, anchor=tk.W)
        tree.column("parent_directory", width=400, anchor=tk.W)
        tree.column("browse", width=100, anchor=tk.CENTER)

        tree.pack(fill=tk.BOTH, expand=True)

        # Insert match data
        for dup_basename, is_exact, dup_full_path in matches:
            parent_directory = os.path.dirname(dup_full_path)
            tree.insert("", tk.END, values=(dup_basename, parent_directory, "Browse"))

        # Scrollbar for the treeview
        scrollbar = ttk.Scrollbar(content_frame, orient=tk.VERTICAL, command=tree.yview)
        tree.configure(yscroll=scrollbar.set)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # Bind single-click event for 'Browse' button
        tree.bind("<Button-1>", lambda event: self.on_browse_click(event, tree))

        # OK button to close the dialog
        button_frame = ttk.Frame(dialog, padding=10)
        button_frame.pack(fill=tk.X, pady=5)

        ok_button = ttk.Button(button_frame, text="OK", command=dialog.destroy)
        ok_button.pack()

    def on_browse_click(self, event, tree):
        """
        Callback for clicking the 'Browse' button in the match dialog.
        """
        region = tree.identify("region", event.x, event.y)
        if region != "cell":
            return  # Only act on cell clicks

        row_id = tree.identify_row(event.y)
        column = tree.identify_column(event.x)

        if not row_id:
            return  # Click was outside any row

        column_number = int(column.replace("#", ""))
        column_name = tree.heading(column)["text"]

        if column_name == "Browse":
            # Get the full path from the parent directory and basename
            basename = tree.set(row_id, "basename")
            parent_directory = tree.set(row_id, "parent_directory")
            file_path = os.path.join(parent_directory, basename)
            if os.path.exists(file_path):
                highlight_files([file_path])
                self.status_var.set(f"Highlighted: {file_path}")
            else:
                messagebox.showerror("File Not Found", f"The file '{file_path}' does not exist.")
                self.status_var.set(f"File not found: {file_path}")

    def on_row_deduplicate_click(self, item):
        """
        Callback to deduplicate a single file when the 'Deduplicate' cell is clicked.
        """
        data = self.row_data.get(item, {})
        file_path = data.get("file_path")
        exists = data.get("exists")
        duplicates = data.get("duplicates")
        match_count = data.get("match_count")
        base_file_path = data.get("base_file_path")

        if isinstance(match_count, int) and match_count == 1:
            base, index, ext = self.finder.parse_filename(os.path.basename(file_path))
            if index:
                # New filename without index
                new_filename = f"{base}{ext}"
                directory = os.path.dirname(file_path)
                new_file_path = os.path.join(directory, new_filename)

                # Check if the only duplicate is the base form
                if duplicates and duplicates[0].lower() == base_file_path.lower():
                    # "Already exact" case
                    messagebox.showinfo("Already Exact", "This file is already the exact match. No action needed.")
                    self.status_var.set("No action needed: Already exact.")
                    return

                # Ensure base file does not exist
                if os.path.exists(new_file_path):
                    messagebox.showerror("Rename Error", f"Base file '{new_filename}' already exists.")
                    self.status_var.set(f"Rename failed for '{file_path}': Base file already exists.")
                    return

                # Proceed to rename
                try:
                    os.rename(file_path, new_file_path)
                    self.tree.item(item, values=(new_file_path, 0, "N/A"))
                    self.row_data[item]["file_path"] = new_file_path
                    self.row_data[item]["match_count"] = 0
                    self.row_data[item]["duplicates"] = []
                    self.status_var.set(f"Successfully renamed '{file_path}' to '{new_file_path}'.")
                    self.update_deduplicate_buttons()
                except Exception as e:
                    messagebox.showerror("Rename Error", f"Failed to rename '{file_path}': {e}")
                    self.status_var.set(f"Rename failed for '{file_path}': {e}")
        else:
            # Rename not applicable
            messagebox.showinfo("Rename Not Applicable", "This file does not meet deduplication criteria.")
            self.status_var.set("Rename not applicable for the selected file.")


def main():
    finder = DuplicateFinder()
    app = DuplicateFinderApp(finder)
    app.mainloop()


if __name__ == "__main__":
    main()
```

do the following
1. remove `Update Pattern` button. the pattern is checked by `Find Duplicates` button first so if pattern fails then no Find Duplicates
2. add a way to deselect items. currently when i click a row it is deselected, but i cant find a way to deselect
3. `Deduplicate Selected` is texted `Deduplicate All` if none selected otherwise its `Deduplicate Selected`
4. update the match count display to the following
```
Parent folder: <path here> <Browse button>
<Table>
```
Table content:
```
Base name | Exact? | Browse |
```
5. the rename behavior of deduplicate is wrong. It should rename the only match (that is not already in base form), not the input.
example, if i have `foo.bar` as input path, and it finds only 1 match `foo (1) .bar`, rename would try to rename `foo.bar` which does not exist and throws me an error. it should rename `foo (1) .bar` instead to `foo.bar`.
the rename filter and conditions are correct tho (showing N/A,  Already exist and Click), just the behavior is not.