"""
Date: 16th/July/2026
Created by: Jap-Slappa 

NFO Metadata Updater+PSG v2.0
By Gemini
"""

import PySimpleGUI as sg
from pathlib import Path

# Paths to my main Anime TV Shows Libraries/Folders
main_folder_paths_list = [
    r'G:\Kodi Builds - 2024\_Kodi 21 Builds_NEW_2025\02. TMM - Anime STRM Files\COMPLTED - TMM Anime STRMs\01. Anime TV Shows\01. Anime-TV 2026\__Currently-Watching--Amine-2025+',
    r'G:\Kodi Builds - 2024\_Kodi 21 Builds_NEW_2025\02. TMM - Anime STRM Files\COMPLTED - TMM Anime STRMs\01. Anime TV Shows\01. Anime-TV 2026\__Discovered-NEW-Anime_TV--2026',
    r'G:\Kodi Builds - 2024\_Kodi 21 Builds_NEW_2025\02. TMM - Anime STRM Files\COMPLTED - TMM Anime STRMs\01. Anime TV Shows\01. Anime-TV 2026\__Old_Fav_Anime',
]

Main_Anime_Lib_Folder = r'G:\Kodi Builds - 2024\_Kodi 21 Builds_NEW_2025\02. TMM - Anime STRM Files\COMPLTED - TMM Anime STRMs\01. Anime TV Shows\01. Anime-TV 2026'

def open_folder_dialog(My_default_folder_path):
    select_folder = sg.filedialog.askdirectory(initialdir=My_default_folder_path,
                                                # key="-SELECTED_TV_SHOW_FOLDER-", 
                                                # enable_events=True,
                                                )
    if select_folder:
        return select_folder
    else:
        return None
#? === End Of Function ==

def Custom_Popup_Get_Folder(My_default_folder_path):
    """Creates the initial window layout to select a folder and scan."""
    layout = [
        [
            sg.Text("Custom Popup Layout + Get Folder", 
                    font=("Helvetica", 14, "bold")
                    )
        ],
        [sg.Text("Select the directory containing your .nfo files:")],
        [
            sg.Multiline(str(My_default_folder_path), 
                        size=(100, 3),
                        key='-UPDATE_FOLDER_PATH-'
                        ),  # (width, height)
            sg.Button("Browse Folder", 
                        key="-BROWSE_FOLDER-",
                        ),
            # sg.filedialog.askdirectory(initialdir=str(My_default_folder_path),
            #                 key="-SELECTED_TV_SHOW_FOLDER-", 
            #                 enable_events=True,
            #                 ),
        ],
        [sg.HSeparator(pad=(0, 15))],
        [
            sg.Button("Exit", key="-EXIT-"),
            sg.Push(),
            sg.Button("Search Folder", key="-SEARCH_FOLDER-"),
            # sg.Button("Scan Directory", key="-SCAN-", 
            #         button_color=("white", "#2A8C82"), 
            #         bind_return_key=True),
        ]
    ]
    return layout
#? === End Of Function ==

