```
from tkinter import ttk, filedialog, messagebox
from collections import defaultdict
from explorer_utils import highlight_files, open_folder

import tkinter as tk
import os
import re
import mmh3
import threading


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 MatchingFilesDialog(tk.Toplevel):
    def __init__(self, master, input_path, matches):
        super().__init__(master)
        self.master = master
        self.input_path = input_path
        self.matches = matches
        self.tree = None
        self._create_ui()
        self._populate_initial_data()
        self._start_background_tasks()

    def _create_ui(self):
        self.title("Duplicate Matches")
        self.resizable(True, True)
        self.master.center_window(self, 800, 400)

        content_frame = ttk.Frame(self, padding=10)
        content_frame.pack(fill=tk.BOTH, expand=True)

        input_label = ttk.Label(content_frame, text=f"Input file: {os.path.basename(self.input_path)}")
        input_label.pack(anchor=tk.W, pady=(0, 5))

        parent_frame = ttk.Frame(content_frame)
        parent_frame.pack(fill=tk.X, pady=(0, 10))

        parent_label = ttk.Label(parent_frame, text="Parent folder:")
        parent_label.pack(side=tk.LEFT)

        parent_path = os.path.dirname(self.input_path)
        parent_path_label = ttk.Label(parent_frame, text=parent_path)
        parent_path_label.pack(side=tk.LEFT, padx=5)

        parent_browse = ttk.Button(parent_frame, text="Browse", command=lambda: open_folder(parent_path))
        parent_browse.pack(side=tk.LEFT)

        tree_frame = ttk.Frame(content_frame)
        tree_frame.pack(fill=tk.BOTH, expand=True)

        columns = ("basename", "size", "murmurhash")
        tree = ttk.Treeview(tree_frame, columns=columns, show='headings')
        self.tree = tree

        tree.heading("basename", text="Basename")
        tree.heading("size", text="File Size")
        tree.heading("murmurhash", text="MurmurHash")

        tree.column("basename", stretch=True, anchor=tk.W)
        tree.column("size", width=100, minwidth=100, stretch=False, anchor=tk.CENTER)
        tree.column("murmurhash", width=150, minwidth=100, stretch=False, anchor=tk.CENTER)

        y_scrollbar = ttk.Scrollbar(tree_frame, orient=tk.VERTICAL, command=tree.yview)
        x_scrollbar = ttk.Scrollbar(tree_frame, orient=tk.HORIZONTAL, command=tree.xview)

        tree.configure(yscroll=y_scrollbar.set, xscroll=x_scrollbar.set)

        tree.grid(row=0, column=0, sticky='nsew')
        y_scrollbar.grid(row=0, column=1, sticky='ns')
        x_scrollbar.grid(row=1, column=0, sticky='ew')

        tree_frame.grid_rowconfigure(0, weight=1)
        tree_frame.grid_columnconfigure(0, weight=1)

        tree.bind("<Double-1>", lambda event: self.on_dbclick_tree(event, tree, parent_path))

        button_frame = ttk.Frame(self, padding=10)
        button_frame.pack(fill=tk.X, pady=5)

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

    def _populate_initial_data(self):
        for dup_basename, _, dup_full_path in self.matches:
            self.tree.insert("", tk.END, values=(dup_basename, "Calculating...", "Calculating..."))

    def _start_background_tasks(self):
        thread = threading.Thread(target=lambda: self._compute_hash_and_size(0))
        thread.start()

    def _compute_hash_and_size(self, index):
        # Base condition: when the index reaches the length of matches
        if index >= len(self.matches):
            return

        dup_basename, _, dup_full_path = self.matches[index]

        try:
            # Try to open the file and read its content
            with open(dup_full_path, 'rb') as f:
                content = f.read()

            # Compute the MurmurHash from the file content
            hash_value_int = mmh3.hash(content, seed=0)
            hash_value = hex(hash_value_int & 0xFFFFFFFF)

            size = os.path.getsize(dup_full_path)
            human_readable_size = self.get_human_readable_size(size)
        except FileNotFoundError:
            # Handle the specific case where the file does not exist
            human_readable_size, hash_value = "Not Found", "N/A"
        except Exception as e:
            # Handle other potential file access or reading errors
            print(f"Error processing {dup_full_path}: {e}")
            human_readable_size, hash_value = "Error", "Error"

        # Update the tree view item
        self.tree.item(
            self.tree.get_children()[index],
            values=(dup_basename, human_readable_size, hash_value)
        )

        # Schedule the next update
        self.tree.after(
            100,
            lambda: self._compute_hash_and_size(index + 1)
        )

    @staticmethod
    def on_dbclick_tree(event, tree, parent_path):
        region = tree.identify("region", event.x, event.y)
        if region != "cell":
            return
        row_id = tree.identify_row(event.y)

        if not row_id:
            return

        basename = tree.set(row_id, "basename")
        file_path = os.path.join(parent_path, basename)
        if os.path.exists(file_path):
            highlight_files(file_path)
        else:
            messagebox.showerror("File Not Found", f"The file '{file_path}' does not exist.")

    @staticmethod
    def get_human_readable_size(size, decimal_places=2):
        for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
            if size < 1024.0:
                return f"{size:.{decimal_places}f} {unit}"
            size /= 1024.0

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.row_data = {}  # Map Treeview row IDs to their data
        self.create_widgets()
        self.center_window(self, 1200, 800)

    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

        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)

        # Import button
        import_append_button = ttk.Button(button_frame, text="Import + Append", command=self.import_append_paths)
        import_append_button.pack(side=tk.LEFT, padx=(0, 5))

        # Clear and Import button
        clear_import_button = ttk.Button(button_frame, text="Clear + Import", command=self.clear_and_import_paths)
        clear_import_button.pack(side=tk.LEFT, padx=(0, 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))

        # Deduplicate All button
        deduplicate_all_button = ttk.Button(button_frame, text="Deduplicate All", command=self.deduplicate_all)
        deduplicate_all_button.pack(side=tk.LEFT, padx=(0, 5))

        clear_results_button = ttk.Button(button_frame, text="Clear results", command=self.clear_tree)
        clear_results_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)

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

        # 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", stretch=True, anchor=tk.W)
        self.tree.column("match_count", minwidth=100, width=100, stretch=False, anchor=tk.CENTER)
        self.tree.column("deduplicate", minwidth=100, width=100, stretch=False, 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.
        """
        self.update_pattern()

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

    def import_append_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 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 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:
                    MatchingFilesDialog(self, 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.deduplicate_item(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 deduplicate_all(self):
        """
        Deduplicate all eligible files by renaming them based on deduplication conditions.
        """
        for item in self.tree.get_children():
            self.deduplicate_item(item, show_dialog=False)

    def deduplicate_item(self, item, show_dialog=True):
        """
        Callback to deduplicate a single file when the 'Deduplicate' cell is clicked.
        """
        print(item)
        data = self.row_data.get(item, {})
        duplicates = data.get("duplicates", [])
        base_file_path = data.get("base_file_path")

        if len(duplicates) == 1:
            file_path = duplicates[0]
            base, index, ext = self.finder.parse_filename(os.path.basename(file_path))
            # 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 file_path.lower() == base_file_path.lower():
                # "Already exact" case
                if show_dialog:
                    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):
                if show_dialog:
                    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=(file_path, 1, "Already exact"))
                self.row_data[item]["match_count"] = 1
                self.row_data[item]["duplicates"] = [new_file_path]
                self.status_var.set(f"Successfully renamed '{file_path}' to '{new_file_path}'.")
            except Exception as e:
                if show_dialog:
                    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
            if show_dialog:
                messagebox.showinfo("Rename Not Applicable", "This file does not meet deduplication criteria.")
            self.status_var.set("Rename not applicable for the selected file.")

    def clear_tree(self):
        self.tree.delete(*self.tree.get_children())
        self.row_data = {}

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


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

what can you do to refactor this script to improve its readability and maintainability for other devs?