from src.imports import *
from src.helpers import *

import src.shared_vars as shared

class LibraryTab(ctk.CTkScrollableFrame):
    def __init__(self, master, **kwargs):

        super().__init__(master, **kwargs)
        self.added_items = set()
        self.to_update = set()
        self.grid_columnconfigure(0, weight=1)

        self.radiobutton_variable = ctk.StringVar()
        self.no_items_label = ctk.CTkLabel(self, text="", anchor="w")
        self.filter_entry = ctk.CTkEntry(self, placeholder_text="Your search query here, or type in mod or map to only see that")
        self.filter_entry.bind("<KeyRelease>", self.filter_items)
        self.filter_entry.grid(row=0, column=0,  padx=(10, 20), pady=(10, 20), sticky="we")
        filter_refresh_button_image = os.path.join(RESOURCES_DIR, "Refresh_icon.svg.png")
        update_button_image = os.path.join(RESOURCES_DIR, "update_icon.png")
        self.filter_refresh_button = ctk.CTkButton(self, image=ctk.CTkImage(Image.open(filter_refresh_button_image)), command=self.refresh_items, width=20, height=20,
                                                   fg_color="transparent", text="")
        self.filter_refresh_button.grid(row=0, column=1, padx=(10, 0), pady=(10, 20), sticky="nw")
        self.update_button = ctk.CTkButton(self, image=ctk.CTkImage(Image.open(update_button_image)), command=self.check_for_updates, width=65, height=20,
                                           text="", fg_color="transparent")
        self.update_button.grid(row=0, column=1, padx=(0, 20), pady=(10, 20), sticky="en")
        self.update_tooltip = ctk_tooltip.CTkToolTip(self.update_button, message="Check items for updates", topmost=True)
        filter_tooltip = ctk_tooltip.CTkToolTip(self.filter_refresh_button, message="Refresh library", topmost=True)
        self.label_list = []
        self.button_list = []
        self.button_view_list = []
        self.file_cleaned = False
        self.filter_type = True
        self.clipboard_has_content = False
        self.item_block_list = set()
        self.added_folders = set()
        self.ids_added = set()

    def add_item(self, item, image=None, workshop_id=None, folder=None, invalid_warn=False):
        label = ctk.CTkLabel(self, text=item, image=image, compound="left", padx=5, anchor="w")
        button = ctk.CTkButton(self, text="Remove", width=60, height=24, fg_color="#3d3f42")
        button_view = ctk.CTkButton(self, text="Details", width=55, height=24, fg_color="#3d3f42")
        button.configure(command=lambda: self.remove_item(item, folder, workshop_id))
        button_view.configure(command=lambda: self.show_map_info(workshop_id, invalid_warn))
        button_view_tooltip = ctk_tooltip.CTkToolTip(button_view, message="Opens up a window that shows basic details")
        button_tooltip = ctk_tooltip.CTkToolTip(button, message="Removes the map/mod from your game")
        label.grid(row=len(self.label_list) + 1, column=0, pady=(0, 10), padx=(5, 10), sticky="w")
        button.grid(row=len(self.button_list) + 1, column=1, pady=(0, 10), padx=(50, 10), sticky="e")
        button_view.grid(row=len(self.button_view_list) + 1, column=1, pady=(0, 10), padx=(10, 75), sticky="w")
        self.label_list.append(label)
        self.button_list.append(button)
        self.button_view_list.append(button_view)
        label.bind("<Enter>", lambda event, label=label: self.on_label_hover(label, enter=True))
        label.bind("<Leave>", lambda event, label=label: self.on_label_hover(label, enter=False))
        label.bind("<Button-1>", lambda event, label=label: self.copy_to_clipboard(label, workshop_id, event))
        label.bind("<Control-Button-1>", lambda event, label=label: self.copy_to_clipboard(label, workshop_id, event, append=True))
        label.bind("<Button-2>", lambda event: self.open_folder_location(folder, event))
        label.bind("<Button-3>", lambda event, label=label: self.copy_to_clipboard(label, folder, event))
        if invalid_warn:
            label_warn = ctk_tooltip.CTkToolTip(label, message="Duplicated or Blocked item (Search item id in search)")

    def on_label_hover(self, label, enter):
        if enter:
            label.configure(fg_color="#272727")
        else:
            label.configure(fg_color="transparent")

    def copy_to_clipboard(self, label, something, event=None, append=False):
        try:
            if append:
                if self.clipboard_has_content:
                    label.clipboard_append(f"\n{something}")
                    show_noti(label, "Appended to clipboard", event, 1.0)
                else:
                    label.clipboard_clear()
                    label.clipboard_append(something)
                    self.clipboard_has_content = True
                    show_noti(label, "Copied to clipboard", event, 1.0)
            else:
                label.clipboard_clear()
                label.clipboard_append(something)
                self.clipboard_has_content = True
                show_noti(label, "Copied to clipboard", event, 1.0)
        except:
            pass

    def open_folder_location(self, folder, event=None):
        if os.path.exists(folder):
            os.startfile(folder)
            show_noti(self, "Opening folder", event, 1.0)

    def item_exists_in_file(self, items_file, workshop_id, folder_name=None):
        if not os.path.exists(items_file):
            return False, False

        with open(items_file, "r") as f:
            items_data = json.load(f)
            for item_info in items_data:
                if "id" in item_info and "folder_name" in item_info and "json_folder_name" in item_info:
                    if item_info["id"] == workshop_id and item_info["folder_name"] == folder_name:
                        if item_info["folder_name"] in self.added_folders:
                            continue
                        if item_info["folder_name"] in self.item_block_list:
                            return False ,None
                        return True, True
                    elif item_info["id"] == workshop_id:
                        if item_info["folder_name"] in self.added_folders:
                            continue
                        if item_info["folder_name"] in self.item_block_list:
                            return False ,None
                        return True, False

                elif "id" in item_info and item_info["id"] == workshop_id:
                    return True, False
        return False, False

    def remove_item_by_option(self, items_file, option, option_name="id"):

        if not os.path.exists(items_file):
            return

        with open(items_file, "r") as f:
            items_data = json.load(f)

        updated_items_data = [item for item in items_data if item.get(option_name) != option]

        if len(updated_items_data) < len(items_data):
            with open(items_file, "w") as f:
                json.dump(updated_items_data, f, indent=4)

    def get_item_by_id(self, items_file, item_id, return_option="all"):

        if not os.path.exists(items_file):
            return None

        with open(items_file, "r") as f:
            items_data = json.load(f)

        for item in items_data:
            if item.get("id") == item_id:
                if return_option == "all":
                    return item
                elif return_option == return_option:
                    return item.get(return_option)
        return None

    def get_item_index_by_id(self, items_data, item_id):
        for index, item in enumerate(items_data):
            if item.get("id") == item_id:
                return index
        return None

    def update_or_add_item_by_id(self, items_file, item_info, item_id):
        if not os.path.exists(items_file):
            with open(items_file, "w") as f:
                json.dump([item_info], f, indent=4)
        else:
            with open(items_file, "r+") as f:
                items_data = json.load(f)
                existing_item_index = self.get_item_index_by_id(items_data, item_id)
                if existing_item_index is not None:
                    items_data[existing_item_index] = item_info
                else:
                    items_data.append(item_info)
                f.seek(0)
                f.truncate()
                json.dump(items_data, f, indent=4)

    def clean_json_file(self, file):

        if not os.path.exists(file):
            show_message("Error", f"File '{file}' does not exist.")
            return

        with open(file, "r") as f:
            items_data = json.load(f)

        cleaned_items = [item for item in items_data if 'folder_name' in item and 'json_folder_name'
                         in item and item['folder_name'] not in self.item_block_list and item['folder_name'] in self.added_folders]

        with open(file, 'w') as file:
            json.dump(cleaned_items, file, indent=4)

    def filter_items(self, event):
        filter_text = self.filter_entry.get().lower()
        for label, button, button_view_list in zip(self.label_list, self.button_list, self.button_view_list):
            item_text = label.cget("text").lower()
            if filter_text in item_text:
                label.grid()
                button.grid()
                button_view_list.grid()
            else:
                label.grid_remove()
                button_view_list.grid_remove()
                button.grid_remove()

    def load_items(self, boiiiFolder):
        maps_folder = Path(boiiiFolder) / "mods"
        mods_folder = Path(boiiiFolder) / "usermaps"
        mod_img = os.path.join(RESOURCES_DIR, "mod_image.png")
        map_img = os.path.join(RESOURCES_DIR, "map_image.png")
        b_mod_img = os.path.join(RESOURCES_DIR, "b_mod_image.png")
        b_map_img = os.path.join(RESOURCES_DIR, "b_map_image.png")
        map_count = 0
        mod_count = 0
        total_size = 0

        folders_to_process = [mods_folder, maps_folder]

        items_file = os.path.join(application_path, LIBRARY_FILE)

        for folder_path in folders_to_process:
            for zone_path in folder_path.glob("**/zone"):
                json_path = zone_path / "workshop.json"
                if json_path.exists():
                    # current folder name
                    curr_folder_name = zone_path.parent.name

                    workshop_id = extract_json_data(json_path, "PublisherID")
                    name = extract_json_data(json_path, "Title").replace(">", "").replace("^", "")
                    name = name[:45] + "..." if len(name) > 45 else name
                    item_type = extract_json_data(json_path, "Type")
                    folder_name = extract_json_data(json_path, "FolderName")
                    folder_size_bytes = get_folder_size(zone_path.parent)
                    size = convert_bytes_to_readable(folder_size_bytes)
                    total_size += folder_size_bytes
                    text_to_add = f"{name} | Type: {item_type.capitalize()}"
                    mode_type = "ZM" if item_type == "map" and folder_name.startswith("zm") else "MP" if folder_name.startswith("mp") and item_type == "map" else None
                    if mode_type:
                        text_to_add += f" | Mode: {mode_type}"
                    text_to_add += f" | ID: {workshop_id} | Size: {size}"

                    creation_timestamp = None
                    for ff_file in zone_path.glob("*.ff"):
                        if ff_file.exists():
                            creation_timestamp = ff_file.stat().st_ctime
                            break

                    if creation_timestamp is not None:
                        date_added = datetime.fromtimestamp(creation_timestamp).strftime("%d %b, %Y @ %I:%M%p")
                    else:
                        creation_timestamp = zone_path.stat().st_ctime
                        date_added = datetime.fromtimestamp(creation_timestamp).strftime("%d %b, %Y @ %I:%M%p")

                    map_count += 1 if item_type == "map" else 0
                    mod_count += 1 if item_type == "mod" else 0
                    if curr_folder_name not in self.added_folders:
                        image_path = mod_img if item_type == "mod" else map_img
                        if not (str(curr_folder_name).strip() == str(workshop_id).strip() or str(curr_folder_name).strip() == str(folder_name).strip()):
                            try: self.remove_item_by_option(items_file, curr_folder_name, "folder_name")
                            except: pass
                            self.item_block_list.add(curr_folder_name)
                            image_path = b_mod_img if item_type == "mod" else b_map_img
                            text_to_add += " | ⚠️"
                        elif curr_folder_name not in self.added_folders and workshop_id in self.ids_added:
                            try: self.remove_item_by_option(items_file, curr_folder_name, "folder_name")
                            except: pass
                            image_path = b_mod_img if item_type == "mod" else b_map_img
                            text_to_add += " | ⚠️"

                        self.added_items.add(text_to_add)
                        if image_path is b_mod_img or image_path is b_map_img:
                            self.add_item(text_to_add, image=ctk.CTkImage(Image.open(image_path)), workshop_id=workshop_id, folder=zone_path.parent, invalid_warn=True)
                        else:
                            self.add_item(text_to_add, image=ctk.CTkImage(Image.open(image_path)), workshop_id=workshop_id, folder=zone_path.parent)
                        id_found, folder_found = self.item_exists_in_file(items_file, workshop_id, curr_folder_name)
                        item_info = {
                                "id": workshop_id,
                                "text": text_to_add,
                                "date": date_added,
                                "folder_name": curr_folder_name,
                                "json_folder_name": folder_name
                            }
                        # when item is blocked ,item_exists_in_file() returns None for folder_found
                        if not id_found and folder_found == None:
                            self.remove_item_by_option(items_file, curr_folder_name, "folder_name")
                        elif not id_found and not folder_found and curr_folder_name not in self.item_block_list and workshop_id not in self.ids_added:
                            if not os.path.exists(items_file):
                                with open(items_file, "w") as f:
                                    json.dump([item_info], f, indent=4)
                            else:
                                with open(items_file, "r+") as f:
                                    items_data = json.load(f)
                                    items_data.append(item_info)
                                    f.seek(0)
                                    json.dump(items_data, f, indent=4)

                        if id_found and not folder_found and curr_folder_name not in self.item_block_list and workshop_id not in self.ids_added:
                            self.update_or_add_item_by_id(items_file, item_info, workshop_id)

                        # keep here cuz of item_exists_in_file() testing
                        self.added_folders.add(curr_folder_name)
                        if not workshop_id in self.ids_added:
                            self.ids_added.add(workshop_id)

        if not self.file_cleaned and os.path.exists(items_file):
            self.file_cleaned = True
            self.clean_json_file(items_file)

        if not self.added_items:
            self.show_no_items_message()
        else:
            self.hide_no_items_message()

        if map_count > 0 or mod_count > 0:
            return f"Maps: {map_count} - Mods: {mod_count} - Total size: {convert_bytes_to_readable(total_size)}"
        return "No items in current selected folder"

    def update_item(self, boiiiFolder, id, item_type, foldername):
        try:
            if item_type == "map":
                folder_path = Path(boiiiFolder) / "usermaps" / f"{foldername}"
            elif item_type == "mod":
                folder_path = Path(boiiiFolder) / "mods" / f"{foldername}"
            else:
                raise ValueError("Unsupported item_type. It must be 'map' or 'mod'.")

            for zone_path in folder_path.glob("**/zone"):
                json_path = zone_path / "workshop.json"
                if json_path.exists():
                    workshop_id = extract_json_data(json_path, "PublisherID")
                    if workshop_id == id:
                        name = extract_json_data(json_path, "Title").replace(">", "").replace("^", "")
                        name = name[:45] + "..." if len(name) > 45 else name
                        item_type = extract_json_data(json_path, "Type")
                        folder_name = extract_json_data(json_path, "FolderName")
                        size = convert_bytes_to_readable(get_folder_size(zone_path.parent))
                        text_to_add = f"{name} | Type: {item_type.capitalize()}"
                        mode_type = "ZM" if item_type == "map" and folder_name.startswith("zm") else "MP" if folder_name.startswith("mp") and item_type == "map" else None
                        if mode_type:
                            text_to_add += f" | Mode: {mode_type}"
                        text_to_add += f" | ID: {workshop_id} | Size: {size}"
                        date_added = datetime.now().strftime("%d %b, %Y @ %I:%M%p")
                        items_file = os.path.join(application_path, LIBRARY_FILE)

                        item_info = {
                            "id": workshop_id,
                            "text": text_to_add,
                            "date": date_added,
                            "folder_name": foldername,
                            "json_folder_name": folder_name
                        }
                        self.update_or_add_item_by_id(items_file, item_info, id)
                        return

        except Exception as e:
            show_message("Error updating json file", f"Error while updating library json file\n{e}")

    def remove_item(self, item, folder, id):
        items_file = os.path.join(application_path, LIBRARY_FILE)
        for label, button, button_view_list in zip(self.label_list, self.button_list, self.button_view_list):
            if item == label.cget("text"):
                self.added_folders.remove(os.path.basename(folder))
                try:
                    shutil.rmtree(folder)
                except Exception as e:
                    show_message("Error" ,f"Error removing folder '{folder}': {e}", icon="cancel")
                    return
                label.destroy()
                button.destroy()
                button_view_list.destroy()
                self.label_list.remove(label)
                self.button_list.remove(button)
                self.added_items.remove(label.cget("text"))
                self.ids_added.remove(id)
                self.button_view_list.remove(button_view_list)
                self.remove_item_by_option(items_file, id)

    def refresh_items(self):
        for label, button, button_view_list in zip(self.label_list, self.button_list, self.button_view_list):
            label.destroy()
            button.destroy()
            button_view_list.destroy()
        self.label_list.clear()
        self.button_list.clear()
        self.button_view_list.clear()
        self.added_items.clear()
        self.added_folders.clear()
        self.ids_added.clear()
        status = self.load_items(shared.app.edit_destination_folder.get().strip())
        shared.app.title(f"BOIII Workshop Downloader - Library  ➜  {status}")

    def view_item(self, workshop_id):
        url = f"https://steamcommunity.com/sharedfiles/filedetails/?id={workshop_id}"
        webbrowser.open(url)

    def show_no_items_message(self):
        self.no_items_label.grid(row=1, column=0, padx=10, pady=(0, 10), sticky="n")
        self.no_items_label.configure(text="No items found in the selected folder. \nMake sure you have a mod/map downloaded and or have the right boiii folder selected.")

    def hide_no_items_message(self):
        self.no_items_label.configure(text="")
        self.no_items_label.forget()

    # i know i know ,please make a pull request i cant be bother
    def show_map_info(self, workshop, invalid_warn=False):
        for button_view in self.button_view_list:
            button_view.configure(state="disabled")

        def show_map_thread():
            workshop_id = workshop

            if not workshop_id.isdigit():
                try:
                    if extract_workshop_id(workshop_id).strip().isdigit():
                        workshop_id = extract_workshop_id(workshop_id).strip()
                    else:
                        show_message("Warning", "Not a valid Workshop ID.")
                except:
                    show_message("Warning", "Not a valid Workshop ID.")
                    return
            try:
                url = f"https://steamcommunity.com/sharedfiles/filedetails/?id={workshop_id}"
                response = requests.get(url)
                response.raise_for_status()
                content = response.text

                soup = BeautifulSoup(content, "html.parser")

                try:
                    map_mod_type = soup.find("div", class_="rightDetailsBlock").text.strip()
                    map_name = soup.find("div", class_="workshopItemTitle").text.strip()
                    map_size = map_size = get_workshop_file_size(workshop_id, raw=True)
                    details_stats_container = soup.find("div", class_="detailsStatsContainerRight")
                    details_stat_elements = details_stats_container.find_all("div", class_="detailsStatRight")
                    date_created = details_stat_elements[1].text.strip()
                    try:
                        ratings = soup.find('div', class_='numRatings')
                        ratings_text = ratings.get_text()
                    except:
                        ratings = "Not found"
                        ratings_text= "Not enough ratings"
                    try:
                        date_updated = details_stat_elements[2].text.strip()
                    except:
                        date_updated = "Not updated"
                    stars_div = soup.find("div", class_="fileRatingDetails")
                    starts = stars_div.find("img")["src"]
                except:
                    show_message("Warning", "Not a valid Workshop ID\nCouldn't get information.")
                    for button_view in self.button_view_list:
                        button_view.configure(state="normal")
                    return

                try:
                    preview_image_element = soup.find("img", id="previewImage")
                    workshop_item_image_url = preview_image_element["src"]
                except:
                    try:
                        preview_image_element = soup.find("img", id="previewImageMain")
                        workshop_item_image_url = preview_image_element["src"]
                    except Exception as e:
                        show_message("Warning", f"Failed to get preview image ,probably wrong link/id if not please open an issue on github.\n{e}")
                        for button_view in self.button_view_list:
                            button_view.configure(state="normal")
                        return

                starts_image_response = requests.get(starts)
                stars_image = Image.open(io.BytesIO(starts_image_response.content))
                stars_image_size = stars_image.size

                image_response = requests.get(workshop_item_image_url)
                image_response.raise_for_status()
                image = Image.open(io.BytesIO(image_response.content))
                image_size = image.size

                self.toplevel_info_window(map_name, map_mod_type, map_size, image, image_size, date_created,
                                        date_updated, stars_image, stars_image_size, ratings_text, url, workshop_id, invalid_warn)

            except requests.exceptions.RequestException as e:
                show_message("Error", f"Failed to fetch map information.\nError: {e}", icon="cancel")
                for button_view in self.button_view_list:
                    button_view.configure(state="normal")
                return

        info_thread = threading.Thread(target=show_map_thread)
        info_thread.start()

    def toplevel_info_window(self, map_name, map_mod_type, map_size, image, image_size,
                             date_created ,date_updated, stars_image, stars_image_size, ratings_text, url, workshop_id, invalid_warn):
        def main_thread():
            try:
                items_file = os.path.join(application_path, LIBRARY_FILE)
                top = ctk.CTkToplevel(self)
                if os.path.exists(os.path.join(RESOURCES_DIR, "ryuk.ico")):
                    top.after(210, lambda: top.iconbitmap(os.path.join(RESOURCES_DIR, "ryuk.ico")))
                top.title("Map/Mod Information")
                top.attributes('-topmost', 'true')
                down_date = self.get_item_by_id(items_file, workshop_id, 'date')

                def close_window():
                    top.destroy()

                def view_map_mod():
                    webbrowser.open(url)

                def check_for_updates():
                    try:

                        if check_item_date(down_date, date_updated):
                            if show_message("There is an update.", "Press download to redownload!", icon="info", _return=True, option_1="No", option_2="Download"):
                                if shared.app.is_downloading:
                                    show_message("Error", "Please wait for the current download to finish or stop it then restart.", icon="cancel")
                                    return
                                shared.app.edit_workshop_id.delete(0, "end")
                                shared.app.edit_workshop_id.insert(0, workshop_id)
                                shared.app.main_button_event()
                                shared.app.download_map(update=True)
                                top.destroy()
                                return
                        else:
                            show_message("Up to date!", "No updates found!", icon="info")
                    except:
                        show_message("Up to date!", "No updates found!", icon="info")

                # frames
                stars_frame = ctk.CTkFrame(top)
                stars_frame.grid(row=0, column=0, columnspan=2, padx=20, pady=(20, 0), sticky="nsew")
                stars_frame.columnconfigure(0, weight=0)
                stars_frame.rowconfigure(0, weight=1)

                image_frame = ctk.CTkFrame(top)
                image_frame.grid(row=1, column=0, columnspan=2, padx=20, pady=0, sticky="nsew")

                info_frame = ctk.CTkFrame(top)
                info_frame.grid(row=2, column=0, columnspan=2, padx=20, pady=20, sticky="nsew")

                buttons_frame = ctk.CTkFrame(top)
                buttons_frame.grid(row=3, column=0, columnspan=2, padx=20, pady=(0, 20), sticky="nsew")

                # fillers
                name_label = ctk.CTkLabel(info_frame, text=f"Name: {map_name}")
                name_label.grid(row=0, column=0, columnspan=2, sticky="w", padx=20, pady=5)

                type_label = ctk.CTkLabel(info_frame, text=f"Type: {map_mod_type}")
                type_label.grid(row=1, column=0, columnspan=2, sticky="w", padx=20, pady=5)

                size_label = ctk.CTkLabel(info_frame, text=f"Size (Workshop): {map_size}")
                size_label.grid(row=2, column=0, columnspan=2, sticky="w", padx=20, pady=5)

                date_created_label = ctk.CTkLabel(info_frame, text=f"Posted: {date_created}")
                date_created_label.grid(row=3, column=0, columnspan=2, sticky="w", padx=20, pady=5)

                date_updated_label = ctk.CTkLabel(info_frame, text=f"Updated: {date_updated}")
                date_updated_label.grid(row=4, column=0, columnspan=2, sticky="w", padx=20, pady=5)

                date_updated_label = ctk.CTkLabel(info_frame, text=f"Downloaded at: {down_date}")
                date_updated_label.grid(row=5, column=0, columnspan=2, sticky="w", padx=20, pady=5)

                stars_image_label = ctk.CTkLabel(stars_frame)
                stars_width, stars_height = stars_image_size
                stars_image_widget = ctk.CTkImage(stars_image, size=(int(stars_width), int(stars_height)))
                stars_image_label.configure(image=stars_image_widget, text="")
                stars_image_label.pack(side="left", padx=(10, 20), pady=(10, 10))

                ratings = ctk.CTkLabel(stars_frame)
                ratings.configure(text=ratings_text)
                ratings.pack(side="right", padx=(10, 20), pady=(10, 10))

                image_label = ctk.CTkLabel(image_frame)
                width, height = image_size
                image_widget = ctk.CTkImage(image, size=(int(width), int(height)))
                image_label.configure(image=image_widget, text="")
                image_label.pack(expand=True, fill="both", padx=(10, 20), pady=(10, 10))

                # Buttons
                close_button = ctk.CTkButton(buttons_frame, text="View", command=view_map_mod, width=130)
                close_button.grid(row=0, column=0, padx=(20, 20), pady=(10, 10), sticky="n")

                update_btn = ctk.CTkButton(buttons_frame, text="Update", command=check_for_updates, width=130)
                update_btn.grid(row=0, column=1, padx=(10, 20), pady=(10, 10), sticky="n")
                update_btn_tooltip = ctk_tooltip.CTkToolTip(update_btn, message="Checks and installs updates of the current selected item (redownload!)", topmost=True)

                view_button = ctk.CTkButton(buttons_frame, text="Close", command=close_window, width=130)
                view_button.grid(row=0, column=2, padx=(10, 20), pady=(10, 10), sticky="n")

                if invalid_warn:
                    update_btn.configure(text="Update", state="disabled")
                    update_btn_tooltip.configure(message="Disabled due to item being blocked or duplicated")

                top.grid_rowconfigure(0, weight=0)
                top.grid_rowconfigure(1, weight=0)
                top.grid_rowconfigure(2, weight=1)
                top.grid_columnconfigure(0, weight=1)
                top.grid_columnconfigure(1, weight=1)

                buttons_frame.grid_rowconfigure(0, weight=1)
                buttons_frame.grid_rowconfigure(1, weight=1)
                buttons_frame.grid_rowconfigure(2, weight=1)
                buttons_frame.grid_columnconfigure(0, weight=1)
                buttons_frame.grid_columnconfigure(1, weight=1)
                buttons_frame.grid_columnconfigure(2, weight=1)

            finally:
                for button_view in self.button_view_list:
                    button_view.configure(state="normal")
        self.after(0, main_thread)

    def check_for_updates(self, on_launch=False):
        if not is_internet_available():
            show_message("Error!", "Internet connection is not available. Please check your internet connection and try again.")
            return
        self.after(1, self.update_button.configure(state="disabled"))
        self.update_tooltip.configure(message='Still loading please wait...')
        cevent = Event()
        cevent.x_root = self.update_button.winfo_rootx()
        cevent.y_root = self.update_button.winfo_rooty()
        if not on_launch:
            show_noti(self.update_button, "Please wait, window will popup shortly", event=cevent, noti_dur=3.0, topmost=True)
        threading.Thread(target=self.check_items_func, args=(on_launch,)).start()

    def items_update_message(self, to_update_len):
        def main_thread():
            if show_message(f"{to_update_len} Item updates available", f"{to_update_len} Workshop Items have an update, Would you like to open the item updater window?", icon="info", _return=True):
                shared.app.after(1, self.update_items_window)
            else:
                return
        shared.app.after(0, main_thread)
        self.update_button.configure(state="normal", width=65, height=20)
        self.update_tooltip.configure(message='Check items for updates')
        return

    def check_items_func(self, on_launch):
        # Needed to refresh item that needs updates
        self.to_update.clear()

        def if_id_needs_update(item_id, item_date, text):
            try:
                headers = {'Cache-Control': 'no-cache'}
                url = f"https://steamcommunity.com/sharedfiles/filedetails/?id={item_id}"
                response = requests.get(url, headers=headers)
                response.raise_for_status()
                content = response.text
                soup = BeautifulSoup(content, "html.parser")
                details_stats_container = soup.find("div", class_="detailsStatsContainerRight")
                details_stat_elements = details_stats_container.find_all("div", class_="detailsStatRight")
                try:
                    date_updated = details_stat_elements[2].text.strip()
                except:
                    try:
                        date_updated = details_stat_elements[1].text.strip()
                    except:
                        return False

                if check_item_date(item_date, date_updated):
                    self.to_update.add(text + f" | Updated: {date_updated}")
                    return True
                else:
                    return False

            except Exception as e:
                show_message("Error", f"Error occured\n{e}", icon="cancel")
                return

        def check_for_update():
            lib_data = None

            if not os.path.exists(os.path.join(application_path, LIBRARY_FILE)):
                show_message("Error checking for item updates! -> Setting is on", "Please visit library tab at least once with the correct boiii path!, you also need to have at lease 1 item!")
                return

            with open(LIBRARY_FILE, 'r') as file:
                lib_data = json.load(file)

            for item in lib_data:
                item_id = item["id"]
                item_date = item["date"]
                if_id_needs_update(item_id, item_date, item["text"])

        check_for_update()

        to_update_len = len(self.to_update)
        if to_update_len > 0:
            self.items_update_message(to_update_len)
        else:
            self.update_button.configure(state="normal", width=65, height=20)
            self.update_tooltip.configure(message='Check items for updates')
            if not on_launch:
                show_message("No updates found!", "Items are up to date!", icon="info")

    def update_items_window(self):
        try:
            top = ctk.CTkToplevel(master=None)
            top.withdraw()
            if os.path.exists(os.path.join(RESOURCES_DIR, "ryuk.ico")):
                top.after(210, lambda: top.iconbitmap(os.path.join(RESOURCES_DIR, "ryuk.ico")))
            top.title("Item updater - List of Items with Updates - Click to select 1 or more")
            longest_text_length = max(len(text) for text in self.to_update)
            window_width = longest_text_length * 6 + 5
            top.geometry(f"{window_width}x450")
            top.attributes('-topmost', 'true')
            top.resizable(True, True)
            selected_id_list = []
            cevent = Event()
            self.select_all_bool = False

            listbox = ctk_listbox.CTkListbox(top, multiple_selection=True)
            listbox.grid(row=0, column=0, sticky="nsew")

            update_button = ctk.CTkButton(top, text="Update")
            update_button.grid(row=1, column=0, pady=10, padx=5, sticky='ns')

            select_button = ctk.CTkButton(top, text="Select All", width=5)
            select_button.grid(row=1, column=0, pady=10, padx=(230, 0), sticky='ns')

            def open_url(id_part, e=None):
                url = f"https://steamcommunity.com/sharedfiles/filedetails/?id={id_part}"
                webbrowser.open(url)

            # you gotta use my modded CTkListbox originaly by Akascape
            def add_checkbox_item(index, item_text):
                parts = item_text.split('ID: ')
                id_part = parts[1].split('|')[0].strip()
                listbox.insert(index, item_text, keybind="<Button-3>", func=lambda e: open_url(id_part))

            def load_items():
                for index, item_text in enumerate(self.to_update):
                    if index == len(self.to_update) - 1:
                        add_checkbox_item("end", item_text)
                        top.deiconify()
                        return
                    add_checkbox_item(index, item_text)

            def update_list(selected_option):
                selected_id_list.clear()

                if selected_option:
                    for option in selected_option:
                        parts = option.split('ID: ')
                        if len(parts) > 1:
                            id_part = parts[1].split('|')[0].strip()
                            selected_id_list.append(id_part)

            def select_all():
                if self.select_all_bool:
                    listbox.deactivate("all")
                    update_list(listbox.get())
                    self.select_all_bool = False
                    return
                listbox.deactivate("all")
                listbox.activate("all")
                update_list(listbox.get())
                self.select_all_bool = True

            def update_btn_fun():
                if len(selected_id_list) == 1:
                    if shared.app.is_downloading:
                        show_message("Error", "Please wait for the current download to finish or stop it then start.", icon="cancel")
                        return
                    shared.app.edit_workshop_id.delete(0, "end")
                    shared.app.edit_workshop_id.insert(0, selected_id_list[0])
                    shared.app.main_button_event()
                    shared.app.download_map(update=True)
                    top.destroy()
                    return

                elif len(selected_id_list) > 1:
                    if shared.app.is_downloading:
                        show_message("Error", "Please wait for the current download to finish or stop it then start.", icon="cancel")
                        return
                    comma_separated_ids = ",".join(selected_id_list)
                    shared.app.queuetextarea.delete("1.0", "end")
                    shared.app.queuetextarea.insert("1.0", comma_separated_ids)
                    shared.app.queue_button_event()
                    shared.app.download_map(update=True)
                    top.destroy()
                    return

                else:
                    cevent.x_root = update_button.winfo_rootx()
                    cevent.y_root = update_button.winfo_rooty()
                    show_noti(update_button ,"Please select 1 or more items", event=cevent, noti_dur=0.8, topmost=True)


            listbox.configure(command=update_list)
            update_button.configure(command=update_btn_fun)
            select_button.configure(command=select_all)

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

            load_items()

        except Exception as e:
            show_message("Error", f"{e}", icon="cancel")

        finally:
            self.update_button.configure(state="normal", width=65, height=20)
            self.update_tooltip.configure(message='Check items for updates')