def PSG_get_nfo_folder_path(My_default_folder_path):
    # My_default_folder_path = r'\\192.168.4.212\web\00.My_Watclists_4_Kodi\Anime-TV-(Blank-nfos)'
    
    # Test Path...
    # My_default_folder_path = r'\\192.168.4.212\web\00.My_Watclists_4_Kodi\z-Umbrella-Anime-STRMs\01.Anime-TV\Py NFO Updater\v2.0 - PSG\_Test_Files'
    # Test Path...
    # Open the folder scanning configuration window
    main_window = sg.Window("Custom Popup Window", 
                                Custom_Popup_Get_Folder(My_default_folder_path), 
                                finalize=True,
                                )

    while True:
        event, values = main_window.read()

        if event in (sg.WIN_CLOSED, "-EXIT-"):
            break
            
        elif event == "-BROWSE_FOLDER-":
            print('Event (Button Pressed) = BROWSE_FOLDER')
            selected_folder_2 = open_folder_dialog(app.Main_folders_dir)
            main_window['-UPDATE_FOLDER_PATH-'].update(selected_folder_2)
            print('selected_folder_2 = ', selected_folder_2)
            print()
        
        elif event == "-SEARCH_FOLDER-":
            selected_folder = values.get("-UPDATE_FOLDER_PATH-", False)  # This Works
            print('Event = SEARCH_FOLDER Button Was Pressed!!')
            print('selected_folder = ', selected_folder)
            print()
            
            # selected_folder_1 = values["-UPDATE_FOLDER_PATH-"]  # AND This Works
            # print('selected_folder_1 = ', selected_folder_1)
            try:
                if selected_folder:
                    PathLib_selected_folder_Path = Path(selected_folder)
                    TV_Show_Folder_Name = PathLib_selected_folder_Path.parts[-2]
                    print('')
                    print('==== PSG_get_nfo_folder_path() ====')
                    print('Your Selected TV SHow Folder To... "SCAN"')
                    print('---')
                    print('PathLib_selected_folder_Path [Name]: ', 
                        f'{TV_Show_Folder_Name}: {PathLib_selected_folder_Path.name}')
                    print('PathLib_selected_folder_Path [FULL]: \n >>', PathLib_selected_folder_Path)
                    print('')
                    return PathLib_selected_folder_Path
                elif selected_folder is None:
                    return
            except Exception as Error:
                print(f"PSG_get_nfo_folder_path() \n(Error: {Error})")
    main_window.close()
#? === End Of Function ==

