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

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

    def __init__(self):
        self.dir_cache = {}  # Cache to store directory listings

    def parse_filename(self, filename):
        """
        Parses the filename into base, index (if any), and extension.
        Returns a tuple: (base, index, extension)
        """
        pattern = re.compile(r'^(?P<base>.+?)(?:\((?P<index>\d{1,3})\))?(?P<ext>\.[^.]+)?$')
        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 and its duplicate list string.
        """
        results = []

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

        # Validate all paths first
        invalid_paths = [path for path in paths if not os.path.exists(path)]
        if invalid_paths:
            raise FileNotFoundError(f"The following paths do not exist:\n" + "\n".join(invalid_paths))

        # 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 as e:
                    for path in dir_paths:
                        results.append((path, "Error reading directory"))
                    continue  # Skip processing files in this directory

            for path in dir_paths:
                try:
                    dup_str = self.get_duplicate_string(path, siblings)
                    results.append((path, dup_str))
                except Exception as e:
                    results.append((path, f"Error processing file: {e}"))

        return results

    def get_duplicate_string(self, path, siblings):
        """
        Given a file path and the list of sibling files, returns the duplicate list string as specified.
        """
        directory, filename = os.path.split(path)
        base, index, ext = self.parse_filename(filename)

        if index:
            base_filename = f"{base}{ext}"
        else:
            base_filename = filename

        duplicates = []
        indices = []

        # If input has index, check if base file exists
        if index:
            if base_filename in siblings:
                duplicates.append("base")

        # Pattern to match duplicates
        # Escape base and extension to handle any special characters
        escaped_base = re.escape(base)
        escaped_ext = re.escape(ext)
        dup_pattern = re.compile(rf'^{escaped_base}\((\d{{1,3}})\){escaped_ext}$')

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

            match = dup_pattern.match(sibling)
            if match:
                dup_index = match.group(1).lstrip('0') or '0'  # Remove leading zeros
                if dup_index.isdigit():
                    dup_index_int = int(dup_index)
                    if 0 <= dup_index_int <= 999:
                        indices.append(str(dup_index_int))

        # Sort indices numerically and remove duplicates
        unique_sorted_indices = sorted(set(indices), key=lambda x: int(x))

        if index:
            if unique_sorted_indices:
                duplicates.append(",".join(unique_sorted_indices))
        else:
            duplicates = unique_sorted_indices

        dup_list_str = ",".join(duplicates) if duplicates else "None"
        return dup_list_str


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("950x650")  # Adjusted size for better layout with scrollable input
        self.create_widgets()

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

        # Label for input
        input_label = ttk.Label(input_frame, text="Enter file paths (one per line):")
        input_label.pack(anchor=tk.W)

        # Frame for text input and its scrollbar
        text_frame = ttk.Frame(input_frame)
        text_frame.pack(fill=tk.BOTH, expand=True, pady=5)

        # Text widget for multiline input
        self.text_input = tk.Text(text_frame, height=15, wrap=tk.NONE)
        self.text_input.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

        # Vertical scrollbar for the text widget
        text_scrollbar = ttk.Scrollbar(text_frame, orient=tk.VERTICAL, command=self.text_input.yview)
        text_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # Configure the text widget to work with the scrollbar
        self.text_input.configure(yscrollcommand=text_scrollbar.set)

        # Frame for buttons
        button_frame = ttk.Frame(input_frame)
        button_frame.pack(fill=tk.X, pady=5)

        # 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))

        # 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
        table_frame = ttk.Frame(self)
        table_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        # Define columns
        columns = ("file_path", "dup_list")
        self.tree = ttk.Treeview(table_frame, columns=columns, show='headings')
        self.tree.heading("file_path", text="File Path")
        self.tree.heading("dup_list", text="Duplicate List")
        self.tree.column("file_path", width=700, anchor=tk.W)
        self.tree.column("dup_list", width=200, anchor=tk.W)
        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)

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

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

        # 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:
            messagebox.showerror("Input Error", "Please enter at least one file path.")
            return

        # Process the paths using DuplicateFinder
        try:
            results = self.finder.find_duplicates(paths)
        except FileNotFoundError as fe:
            messagebox.showerror("Invalid Paths", str(fe))
            return
        except Exception as e:
            messagebox.showerror("Error", f"An unexpected error occurred:\n{e}")
            return

        # Populate the table with results
        for file_path, dup_str in results:
            self.tree.insert("", tk.END, values=(file_path, dup_str))

        messagebox.showinfo("Processing Complete", "Duplicate analysis completed.")

    def on_row_double_click(self, event):
        """
        Callback for double-clicking a table row.
        Opens the file location in Windows Explorer and selects the file.
        """
        selected_item = self.tree.selection()
        if not selected_item:
            return
        item = selected_item[0]
        file_path = self.tree.item(item, "values")[0]
        if os.path.exists(file_path):
            # Use explorer to select the file
            try:
                # For paths with spaces or special characters, enclose in quotes
                highlight_files(os.path.normpath(file_path))
            except e:
                messagebox.showerror("Error", f"Could not open file location.\n{e}")
        else:
            messagebox.showerror("File Not Found", f"The file does not exist:\n{file_path}")

    def show_help(self):
        """
        Opens a Help window displaying information about the application.
        """
        help_window = tk.Toplevel(self)
        help_window.title("Help - Duplicate File Finder")
        help_window.geometry("500x400")
        help_window.resizable(False, False)

        # Center the help window relative to the main window
        self.center_window(help_window, 500, 400)

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

        # Label with help text
        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"
            "   ```\n\n"

            "**Duplicate Detection Logic:**\n"
            " - For a given file path, the application searches for duplicates in the same directory "
            "   following the pattern `file(<index>).extension`.\n"
            " - Handles files both with and without existing numbered indices.\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. Click the 'Find Duplicates' button to start the analysis.\n"
            "3. Results will be displayed in the table below, showing each file and its duplicates.\n"
            "4. Double-click on any row to open the file's location in Windows Explorer.\n\n"

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

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

        # Scrollable Text widget for help content
        help_text_widget = tk.Text(content_frame, wrap=tk.WORD)
        help_text_widget.grid(row=0, column=0, sticky="nsew")

        # Insert help text and make it read-only
        help_text_widget.insert(tk.END, help_text)
        help_text_widget.configure(state='disabled')

        # Scrollbar for the help text
        help_scrollbar = ttk.Scrollbar(content_frame, orient=tk.VERTICAL, command=help_text_widget.yview)
        help_scrollbar.grid(row=0, column=1, sticky="ns")

        help_text_widget.configure(yscrollcommand=help_scrollbar.set)

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

        ok_button = ttk.Button(button_frame, text="OK", command=help_window.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 main():
    finder = DuplicateFinder()
    app = DuplicateFinderApp(finder)
    app.mainloop()


if __name__ == "__main__":
    main()

```

add 3 more buttons:
1. `Import`: which appends the content of txt to the input textbox 
2. `Clear and Import`: which puts the content of txt to the input textbox
3. `Deduplicate`: for all names in results with no duplicates (i.e. empty duplicate cell), rename them to base name if they are not already
index name example `path/to/file(1)`
we want to rename it to base name `path/to/file` given that the base name file does not exist, nor index duplicates `path/to/file(<index>)`

also handle the case if it has extension `path/to/file(1).pdf`
rename it to base name `path/to/file.pdf` given that the base name file does not exist, nor index duplicates `path/to/file(<index>).pdf`