class NfoUpdaterApp:
    def __init__(self):
        # Set a clean, modern dark theme
        sg.theme("DarkBlue")
        # self.target_dir = Path(r"F:\Media\Movies") # Default fallback path OG Code
        self.target_dir = '' # Default fallback path
        # self.target_dir = '' # 
        self.nfo_files:list[Path] = [] # Stores Path objects
        
        # Main_folders_dir: Path is created in... "Select_Main_Folder_Window()" Function
        self.Main_folders_dir:Path[str] = '' # Folder with my TV Show Folder
        self.Main_folders_list:list[Path] = main_folder_paths_list # Stores the Main Folders Path objects
        self.Main_folders_window = None
        
        self.main_window = None
        self.list_window = None

    def create_initial_layout(self):
        """Creates the initial window layout to select a folder and scan."""
        Anime_Libray_Name = self.target_dir.parts[-3] 
        Anime_Show_Name = self.target_dir.parts[-2] 
        Anime_Season_Folder = self.target_dir.name
        layout = [
            [
                sg.Text("Kodi NFO Watched Status Updater", 
                        font=("Helvetica", 14, "bold")
                        )
            ],
            [
                sg.Text(f'Anime_Libray_Name = {Anime_Libray_Name}', 
                        font=("Helvetica", 12, "bold")
                        )
            ],
            [
                sg.Text(f'Target = {Anime_Show_Name}: {Anime_Season_Folder}', 
                        font=("Helvetica", 12, "bold")
                        )
            ],
            [sg.Text("Select the directory containing your media and .nfo files:")],
            [
                sg.Input(str(self.target_dir), 
                        key="-FOLDER-", 
                        size=(100, 4)),  # (width, height)
                sg.FolderBrowse(initial_folder=str(self.target_dir))
            ],
            [sg.HSeparator(pad=(0, 15))],
            [
                sg.Button("Exit", key="-EXIT-"),
                sg.Push(),
                sg.Button("Scan Directory", key="-SCAN-", 
                        button_color=("white", "#2A8C82"), 
                        bind_return_key=True),
            ]
        ]
        return layout
    #? === End Of Method/Function ==

    def create_MAIN_Folder_list_layout(self):
        """Dynamically generates a scrollable list of found Folders with checkboxes on the right."""
        folder_rows:list[str] = []
        
        # Build individual rows for each NFO folder found
        for idx, folder_path in enumerate(self.Main_folders_list, start=1):
            idx_num = str(idx).zfill(2)
            pathlib_folder_path = Path(folder_path)
            folder_rows.append(
                [
                # Display the foldername (relative to parent to keep it short and clean)
                sg.Text(f'{idx_num}-{pathlib_folder_path.name}', 
                        size=(50, 1), 
                        tooltip=str(pathlib_folder_path)),
                sg.Push(), # Pushes the checkbox all the way to the right
                # Key is indexed so we can match it back to the Path list later
                sg.Checkbox("", default=False, key=f"-CHECK_MAIN{idx}-")
                ]
                )

        # Pack the rows into a scrollable column
        scrollable_column_2 = sg.Column(
                                    folder_rows,
                                    scrollable=True,
                                    vertical_scroll_only=True,
                                    # size=(550, 300),
                                    size=(600, 250),  # (width, height)
                                    key="-SCROLLABLE_COL_MAIN-",
                                    expand_x=True,
                                    expand_y=True
                                    )

        #todo: TV Show folder Layout
        layout = [
            [sg.Text("Select TV Show folder to Update", font=("Helvetica", 14, "bold"))],
            [
                sg.Text(f"Found {len(self.Main_folders_list)} TV Show folders in: {Path(self.Main_folders_dir).name}", 
                        text_color="#A4E3DB"
                        )
            ],
            [sg.HSeparator(pad=(0, 10))],
            [scrollable_column_2],
            [sg.HSeparator(pad=(0, 15))],
            
            [
                # Bottom Row of Buttons
                # sg.Button("Select All", key="-MAIN_SELECT_ALL-"),
                # sg.Button("Select None", key="-MAIN_SELECT_NONE-"),
                sg.Button("Close", key="-MAIN_CLOSE-"),
                sg.Push(),
                sg.Button("Select Folder", key="-MAIN_FOLDER_SELECT-", 
                        button_color=("white", "#2A8C82")),
            ]
        ]
        return layout
    #? === End Of Method/Function ==

    def create_list_layout(self):
        """Dynamically generates a scrollable list of found .nfo files with checkboxes on the right."""
        file_rows:list[str] = []
        
        # Build individual rows for each NFO file found
        for idx, file_path in enumerate(self.nfo_files):
            idx_num = str(idx).zfill(2)
            file_rows.append([
                # Display the filename (relative to parent to keep it short and clean)
                sg.Text(f'{idx_num}-{file_path.name}', 
                        size=(50, 1), 
                        tooltip=str(file_path)),
                sg.Push(), # Pushes the checkbox all the way to the right
                # Key is indexed so we can match it back to the Path list later
                sg.Checkbox("", default=True, key=f"-CHECK_{idx}-")
            ])

        # Pack the rows into a scrollable column
        scrollable_column = sg.Column(
                                    file_rows,
                                    scrollable=True,
                                    vertical_scroll_only=True,
                                    # size=(550, 300),
                                    size=(600, 450),  # (width, height)
                                    key="-SCROLLABLE_COL-",
                                    expand_x=True,
                                    expand_y=True
                                    )

        layout = [
            [sg.Text("Select NFO Files to Update", font=("Helvetica", 14, "bold"))],
            [
                sg.Text(f"Found {len(self.nfo_files)} .nfo files in: {self.target_dir.name}", 
                        text_color="#A4E3DB"
                        )
            ],
            [sg.HSeparator(pad=(0, 10))],
            [scrollable_column],
            [sg.HSeparator(pad=(0, 15))],
            
            [
                # Bottom Row of Buttons
                sg.Button("Select All", key="-SELECT_ALL-"),
                sg.Button("Select None", key="-SELECT_NONE-"),
                sg.Push(),
                sg.Button("Update NFO", key="-UPDATE-", button_color=("white", "#2A8C82")),
                sg.Button("Close", key="-CLOSE-")
            ]
        ]
        return layout
    #? === End Of Method/Function ==

    def scan_directory(self, path_str):
        """Scans the selected path using Pathlib for .nfo files."""
        self.target_dir = Path(path_str)
        if not self.target_dir.exists() or not self.target_dir.is_dir():
            sg.popup_error("Error", "Selected directory does not exist or is invalid.", keep_on_top=True)
            return False
        
        # self.nfo_files = list(self.target_dir.rglob("*.nfo"))  # OG Gemini Code
        # Scan the Selected Folder ONLY
        self.nfo_files = list(self.target_dir.glob("*.nfo"))  # Me
        
        if not self.nfo_files:
            sg.popup_annoying("No Files Found", "No .nfo files were found in the selected folder tree.", keep_on_top=True)
            return False
            
        return True
    #? === End Of Method/Function ==

    def process_updates(self, values):
        """Performs the watched-status updates on selected files."""
        changed_files = []
        unchanged_files = []

        for idx, file_path in enumerate(self.nfo_files):
            # Check if this specific file's checkbox was ticked
            is_selected = values.get(f"-CHECK_{idx}-", False)
            
            if not is_selected:
                unchanged_files.append(f"{file_path.name} (Skipped)")
                continue

            try:
                watch_false = "<watched>false</watched>"
                watch_true = "<watched>true</watched>"
                
                playcount_zero = "<playcount>0</playcount>"
                playcount_one = "<playcount>1</playcount>"
                
                # Read content using UTF-8 safely
                content = file_path.read_text(encoding='utf-8', errors='ignore')
                modified = False

                # Swap <watched>
                if "<watched>false</watched>" in content:
                    if watch_false == "<watched>false</watched>":
                        content = content.replace("<watched>false</watched>", 
                                                "<watched>true</watched>")
                        modified = True
                    elif watch_true == "<watched>true</watched>":
                        modified = False
                        continue

                # Swap <playcount>
                if "<playcount>0</playcount>" in content:
                    if playcount_zero == "<playcount>0</playcount>":
                        content = content.replace("<playcount>0</playcount>", 
                                                "<playcount>1</playcount>")
                        modified = True
                    elif playcount_one == "<playcount>1</playcount>":
                        modified = False
                        continue

                if modified:
                    file_path.write_text(content, encoding='utf-8')
                    changed_files.append(file_path.name)
                else:
                    unchanged_files.append(file_path.name)

            except Exception as e:
                unchanged_files.append(f"{file_path.name} (Error: {e})")

        # Generate a detailed summary window
        self.show_summary_popup(changed_files, unchanged_files)
    #? === End Of Method/Function ==

    def show_summary_popup(self, changed, unchanged):
        """Creates a readable, scrollable text summary popup of the operation."""
        summary_text = (
                        f"=== UPDATE SUMMARY ===\n"
                        f"Total Files Found: {len(self.nfo_files)}\n"
                        f"Files Updated: {len(changed)}\n"
                        f"Files Unchanged / Skipped: {len(unchanged)}\n\n"
                        )
        
        if changed:
            # OG Code
            # summary_text += "UPDATED FILES:\n" + "\n".join([f"  [✓] {f}" for f in changed]) + "\n\n"
            # New Code (Coz this... [✓], caused an error. I changed to.. [+])
            summary_text += "UPDATED FILES:\n" + "\n".join([f"  [+] {f}" for f in changed]) + "\n\n"
        else:
            summary_text += "UPDATED FILES:\n  None\n\n"

        if unchanged:
            summary_text += "UNCHANGED / SKIPPED FILES:\n" + "\n".join([f"  [-] {f}" for f in unchanged]) + "\n"

        sg.popup_scrolled(
                        summary_text,
                        title="Results",
                        # size=(80, 20),  # (width, height)
                        size=(80, 30),  # (width, height)
                        font=("Consolas", 10),
                        non_blocking=False
                        )
        print('')
        print(f'{summary_text}')
        print('')
    #? === End Of Method/Function ==

###############################################################
#todo: This Works!
    def get_main_folder_path(self, values):
        """Performs the watched-status updates on selected files."""
        changed_files = []
        unchanged_files = []

        for idx, file_path in enumerate(self.Main_folders_list, start=1):
            # Check if this specific file's checkbox was ticked
            is_selected = values.get(f"-CHECK_MAIN{idx}-", False)
            
            if is_selected:
                selected_Anime_Lib_Folder = Path(file_path)
                print(f'')
                print(f'Your Selected Anime Lib Folder Is...')
                print('.....Name: ', f'{selected_Anime_Lib_Folder.parts[-2]}: {selected_Anime_Lib_Folder.name}')
                print('Full Path: \n >>', selected_Anime_Lib_Folder)
                print(f'')
                return selected_Anime_Lib_Folder
            elif not is_selected:
                # unchanged_files.append(f"{file_path.name} (Skipped)")
                continue
        
    #? === End Of Method/Function ==

#todo: This Works!
    def Select_Main_Folder_Window(self):
        # Open the folder scanning configuration window
        self.Main_folders_window = sg.Window("Select Your Main Anime Library Folder", 
                                    self.create_MAIN_Folder_list_layout(), 
                                    finalize=True,
                                    )

        while True:
            foldr_event, foldr_values = self.Main_folders_window.read()

            if foldr_event in (sg.WIN_CLOSED, "-MAIN_CLOSE-"):
                break

            elif foldr_event == "-MAIN_FOLDER_SELECT-":
                # Process only what's checked
                app.Main_folders_dir = Path(self.get_main_folder_path(foldr_values))
                break # Close selection list after running updates
        self.Main_folders_window.close()
    #? === End Of Method/Function ==
###############################################################

    def run(self):
        # Open the folder scanning configuration window
        self.main_window = sg.Window("Kodi NFO Updater", 
                                    self.create_initial_layout(), 
                                    finalize=True,
                                    )

        while True:
            event, values = self.main_window.read()

            if event in (sg.WIN_CLOSED, "-EXIT-"):
                break

            if event == "-SCAN-":
                folder_path = values["-FOLDER-"]
                if self.scan_directory(folder_path):
                    # Hide main window and open the selection list window
                    self.main_window.hide()
                    self.list_window = sg.Window("NFO Selector", 
                                                self.create_list_layout(), 
                                                modal=True, 
                                                finalize=True
                                                )

                    # Sub-loop for managing the list window selection
                    while True:
                        list_event, list_values = self.list_window.read()

                        if list_event in (sg.WIN_CLOSED, "-CLOSE-"):
                            break

                        elif list_event == "-SELECT_ALL-":
                            for idx in range(len(self.nfo_files)):
                                self.list_window[f"-CHECK_{idx}-"].update(True)

                        elif list_event == "-SELECT_NONE-":
                            for idx in range(len(self.nfo_files)):
                                self.list_window[f"-CHECK_{idx}-"].update(False)

                        elif list_event == "-UPDATE-":
                            # Process only what's checked
                            self.process_updates(list_values)
                            break # Close selection list after running updates

                    self.list_window.close()
                    self.list_window = None
                    self.main_window.un_hide() # Restore the main setup window

        self.main_window.close()
    #? === End Of Method/Function ==
#todo ============ End Of The Whole Class ============


if __name__ == "__main__":
    # Create an app instance
    app = NfoUpdaterApp()  # THIS
    
    # Runs the Main function in my Class/app instance
    app.Select_Main_Folder_Window()  # THIS
    
    # Main_folders_dir / Path(my_selected_path)
    my_selected_path = PSG_get_nfo_folder_path(app.Main_folders_dir)  # THIS
    # my_selected_path = Custom_Popup_Get_Folder(app.Main_folders_dir)
    
    app.target_dir = Path(my_selected_path)  # THIS
    
    # Runs the Main function in my Class/app instance
    app.run()

