import traceback
import customtkinter as ctk
import tkinter as tk
from tkinter import messagebox
import threading
from typing import Literal
from tkinter import filedialog
import datetime
import pandas as pd
from functools import partial
import shutil
import os
from PIL import Image, ImageTk

from shared.utils import Logger, RaportManager
from shared.contracts import (
    ValidatorValidatorViewSignature, ValidatorWindowSignature, 
    GetVerificationResultsCallback, GetValidationResultsCallback, 
    ZipStructRulesGet, GpkgDataRulesGet, GpkgStructRulesGet, UatLimitGet,
    GetProgressbarSteps, GetProgressbarUpdate, GetProgressbarEnd, RequestUpdateCallback)
from validator.src.services.authentification_service import Authentification
from validator.src.services.validation_packages_service import ValidationPackageService
from validator.src.packages.processes import DocumentVerification, DocumentValidation
from validator.src.services.validation_metadata_service import ValidationMetadataService
from validator.src.services.validation_kits_service import ValidationKitService
from validator.src.services.support_service import SupportService
from validator.src.services.news_service import NewsService

# =============== Validator View Controller ===============
class ValidatorViewController:
    def __init__(self, view_master: ValidatorWindowSignature, validator_view: ValidatorValidatorViewSignature, request_update_callback: RequestUpdateCallback):
        # Logger
        self.logger = Logger.create_logger(logger_name = "Validator View Controller", log_file_name = "validator_app")
        
        # Views
        self.viewMaster = view_master
        self.validatorView = validator_view
        
        # Callback Functions
        self.requestUpdateCallback: RequestUpdateCallback = request_update_callback
        
        # Helper Classes Instances
        self.authInstance = Authentification()
        self.raportManagerInstance = RaportManager()
        self.documentVerificationInstance: DocumentVerification | None = None
        self.documentValidationInstance: DocumentValidation | None = None
        self.validationPackageService: ValidationPackageService = ValidationPackageService()
        self.validationKitService: ValidationKitService = ValidationKitService()
        self.newsService: NewsService = NewsService()
        self.validationMetadataService: ValidationMetadataService | None = None
        # Variables
        self.ProductionStatus: bool = True
        
        # Tab Status 
        self.ValidationTabStatus = False
        self.RaportTabStatus = False
        self.NewsTabStatus = False
        self.PackagesTabStatus = False
        self.KitsTabStatus = False
        
        # Validation Tab Variables
        self.validationTabWidgetStatuses: dict[str, str] = {
            "ChooseEntry" : "disabled",
            "ChooseButton" : "normal",
            "VerifyButton" : "disabled",
            "UpdateButton" : "disabled",
            "ValidateButton" : "disabled",
            "ValidateOptionMenu" : "disabled",
            "SaveEntry" : "disabled",
            "SaveButton" : "disabled"
            
        }
        self.validateTabFilePath: str | None = None 
        self.validationTabVerificationResults: dict[str, str] = {}
        self.validationTabVerificationResultMessages: list[str] = []
        self.validationTabOptionMenuSelectedVersion: str = None
        self.validationTabValidationResultMessages: list[str] = []
        self.validationTabValidationStatus: bool | None = None
        self.raportDataFrame: pd.DataFrame | None = None
        
        self.validationTabProgressbarStart: int = 0
        self.validationTabProgressbarCurrent: int = 0
        self.validationTabProgressbarSteps: int = None
        self.validationInProgress: bool = False
        
        # Raports Tab
        self.raportTabRaportsBatchSize: int = 7
        self.raportTabRaportsLoadedCount: int = 0
        self.raportTabRaportsLoading: bool = False
        self.raportTabRaportsFramesLoaded: list[ctk.CTkFrame] = []
        # News Tab
        self.NewsTabNewsBatchSize: int = 7
        self.NewsTabNewsLoadedCount: int = 0
        self.NewsTabNewsLoading: bool = False
        self.NewsTabNewsFramesLoaded: list[ctk.CTkFrame] = []
        # Packages
        self.packagesTabPackagesBatchSize: int = 7
        self.packagesTabPackagesLoadedCount: int = 0
        self.packagesTabPackagesLoading: bool = False
        self.packagesTabPackagesFramesLoaded: list[ctk.CTkFrame] = []
        # Kits
        self.KitsTabKitsBatchSize: int = 7
        self.KitsTabKitsLoadedCount: int = 0
        self.KitsTabKitsLoading: bool = False
        self.KitsTabKitsFramesLoaded: list[ctk.CTkFrame] = []
        
        # Checks if tester
        self.is_tester()
        
        # Set button commands
        # Sidenavbar
        self.validatorView.sidenavbarLogoutButton.configure(
            command = lambda: self.handle_logout())
        self.validatorView.sidenavbarValidationTabButton.configure(
            command = lambda: self.change_validator_tabs(tab = "Validation"))
        self.validatorView.sidenavbarRaportTabButton.configure(
            command = lambda: self.change_validator_tabs(tab = "Raport"))
        self.validatorView.sidenavbarNewsTabButton.configure(
            command = lambda: self.change_validator_tabs(tab = "News"))
        self.validatorView.sidenavbarPackagesTabButton.configure(
            command = lambda: self.change_validator_tabs(tab = "Packages"))
        self.validatorView.sidenavbarKitsTabButton.configure(
            command = lambda: self.change_validator_tabs(tab = "Kits"))
        self.validatorView.sidenavbarSupportButton.configure(
            command = lambda: self._support_window())
        self.validatorView.after(0, lambda: self.change_validator_tabs(tab = "Validation"))
        
        
    # ========== Logout ==========
    def handle_logout(self, event = None) -> None:
        """Runs the logout thread."""
        try:
            # Start background thread
            threading.Thread(
                target=self._logout_thread,
                daemon=True
            ).start()

        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self.handle_logout.__name__}] "
                f"Failed to start login thread.\n{traceBack}")
    
    def _logout_thread(self,) -> None:
        """The thread of the login."""
        try:
            self.validatorView.after(0, self._complete_logout)
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._logout_thread.__name__}] "
                f"Failed to logout the user. \n{traceBack}")
    
    def _complete_logout(self) -> None:
        """The complete logout function."""
        try:
            self.authInstance.tokenManagerInstance.wipe_tokens()
            self.validatorView.destroy()
            self.viewMaster.delete_validator_view()
            self.viewMaster.create_login_view()
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._logout_thread.__name__}] "
                f"Failed to complete the logout of user. \n{traceBack}")
    # ============================
    
    # ========== Tester Functionality ==========
    def is_tester(self) -> None:
        """Starts to check if the user is a tester."""
        try:
            threading.Thread(target=self._check_tester_worker, daemon=True).start()
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self.is_tester.__name__}] "
                f"Failed to start the verification to see if the user is a tester.\n{traceBack}")
            
    def _check_tester_worker(self) -> None:
        """Checks if the token contains the is_tester field and if it is set to true or false."""
        try:
            isTester = self.authInstance.check_is_tester()
            if isTester:
                self.validatorView.after(0, self.validatorView.place_tester_button(command = self.change_tester_status))
            
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self.is_tester.__name__}] "
                f"Failed to check if user is tester.\n{traceBack}")
            
    def change_tester_status(self, event = None) -> None:
        """Starts the change of the production status and UI update"""
        try:
            threading.Thread(target = self._change_tester_status_worker, daemon=True).start()
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self.is_tester.__name__}] "
                f"Failed to start the tester change status.\n{traceBack}")
    
    def _change_tester_status_worker(self) -> None:
        """Changes the UI and signals the main thread to change the UI elements."""
        try:
            if self.ProductionStatus == True:
                self.ProductionStatus = False
            else:
                self.ProductionStatus = True
            
            if self.RaportTabStatus == True:
                self._reload_raports()
            
            if self.PackagesTabStatus == True:
                self._reload_validation_packages()
            
            if self.KitsTabStatus == True:
                self._reload_validation_kits()
            
            if self.NewsTabStatus == True:
                self._reload_news()
                
            # Schedule UI updates in main thread
            self.validatorView.after(0, lambda: self._update_ui_for_tester_status(self.ProductionStatus))

        except Exception as e:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._change_tester_status_worker.__name__}] "
                f"Failed to change the tester change status.\n{traceBack}")
            
    def _update_ui_for_tester_status(self, is_production: bool) -> None:
        """Changes the UI elements based on the production status."""
        try:
            if is_production:
                self.validatorView.sidenavbarTitleLabel.configure(
                    text = self.validatorView.template["PermanentElements"]["Actives"]["SidenavbarValidatorTitleLabelProduction"],
                    text_color = self.validatorView.template["PermanentElements"]["Actives"]["SidenavbarValidatorLabelColorProduction"])
                self.validatorView.footerVersionLabel.configure(
                    text = f"Kit Validare {self.validatorView.kitVersion} Stabila",
                    text_color = self.validatorView.template["PermanentElements"]["Actives"]["FooterValidatorLabelColorProduction"])
                self.validatorView.sidenavbarTesterButton.configure(
                    text = self.validatorView.template["PermanentElements"]["Actives"]["SidenavbarTesterButtonBeta"])
                self.validatorView.footerFrame.configure(
                    border_color = self.validatorView.template["PermanentElements"]["Actives"]["FooterValidatorFrameBorderColorProduction"])
            else:
                self.validatorView.sidenavbarTitleLabel.configure(
                    text = self.validatorView.template["PermanentElements"]["Actives"]["SidenavbarValidatorTitleLabelBeta"],
                    text_color = self.validatorView.template["PermanentElements"]["Actives"]["SidenavbarValidatorLabelColorBeta"])
                self.validatorView.footerVersionLabel.configure(
                    text = f"Kit Validare {self.validatorView.kitVersion} Beta",
                    text_color = self.validatorView.template["PermanentElements"]["Actives"]["FooterValidatorLabelColorBeta"])
                self.validatorView.sidenavbarTesterButton.configure(
                    text = self.validatorView.template["PermanentElements"]["Actives"]["SidenavbarTesterButtonProduction"])
                self.validatorView.footerFrame.configure(
                    border_color = self.validatorView.template["PermanentElements"]["Actives"]["FooterValidatorFrameBorderColorBeta"])
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._update_ui_for_tester_status.__name__}] "
                f"Failed to update the ui based on tester status.\n{traceBack}")
    # ==========================================
    
    # ========== Support Functionality ==========
    def _support_window(self) -> None:
        try:
            # Load icon
            logoPath = self.validatorView.images["logo"]
            logoImage = Image.open(logoPath)
            self._logo_photo = ImageTk.PhotoImage(logoImage)
            # ---------------------
            # Create CTkToplevel
            # ---------------------
            self.validationViewSupport = ctk.CTkToplevel(
                self.validatorView,
                fg_color=self.validatorView.template["SupportSender"]["MainFrame"]["fg_color"]
            )
            self.validationViewSupport.wm_iconphoto(False, self._logo_photo)

            # ---------------------
            # Modal & geometry
            # ---------------------
            self.validationViewSupport.transient(self.validatorView)
            self.validationViewSupport.grab_set()
            self.validationViewSupport.title("Support")
            self.validationViewSupport.resizable(False, False)
            width = self.validatorView.template["SupportSender"]["MainFrame"]["width"]
            height = self.validatorView.template["SupportSender"]["MainFrame"]["height"]
            screenWidth = self.validationViewSupport.winfo_screenwidth()
            screenHeight = self.validationViewSupport.winfo_screenheight()
            midWidth = (screenWidth - int(width)) // 2
            midHeight = (screenHeight - int(height)) // 2
            self.validationViewSupport.geometry(f"{width}x{height}+{midWidth}+{midHeight}")

            # ---------------------
            # Label
            # ---------------------
            self.validationViewSupportLabel = ctk.CTkLabel(
                master=self.validationViewSupport,
                justify="left",
                **self._template_processing(self.validatorView.template["SupportSender"]["Message"])
            )
            self.validationViewSupport.wm_iconphoto(False, self._logo_photo)
            # ---------------------
            # Buttons
            # ---------------------
            self.validationViewSupportConfirmButton = ctk.CTkButton(
                master=self.validationViewSupport,
                command=lambda production=self.ProductionStatus: self._send_app_logs(production=production),
                **self._template_processing(self.validatorView.template["SupportSender"]["ConfirmButton"])
            )
            self.validationViewSupport.wm_iconphoto(False, self._logo_photo)
            self.validationViewSupportCancelButton = ctk.CTkButton(
                master=self.validationViewSupport,
                command=lambda: self.validationViewSupport.destroy(),
                **self._template_processing(self.validatorView.template["SupportSender"]["CancelButton"])
            )
            self.validationViewSupport.wm_iconphoto(False, self._logo_photo)

            # ---------------------
            # Placement
            # ---------------------
            self.validationViewSupportLabel.pack(anchor="center", padx=10, pady=10)
            self.validationViewSupportConfirmButton.place(anchor="center", relx=0.7, rely=0.825)
            self.validationViewSupportCancelButton.place(anchor="center", relx=0.9, rely=0.825)
            self.validationViewSupport.wm_iconphoto(False, self._logo_photo)

        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._support_window.__name__}] "
                f"Failed to open the support window.\n{traceBack}"
            )

    def _send_app_logs(self, production: bool) -> None:
        try:
            # Disable buttons to prevent multiple clicks
            self.validationViewSupportConfirmButton.configure(state="disabled")
            self.validationViewSupportCancelButton.configure(state="disabled")
            self.validationViewSupport.wm_iconphoto(False, self._logo_photo)
            # Send logs
            SupportService().send_zip(production=production)
            # Close window after 1 second
            self.validatorView.after(1000, self.validationViewSupport.destroy)

        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._support_window.__name__}] "
                f"Failed to send the application logs.\n{traceBack}"
            )
    # ===========================================
    
    # ========== Tab Buttons Functionality ==========
    def change_validator_tabs(self, tab: Literal["Validation", "Raport", "News", "Packages", "Kits"]) -> None:
        """Change the validator tabs."""
        try:
            if tab == "Validation":
                if self.RaportTabStatus == True:
                    self.validatorView.RaportTabMainFrame.pack_forget()
                    self.validatorView.sidenavbarRaportTabButton.configure(
                        fg_color = self.validatorView.template["PermanentElements"]["Actives"]["TabButtonNormal"]
                    )
                    self.RaportTabStatus = False
                    
                if self.NewsTabStatus == True:
                    self.validatorView.NewsTabMainFrame.pack_forget()
                    self.validatorView.sidenavbarNewsTabButton.configure(
                        fg_color = self.validatorView.template["PermanentElements"]["Actives"]["TabButtonNormal"]
                    )
                    self.NewsTabStatus = False
                    
                if self.PackagesTabStatus == True:
                    self.validatorView.PackagesTabMainFrame.pack_forget()
                    self.validatorView.sidenavbarPackagesTabButton.configure(
                        fg_color = self.validatorView.template["PermanentElements"]["Actives"]["TabButtonNormal"]
                    )
                    self.PackagesTabStatus = False
                    
                if self.KitsTabStatus == True:
                    self.validatorView.KitsTabMainFrame.pack_forget()
                    self.validatorView.sidenavbarKitsTabButton.configure(
                        fg_color = self.validatorView.template["PermanentElements"]["Actives"]["TabButtonNormal"]
                    )
                    self.KitsTabStatus = False
                
                if self.ValidationTabStatus == False:
                    self.validatorView.create_validation_tab()
                    self.validatorView.sidenavbarValidationTabButton.configure(
                        fg_color = self.validatorView.template["PermanentElements"]["Actives"]["TabButtonActive"]
                    )
                    self.ValidationTabStatus = True
                    
                    # Set widgets states
                    if self.validateTabFilePath is not None:
                        self.change_validator_tab_widget_states(widget = "ChooseEntry", state = "normal")
                        self.validatorView.after(0, lambda: self.validatorView.validationTabChooseEntry.delete(0, ctk.END))
                        self.validatorView.after(0, lambda: self.validatorView.validationTabChooseEntry.insert(0, self.validateTabFilePath))
                        self.change_validator_tab_widget_states(widget = "ChooseEntry", state = "disabled")
                    
                    for widget, state in self.validationTabWidgetStatuses.items():
                        self.change_validator_tab_widget_states(
                            widget = widget, state = state)
                        
                    for msg in self.validationTabVerificationResultMessages[-20:]:
                        self._place_verification_results(message = msg)
                    for msg in self.validationTabValidationResultMessages[-20:]:
                        self._place_validation_results(message = msg)
                        
                    if self.validationTabVerificationResults is not None:
                        self.validatorView.validationTabValidateOptionMenu.configure(
                            values = self.validationTabVerificationResults.get("LocalPackages", []),
                            variable = ctk.StringVar(value = self.validationTabVerificationResults.get("VersionTemplate", "---")))
                    
                    self._progressbar_replace(validation_in_progress = self.validationInProgress)
                    
                    # Set widget commands
                    self.validatorView.validationTabChooseButton.configure(
                        command = lambda: self.browse_files())
                    self.validatorView.validationTabVerifyButtonVerify.configure(
                        command = lambda: self.verification())
                    self.validatorView.validationTabValidateButton.configure(
                        command = lambda: self.validation())
                    self.validatorView.validationTabValidateOptionMenu.configure(
                        command = self.change_package_version)
                    self.validatorView.validationTabSaveRaportButton.configure(
                        command = lambda: self.save_raport_file()
                    )
            
            elif tab == "Raport":
                if self.ValidationTabStatus == True:
                    self.validatorView.ValidationTabMainFrame.pack_forget()
                    self.validatorView.sidenavbarValidationTabButton.configure(
                        fg_color = self.validatorView.template["PermanentElements"]["Actives"]["TabButtonNormal"]
                    )
                    self.ValidationTabStatus = False
                    
                if self.NewsTabStatus == True:
                    self.validatorView.NewsTabMainFrame.pack_forget()
                    self.validatorView.sidenavbarNewsTabButton.configure(
                        fg_color = self.validatorView.template["PermanentElements"]["Actives"]["TabButtonNormal"]
                    )
                    self.NewsTabStatus = False
                    
                if self.PackagesTabStatus == True:
                    self.validatorView.PackagesTabMainFrame.pack_forget()
                    self.validatorView.sidenavbarPackagesTabButton.configure(
                        fg_color = self.validatorView.template["PermanentElements"]["Actives"]["TabButtonNormal"]
                    )
                    self.PackagesTabStatus = False
                    
                if self.KitsTabStatus == True:
                    self.validatorView.KitsTabMainFrame.pack_forget()
                    self.validatorView.sidenavbarKitsTabButton.configure(
                        fg_color = self.validatorView.template["PermanentElements"]["Actives"]["TabButtonNormal"]
                    )
                    self.KitsTabStatus = False
                
                if self.RaportTabStatus == False:
                    self.validatorView.create_raports_tab()
                    self.validatorView.sidenavbarRaportTabButton.configure(
                        fg_color = self.validatorView.template["PermanentElements"]["Actives"]["TabButtonActive"]
                    )
                    self.raportTabRaportsBatchSize: int = 7
                    self.raportTabRaportsLoadedCount: int = 0
                    
                    self._load_more_raports()
                    self._monitor_scroll_raports()
                    self.RaportTabStatus = True
                
            elif tab == "News":
                if self.ValidationTabStatus == True:
                    self.validatorView.ValidationTabMainFrame.pack_forget()
                    self.validatorView.sidenavbarValidationTabButton.configure(
                        fg_color = self.validatorView.template["PermanentElements"]["Actives"]["TabButtonNormal"]
                    )
                    self.ValidationTabStatus = False
                    
                if self.PackagesTabStatus == True:
                    self.validatorView.PackagesTabMainFrame.pack_forget()
                    self.validatorView.sidenavbarPackagesTabButton.configure(
                        fg_color = self.validatorView.template["PermanentElements"]["Actives"]["TabButtonNormal"]
                    )
                    self.PackagesTabStatus = False
                    
                if self.KitsTabStatus == True:
                    self.validatorView.KitsTabMainFrame.pack_forget()
                    self.validatorView.sidenavbarKitsTabButton.configure(
                        fg_color = self.validatorView.template["PermanentElements"]["Actives"]["TabButtonNormal"]
                    )
                    self.KitsTabStatus = False
                
                if self.RaportTabStatus == True:
                    self.validatorView.RaportTabMainFrame.pack_forget()
                    self.validatorView.sidenavbarRaportTabButton.configure(
                        fg_color = self.validatorView.template["PermanentElements"]["Actives"]["TabButtonNormal"]
                    )
                    self.RaportTabStatus = False
                
                if self.NewsTabStatus == False:
                    self.validatorView.create_news_tab()
                    self.validatorView.sidenavbarNewsTabButton.configure(
                        fg_color = self.validatorView.template["PermanentElements"]["Actives"]["TabButtonActive"]
                    )
                    self._reload_news()
                    self._monitor_scroll_news()
                    self.NewsTabStatus = True
            
            elif tab == "Packages":
                self.validationPackageService.get_validation_packages(production = self.ProductionStatus)
                if self.ValidationTabStatus == True:
                    self.validatorView.ValidationTabMainFrame.pack_forget()
                    self.validatorView.sidenavbarValidationTabButton.configure(
                        fg_color = self.validatorView.template["PermanentElements"]["Actives"]["TabButtonNormal"]
                    )
                    self.ValidationTabStatus = False
                    
                if self.KitsTabStatus == True:
                    self.validatorView.KitsTabMainFrame.pack_forget()
                    self.validatorView.sidenavbarKitsTabButton.configure(
                        fg_color = self.validatorView.template["PermanentElements"]["Actives"]["TabButtonNormal"]
                    )
                    self.KitsTabStatus = False
                
                if self.RaportTabStatus == True:
                    self.validatorView.RaportTabMainFrame.pack_forget()
                    self.validatorView.sidenavbarRaportTabButton.configure(
                        fg_color = self.validatorView.template["PermanentElements"]["Actives"]["TabButtonNormal"]
                    )
                    self.RaportTabStatus = False
                
                if self.NewsTabStatus == True:
                    self.validatorView.NewsTabMainFrame.pack_forget()
                    self.validatorView.sidenavbarNewsTabButton.configure(
                        fg_color = self.validatorView.template["PermanentElements"]["Actives"]["TabButtonNormal"]
                    )
                    self.NewsTabStatus = False
                
                if self.PackagesTabStatus == False:
                    self.validatorView.create_packages_tab()
                    self.validatorView.sidenavbarPackagesTabButton.configure(
                        fg_color = self.validatorView.template["PermanentElements"]["Actives"]["TabButtonActive"]
                    )
                    self._reload_validation_packages()
                    self._monitor_scroll_packages()
                    self.PackagesTabStatus = True
            
            elif tab == "Kits":
                if self.ValidationTabStatus == True:
                    self.validatorView.ValidationTabMainFrame.pack_forget()
                    self.validatorView.sidenavbarValidationTabButton.configure(
                        fg_color = self.validatorView.template["PermanentElements"]["Actives"]["TabButtonNormal"]
                    )
                    self.ValidationTabStatus = False
                
                if self.RaportTabStatus == True:
                    self.validatorView.RaportTabMainFrame.pack_forget()
                    self.validatorView.sidenavbarRaportTabButton.configure(
                        fg_color = self.validatorView.template["PermanentElements"]["Actives"]["TabButtonNormal"]
                    )
                    self.RaportTabStatus = False
                
                if self.NewsTabStatus == True:
                    self.validatorView.NewsTabMainFrame.pack_forget()
                    self.validatorView.sidenavbarNewsTabButton.configure(
                        fg_color = self.validatorView.template["PermanentElements"]["Actives"]["TabButtonNormal"]
                    )
                    self.NewsTabStatus = False
                
                if self.PackagesTabStatus == True:
                    self.validatorView.PackagesTabMainFrame.pack_forget()
                    self.validatorView.sidenavbarPackagesTabButton.configure(
                        fg_color = self.validatorView.template["PermanentElements"]["Actives"]["TabButtonNormal"]
                    )
                    self.PackagesTabStatus = False
                    
                if self.KitsTabStatus == False:
                    self.validatorView.create_kits_tab()
                    self.validatorView.sidenavbarKitsTabButton.configure(
                        fg_color = self.validatorView.template["PermanentElements"]["Actives"]["TabButtonActive"]
                    )
                    self._reload_validation_kits()
                    self._monitor_scroll_kits()
                    self.KitsTabStatus = True
        
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._update_ui_for_tester_status.__name__}] "
                f"Failed to update the ui based on tester status.\n{traceBack}")
    # ===============================================
    
    # ========== Change Button Status ==========
    def change_sidenavbar_widget_states(self,
            widget: Literal["TesterButton", "SupportButton"],
            state: Literal["normal", "disabled"]) -> None:
        """Change the sidenavbar widget states."""
        try:
            if widget == "TesterButton":
                self.validatorView.after(0, lambda: self.validatorView.sidenavbarTesterButton.configure(state = state))
            elif widget == "SupportButton":
                self.validatorView.after(0, lambda: self.validatorView.sidenavbarSupportButton.configure(state = state))
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self.change_sidenavbar_widget_states.__name__}] "
                f"Failed to change sidenavbar widget state. \n{traceBack}")
    
    def change_validator_tab_widget_states(self, 
            widget: Literal[
                "ChooseEntry", "ChooseButton",
                "VerifyButton", "UpdateButton",
                "ValidateButton", "ValidateOptionMenu",
                "SaveEntry", "SaveButton"],
            state: Literal["normal", "disabled"]) -> None:
        """Change the validator tab widget states."""
        try:
            if widget == "ChooseEntry":
                self.validatorView.after(
                    0, lambda: self.validatorView.validationTabChooseEntry.configure(state = state))
            elif widget == "ChooseButton":
                self.validatorView.after(
                    0, lambda: self.validatorView.validationTabChooseButton.configure(state = state))
            elif widget == "VerifyButton":
                self.validatorView.after(
                    0, lambda: self.validatorView.validationTabVerifyButtonVerify.configure(state = state))
            elif widget == "UpdateButton":
                self.validatorView.after(
                    0, lambda: self.validatorView.validationTabVerifyButtonUpdate.configure(state = state))
            elif widget == "ValidateButton":
                self.validatorView.after(
                    0, lambda: self.validatorView.validationTabValidateButton.configure(state = state))
            elif widget == "ValidateOptionMenu":
                self.validatorView.after(
                    0, lambda: self.validatorView.validationTabValidateOptionMenu.configure(state = state))
            elif widget == "SaveEntry":
                self.validatorView.after(
                    0, lambda: self.validatorView.validationTabSaveRaportButton.configure(state = state))
            elif widget == "SaveButton":
                self.validatorView.after(
                    0, lambda: self.validatorView.validationTabSaveRaportButton.configure(state = state))
            self.validationTabWidgetStatuses[widget] = state
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self.change_validator_tab_widget_states.__name__}] "
                f"Failed to change validation tab widget state. \n{traceBack}")
    # ==========================================
    
    # ========== Validation Tab Functionality ==========
    # ===== Choose File =====
    def browse_files(self) -> None:
        """Select the zip archive or gpkg file to verify/validate"""
        try:
            filePath = filedialog.askopenfilename(
            filetypes = [
                ("Zip, GeoPackage", "*.zip *.gpkg"),
                ("Zip", "*.zip"), 
                ("GeoPackage", "*.gpkg")
                ]
            )
            if filePath:
                self.validateTabFilePath = filePath
                self.change_validator_tab_widget_states(widget = "ChooseEntry", state = "normal")
                self.validatorView.after(0, lambda: self.validatorView.validationTabChooseEntry.delete(0, ctk.END))
                self.validatorView.after(0, lambda: self.validatorView.validationTabChooseEntry.insert(0, filePath))
                self.change_validator_tab_widget_states(widget = "ChooseEntry", state = "disabled")
                self.change_validator_tab_widget_states(widget = "VerifyButton", state = "normal")
                self.change_validator_tab_widget_states(widget = "UpdateButton", state = "disabled")
                self.change_validator_tab_widget_states(widget = "ValidateButton", state = "disabled")
                self.change_validator_tab_widget_states(widget = "ValidateOptionMenu", state = "disabled")
                self.change_validator_tab_widget_states(widget = "SaveButton", state = "disabled")
            else:
                self.change_validator_tab_widget_states(widget = "ChooseEntry", state = "normal")
                self.validatorView.after(0, lambda: self.validatorView.validationTabChooseEntry.delete(0, ctk.END))
                self.change_validator_tab_widget_states(widget = "ChooseEntry", state = "disabled")
                self.change_validator_tab_widget_states(widget = "VerifyButton", state = "disabled")
                self.change_validator_tab_widget_states(widget = "UpdateButton", state = "disabled")
                self.change_validator_tab_widget_states(widget = "ValidateButton", state = "disabled")
                self.change_validator_tab_widget_states(widget = "ValidateOptionMenu", state = "disabled")
                self.change_validator_tab_widget_states(widget = "SaveButton", state = "disabled")
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self.browse_files.__name__}] "
                f"Failed to open the filedialog. \n{traceBack}")
    # =======================
    
    # ===== Download File =====
    def save_raport_file(self):
        """Save the validation report to CSV."""
        try:
            save_path = filedialog.asksaveasfilename(
                defaultextension=".csv",
                filetypes=[("CSV files", "*.csv"), ("All files", "*.*")],
                title="Save Validation Report"
            )
            # Save with proper encoding
            self.raportDataFrame.to_csv(save_path, index=False, encoding='utf-8')
            self.validatorView.validationTabSaveRaportEntry.configure( state = "normal")
            self.validatorView.validationTabSaveRaportEntry.delete(0, ctk.END)
            self.validatorView.validationTabSaveRaportEntry.insert(0, save_path)
            self.validatorView.validationTabSaveRaportEntry.configure( state = "disabled")
        
        except PermissionError:
            messagebox.showerror(
            "Eroare Salvare Raport",
            "Fisierul este folosit de alta aplicatie.")
        
        except Exception as e:
            self.logger.error(f"[{self.__class__.__name__}.{self.save_raport_file.__name__}] An error has occurred: {e}")
    # =========================
    
    # ===== Verify File =====
    def verification(self) -> None:
        """Verifies the select zip archive or gpkg file."""
        try:
            threading.Thread(
                target = self._verification_thread, 
                args = [self.validateTabFilePath, self._get_verification_results], 
                daemon = True).start()
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self.verification.__name__}] "
                f"Failed to verify the file. \n{traceBack}")
    
    def _verification_thread(self, file_path: str, get_verification_results_callback: GetVerificationResultsCallback) -> None:
        """Verification thread."""
        try:
            self._update_ui_before_verification()
            self.documentVerificationInstance = DocumentVerification()
            self.validationPackageService = ValidationPackageService()
            validTemplates = self.validationPackageService.get_validation_packages_version(production = self.ProductionStatus)
            localValidationPackages = self.validationPackageService.get_local_packages(production = self.ProductionStatus)
            
            self.documentVerificationInstance.verify_documentation(
                file_path = file_path, valid_templates = validTemplates, 
                local_validation_packages = localValidationPackages,
                get_verification_results_callback = get_verification_results_callback)
            
            self.documentVerificationInstance = None
            
            if self.validationTabVerificationResults is not None:
                self._update_ui_after_verification(
                    update = self.validationTabVerificationResults["UpdateStatus"],
                    validate = self.validationTabVerificationResults["ValidateStatus"],
                    error = self.validationTabVerificationResults["Error"])
            
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._verification_thread.__name__}] "
                f"Failed to start the verification thread. \n{traceBack}")
    
    def _get_verification_results(self, complete: bool = None, message: str = None, verification_result: dict[str, str] = None) -> None:
        """Gets the verification results"""
        try:
            if complete == False:
                self._place_verification_results(message = message)
            elif complete == True:
                self._place_verification_results(message = message)
                self.validationTabVerificationResults = verification_result
            
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._get_verification_results.__name__}] "
                f"Failed to get the verification results. \n{traceBack}")
    
    def _place_verification_results(self, message: str) -> None:
        """Places the verification results inside the verification scrollframe"""
        try:
            if message not in self.validationTabVerificationResultMessages:
                timestamp = datetime.datetime.now().strftime("%H:%M:%S")
                formatted_result = f"[{timestamp}] {message}"
                self.validationTabVerificationResultMessages.append(formatted_result)
            else:
                formatted_result = message

            scroll_frame = self.validatorView.validationTabVerifyScrollFrame

            # Make sure width is updated
            scroll_frame.update_idletasks()
            scrollable_width = scroll_frame.winfo_width()

            # Create label instead of canvas
            result_label = ctk.CTkLabel(
                master=scroll_frame,
                text=formatted_result,
                font=ctk.CTkFont(family="Arial", size=14, weight = "normal"),
                anchor="w",
                height = 1,
                justify="left",
                text_color="#000000",
                wraplength=scrollable_width - 100  # small padding
            )

            result_label.pack(fill="x", padx=0, pady=0)

            # Auto-scroll to bottom
            scroll_frame.update_idletasks()
            scroll_frame._parent_canvas.yview_moveto(1.0)
            
            if len(self.validationTabVerificationResultMessages) > 50:
                self.validationTabVerificationResultMessages[-20:]

        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._place_verification_results.__name__}] "
                f"Failed to place the verification results. \n{traceBack}")
    
    def _update_ui_before_verification(self) -> None:
        """Updates the UI before the verification."""
        try:
            self.change_sidenavbar_widget_states(widget = "TesterButton", state = "disabled")
            self.change_sidenavbar_widget_states(widget = "SupportButton", state = "disabled")
            self.change_validator_tab_widget_states(widget = "VerifyButton", state = "disabled")
            self.change_validator_tab_widget_states(widget = "UpdateButton", state = "disabled")
            self.change_validator_tab_widget_states(widget = "ValidateButton", state = "disabled")
            self.change_validator_tab_widget_states(widget = "ValidateOptionMenu", state = "disabled")
            self.change_validator_tab_widget_states(widget = "SaveButton", state = "disabled")
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._update_ui_before_verification.__name__}] "
                f"Failed to update the UI before the validation. \n{traceBack}")
    
    def _update_ui_after_verification(self, update: bool | None = None, validate: bool | None = None, error: bool | None = None) -> None:
        """Update the UI after the verification."""
        try:
            if validate == True:
                self.change_validator_tab_widget_states(widget = "VerifyButton", state = "normal")
                self.change_validator_tab_widget_states(widget = "UpdateButton", state = "disabled")
                self.change_validator_tab_widget_states(widget = "ValidateButton", state = "normal")
                self.change_validator_tab_widget_states(widget = "ValidateOptionMenu", state = "normal")
                self.change_validator_tab_widget_states(widget = "SaveButton", state = "disabled")
                self.change_sidenavbar_widget_states(widget = "SupportButton", state = "normal")
                
                self.validationTabOptionMenuSelectedVersion = self.validationTabVerificationResults["VersionTemplate"]
                self.validatorView.validationTabValidateOptionMenu.configure(
                    values = self.validationTabVerificationResults["LocalPackages"],
                    variable = ctk.StringVar(value = self.validationTabVerificationResults["VersionTemplate"])
                )
            elif update == True:
                self.change_validator_tab_widget_states(widget = "VerifyButton", state = "normal")
                self.change_validator_tab_widget_states(widget = "UpdateButton", state = "normal")
                self.validatorView.validationTabVerifyButtonUpdate.configure(
                    command = lambda production = self.ProductionStatus, packageVersion = self.validationTabVerificationResults["VersionTemplate"]: self._download_needed_validation_package(production = production, package_version = packageVersion))
                self.change_validator_tab_widget_states(widget = "ValidateButton", state = "disabled")
                self.change_validator_tab_widget_states(widget = "ValidateOptionMenu", state = "disabled")
                self.change_validator_tab_widget_states(widget = "SaveButton", state = "disabled")
                self.change_sidenavbar_widget_states(widget = "SupportButton", state = "normal")
            elif error == True:
                self.change_sidenavbar_widget_states(widget = "TesterButton", state = "normal")
                self.change_validator_tab_widget_states(widget = "VerifyButton", state = "normal")
                self.change_validator_tab_widget_states(widget = "UpdateButton", state = "disabled")
                self.change_validator_tab_widget_states(widget = "ValidateButton", state = "disabled")
                self.change_validator_tab_widget_states(widget = "ValidateOptionMenu", state = "disabled")
                self.change_validator_tab_widget_states(widget = "SaveButton", state = "disabled")
                self.change_sidenavbar_widget_states(widget = "SupportButton", state = "normal")
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._update_ui_after_verification.__name__}] "
                f"Failed to update the UI after the validation. \n{traceBack}")
    
    def _download_needed_validation_package(self, production: bool, package_version: str) -> None:
        try:
            packageUrl: str = self.validationPackageService.get_specific_package(production = production, package_version = package_version)
            self._start_validation_package_download(production = production, package_url = packageUrl)
            self._get_verification_results(complete = False, message = "Pachetul de validare a fost descarcat.")
            self.verification()
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._download_needed_validation_package.__name__}] "
                f"Failed to download the needed validation package. \n{traceBack}")
    # =======================
    
    # ===== Validate File =====
    def change_package_version(self, choice: str) -> None:
        """Change the validation package version to use based on the user selection."""
        try:
            self.validationTabOptionMenuSelectedVersion = choice
            if choice != self.validationTabVerificationResults["VersionTemplate"]:
                self.handle_warning(
                    title = "Versiune Validare",
                    message = "Versiunea pachetul de validare nu corespunde template-ului. Validarea se poate efectua, dar exista posibilitatea ca documentatia sa nu fie validata corect!"
                )
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self.change_package_version.__name__}] "
                f"Failed to change the validation package version. \n{traceBack}")
    
    def validation(self) -> None:
        try:
            self.raportDataFrame = None
            self.validationInProgress = True
            self._update_ui_before_validation()
            self._progressbar_start()
            
            threading.Thread(
                target = self._validation_thread, 
                args = [
                    self.validateTabFilePath, self.validationTabVerificationResults, self.ProductionStatus,
                    self.validationTabOptionMenuSelectedVersion, self._get_validation_results,
                    self._progressbar_steps, self._progressbar_update, self._progressbar_end],
                daemon = True).start()
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self.validation.__name__}] "
                f"Failed to validate the documentation. \n{traceBack}")
    
    def _validation_thread(self, 
            file_path: str, verification_results: dict[str, str], production: bool,
            validation_package_version: str, get_validation_results_callback: GetValidationResultsCallback,
            progressbar_steps: GetProgressbarSteps, progressbar_update: GetProgressbarUpdate, progressbar_end: GetProgressbarEnd) -> None:
        try:
            self.validationMetadataService = ValidationMetadataService()
            self.documentValidationInstance = DocumentValidation()
            # ===== Validation Metadata =====
            documentTemplateUrl = self.validationMetadataService.get_document_template(
                production = self.ProductionStatus, plan_type = verification_results["PlanType"], validation_package_version = validation_package_version)
            
            zipStructRulesGet: ZipStructRulesGet = partial(
                self.validationMetadataService.get_rules_zip_structure, self.ProductionStatus, verification_results["PlanType"], validation_package_version)
            gpkgStructRulesGet: GpkgStructRulesGet = partial(
                self.validationMetadataService.get_rules_gpkg_structure, self.ProductionStatus, verification_results["PlanType"], validation_package_version)
            gpkgDataRulesGet: GpkgDataRulesGet = partial(
                self.validationMetadataService.get_rules_gpkg_data, self.ProductionStatus, verification_results["PlanType"], validation_package_version)
            uatLimitGet: UatLimitGet = partial(
                self.validationMetadataService.get_uat_limit, self.ProductionStatus, verification_results["PlanType"], validation_package_version)
            # ===============================
            
            # ===== Validate Documentation =====
            self.documentValidationInstance.validate_documentation(
                file_path = file_path, verification_results = verification_results, 
                production = production, validation_package_version = validation_package_version, 
                template_url = documentTemplateUrl, get_validation_results_callback = get_validation_results_callback, 
                zip_struct_rules_get = zipStructRulesGet, gpkg_struct_rules_get = gpkgStructRulesGet,
                gpkg_data_rules_get = gpkgDataRulesGet, uat_limit_get = uatLimitGet,
                progressbar_end = progressbar_end, progressbar_steps = progressbar_steps, progressbar_update = progressbar_update)
            # ==================================
            
            self.validationInProgress = False
            self._internal_raport_save()
            self._update_ui_after_validation()
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._validation_thread.__name__}] "
                f"Failed to start the validation thread. \n{traceBack}")
    
    def _get_validation_results(self, complete: bool = None, status_validation: bool = None, message: str = None, raport: pd.DataFrame = None) -> None:
        try:
            # Validation in Progress
            if complete == False and message is not None and status_validation == None and raport == None:
                self._place_validation_results(message = message)
            # Validation Failed
            if complete == False and message is not None and status_validation == False and raport == None:
                self._place_validation_results(message = message)
                self.raportDataFrame = None
            # Validation Success
            if complete == True and message is not None and status_validation == True and raport is not None:
                self._place_validation_results(message = message)
                self.validationTabValidationStatus = True
                self.raportDataFrame = raport
            # Validation Fail
            if complete == True and message is not None and status_validation == False and raport is not None:
                self._place_validation_results(message = message)
                self.validationTabValidationStatus = False
                self.raportDataFrame = raport
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._get_validation_results.__name__}] "
                f"Failed to get the validation results. \n{traceBack}")
            
    def _place_validation_results(self, message: str) -> None:
        try:
            if message not in self.validationTabValidationResultMessages:
                timestamp = datetime.datetime.now().strftime("%H:%M:%S")
                formatted_result = f"[{timestamp}] {message}"
                self.validationTabValidationResultMessages.append(formatted_result)
            else:
                formatted_result = message

            scroll_frame = self.validatorView.validationTabValidateScrollFrame

            scroll_frame.update_idletasks()
            scrollable_width = scroll_frame.winfo_width()

            result_label = ctk.CTkLabel(
                master=scroll_frame,
                text=formatted_result,
                font=ctk.CTkFont(family="Arial", size=14, weight = "normal"),
                anchor="w",
                height = 1,
                justify="left",
                text_color="#000000",
                wraplength=scrollable_width - 100
            )
            result_label.pack(fill="x", padx=0, pady=0)
            # Auto-scroll to bottom
            scroll_frame.update_idletasks()
            scroll_frame._parent_canvas.yview_moveto(1.0)
            
            if len(self.validationTabValidationResultMessages) > 50:
                self.validationTabValidationResultMessages[-20:]
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._place_validation_results.__name__}] "
                f"Failed to place the validation results. \n{traceBack}")
    
    def _update_ui_before_validation(self) -> None:
        try:
            self.change_validator_tab_widget_states(widget = "VerifyButton", state = "disabled")
            self.change_validator_tab_widget_states(widget = "UpdateButton", state = "disabled")
            self.change_validator_tab_widget_states(widget = "ValidateButton", state = "disabled")
            self.change_validator_tab_widget_states(widget = "ValidateOptionMenu", state = "disabled")
            self.change_validator_tab_widget_states(widget = "SaveButton", state = "disabled")
            self.change_sidenavbar_widget_states(widget = "SupportButton", state = "disabled")
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._update_ui_before_validation.__name__}] "
                f"Failed to update the UI before the validation. \n{traceBack}")
    
    def _update_ui_after_validation(self) -> None:
        try:
            self.change_sidenavbar_widget_states(widget = "TesterButton", state = "normal")
            self.change_validator_tab_widget_states(widget = "VerifyButton", state = "normal")
            self.change_validator_tab_widget_states(widget = "UpdateButton", state = "disabled")
            self.change_validator_tab_widget_states(widget = "ValidateButton", state = "normal")
            self.change_validator_tab_widget_states(widget = "ValidateOptionMenu", state = "normal")
            self.change_sidenavbar_widget_states(widget = "SupportButton", state = "normal")
            if self.raportDataFrame is not None:
                self.change_validator_tab_widget_states(widget = "SaveButton", state = "normal")
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._update_ui_after_validation.__name__}] "
                f"Failed to update the UI after the validation. \n{traceBack}")
            
    def _progressbar_start(self) -> None:
        try:
            self.validationTabProgressbarCurrent = 0
            self.validatorView.validationTabProgressBar.set(0)
            self.validatorView.validationTabProgressBar.configure(
                progress_color = self.validatorView.template["ValidationTab"]["ValidateFrame"]["Actives"]["ProgressBarValidationInProgressColor"]
            )
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._progressbar_start.__name__}] "
                f"Failed to start the progressbar. \n{traceBack}")
    
    def _progressbar_steps(self, steps: int) -> None:
        try:
            self.validationTabProgressbarSteps = steps
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._progressbar_steps.__name__}] "
                f"Failed to get the progressbar steps. \n{traceBack}")
            
    def _progressbar_update(self) -> None:
        try:
            self.validationTabProgressbarCurrent += 1
            progress_value = self.validationTabProgressbarCurrent / self.validationTabProgressbarSteps
            self.validatorView.validationTabProgressBar.set(progress_value)
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._progressbar_steps.__name__}] "
                f"Failed to get the progressbar steps. \n{traceBack}")
    
    def _progressbar_end(self, validation_status: bool) -> None:
        try:
            if validation_status == False:
                self.validatorView.validationTabProgressBar.configure(
                    progress_color = self.validatorView.template["ValidationTab"]["ValidateFrame"]["Actives"]["ProgressBarValidationFailColor"]
                )
            else:
                self.validatorView.validationTabProgressBar.configure(
                    progress_color = self.validatorView.template["ValidationTab"]["ValidateFrame"]["Actives"]["ProgressBarValidationPassColor"]
                )
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._progressbar_end.__name__}] "
                f"Failed to end the progressbar. \n{traceBack}")
    
    def _progressbar_replace(self, validation_in_progress: bool) -> None:
        try:
            if validation_in_progress == True:
                progress_value = self.validationTabProgressbarCurrent / self.validationTabProgressbarSteps
                self.validatorView.validationTabProgressBar.set(progress_value)
                self.validatorView.validationTabProgressBar.configure(
                    progress_color = self.validatorView.template["ValidationTab"]["ValidateFrame"]["Actives"]["ProgressBarValidationInProgressColor"]
                )
            else:
                self.validatorView.validationTabProgressBar.set(0)
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._progressbar_replace.__name__}] "
                f"Failed to replace the progressbar. \n{traceBack}")
    # =========================
    
    # ===== Save File =====
    def _internal_raport_save(self) -> None:
        try:
            if self.raportDataFrame is not None and self.validateTabFilePath is not None:
                self.raportManagerInstance.save_raport_local(
                    raport = self.raportDataFrame,
                    file_path = self.validateTabFilePath,
                    validation_status = self.validationTabValidationStatus,
                    production = self.ProductionStatus,
                    plan_type = self.validationTabVerificationResults["PlanType"])
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._internal_raport_save.__name__}] "
                f"Failed to save the raport internally. \n{traceBack}")
    # =====================
    # ==================================================
    
    # ========== Raport Tab Functionality ==========
    def _load_more_raports(self) -> None:
        try:
            self.raportManagerInstance._load_raports(production = self.ProductionStatus)
            
            # Determine which raports list to use
            raports_list = (
                self.raportManagerInstance._raportsPublic
                if self.ProductionStatus else
                self.raportManagerInstance._raportsDevelopment
            )

            if self.raportTabRaportsLoading:
                return

            label = getattr(self.validatorView.RaportTabMainFrame, "noRaportsLabel", None)
            if len(raports_list) == 0:
                if not label:
                    self.validatorView.RaportTabMainFrame.noRaportsLabel = ctk.CTkLabel(
                        master=self.validatorView.RaportTabMainFrame,
                        **self._template_processing(
                            self.validatorView.template["RaportTab"]["Raports"]["NoRaports"]
                        )
                    )
                    label = self.validatorView.RaportTabMainFrame.noRaportsLabel
                if not label.winfo_ismapped():
                    label.place(anchor="center", relx=0.5, rely=0.5)
                return  # Nothing else to load

            if label and label.winfo_ismapped():
                label.place_forget()
                
            self.raportTabRaportsLoading = True

            total = len(raports_list)
            startIndex = max(0, total - self.raportTabRaportsLoadedCount - self.raportTabRaportsBatchSize)
            endIndex = total - self.raportTabRaportsLoadedCount

            batch = raports_list[startIndex:endIndex]
            batch.reverse()  # So newest appears first

            # Place the raports in the UI
            for raport in batch:
                self._place_raport_frame(raport=raport)

            self.raportTabRaportsLoadedCount += len(batch)
            self.raportTabRaportsLoading = False
            self.validatorView.RaportTabScrollframe.update_idletasks()
            
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._load_more_raports.__name__}] "
                f"Failed load more raports. \n{traceBack}")
    
    def _place_raport_frame(self, raport: dict[str, str]) -> None:
        try:
            deleteImage = Image.open(self.validatorView.images["bin"])
            saveImage = Image.open(self.validatorView.images["save"])
            deleteCtkImage = ctk.CTkImage(
                light_image = deleteImage,
                dark_image = deleteImage,
                size = (20, 20))
            saveCtkImage = ctk.CTkImage(
                light_image = saveImage,
                dark_image = saveImage,
                size = (20, 20))
            # ===== Frame =====
            raportFrame = ctk.CTkFrame(
                master = self.validatorView.RaportTabScrollframe,
                **self.validatorView.template["RaportTab"]["Raports"]["RaportFrame"]
            )
            # =================
            # ===== Labels =====
            if raport["Judet"] != "":
                nameLabel = ctk.CTkLabel(
                    master=raportFrame,
                    text = f"Documentatie: "
                        f"{raport['Judet']}, "
                        f"{raport['UAT']}, "
                        f"{raport['Siruta']}",
                    **self._template_processing(self.validatorView.template["RaportTab"]["Raports"]["RaportLabel"]))
            else:
                nameLabel = ctk.CTkLabel(
                    master=raportFrame,
                    text = f"Documentatie: "
                        f"{raport['UAT']}, "
                        f"{raport['Siruta']}",
                    **self._template_processing(self.validatorView.template["RaportTab"]["Raports"]["RaportLabel"]))
            planLabel = ctk.CTkLabel(
                master=raportFrame,
                text = f"Tip Plan: "
                    f"{raport['TipPlan']}",
                **self._template_processing(self.validatorView.template["RaportTab"]["Raports"]["RaportLabel"]))
            dataLabel = ctk.CTkLabel(
                master=raportFrame,
                text = f"Validat la: "
                    f"{raport['TimeDate']}",
                **self._template_processing(self.validatorView.template["RaportTab"]["Raports"]["RaportLabel"]))
            
            statusLabel = ctk.CTkLabel(
                master=raportFrame,
                text = f"Status: "
                    f"{raport['ValdationStatus']}",
                **self._template_processing(self.validatorView.template["RaportTab"]["Raports"]["RaportLabel"]))
            # ==================
            # ===== Buttons =====
            saveButton = ctk.CTkButton(
                master = raportFrame,
                image = saveCtkImage,
                **self._template_processing(self.validatorView.template["RaportTab"]["Raports"]["SaveButton"]),
                command = lambda path = raport["Path"]: self._save_raport_dataframe(raport_path = path))
            deleteButton = ctk.CTkButton(
                master = raportFrame,
                image = deleteCtkImage,
                **self._template_processing(self.validatorView.template["RaportTab"]["Raports"]["DeleteButton"]),
                command = lambda frame = raportFrame, path = raport["Path"]: self._delete_raport_dataframe(frame = frame, raport_path = path))
            # ===================
            
            # ===== Widget Placement =====
            raportFrame.pack(anchor = "center", padx = (0, 0), pady = (5, 0))
            raportFrame.pack_propagate(False)
            nameLabel.pack(anchor = "w", padx = (10, 5), pady = (5, 0))
            planLabel.pack(anchor = "w", padx = (10, 5), pady = (0, 0))
            dataLabel.pack(anchor = "w", padx = (10, 5), pady = (0, 0))
            statusLabel.pack(anchor = "w", padx = (10, 5), pady = (0, 0))
            saveButton.place(relx = 0.8675, rely = 0.1)
            deleteButton.place(relx = 0.9325, rely = 0.1)
            # ============================
            
            self.raportTabRaportsFramesLoaded.append(raportFrame)
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._place_raport_frame.__name__}] "
                f"Failed place the raport. \n{traceBack}")
    
    def _monitor_scroll_raports(self) -> None:
        try:
            canvas = self.validatorView.RaportTabScrollframe._parent_canvas
            start, end = canvas.yview()
            
            if self.ProductionStatus == True:
                if end >= 0.9:
                    if not self.raportTabRaportsLoading:
                        if self.raportTabRaportsLoadedCount < len(self.raportManagerInstance._raportsPublic):
                            self._load_more_raports()
            else:
                if end >= 0.9:
                    if not self.raportTabRaportsLoading:
                        if self.raportTabRaportsLoadedCount < len(self.raportManagerInstance._raportsDevelopment):
                            self._load_more_raports()

            # Check again after 100ms
            self.validatorView.after(100, self._monitor_scroll_raports)
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._monitor_scroll_packages.__name__}] "
                f"Failed to check the scroll level for the raports. \n{traceBack}")
    
    def _save_raport_dataframe(self, raport_path: str) -> None:
        """Copies the file to another location."""
        try:
            save_path = filedialog.asksaveasfilename(
                defaultextension=".csv",
                filetypes=[("CSV files", "*.csv"), ("All files", "*.*")],
                title="Save Validation Report"
            )
            shutil.copy2(raport_path, save_path)
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._save_raport_dataframe.__name__}] "
                f"Failed to save the raport data frame. \n{traceBack}")
    
    def _delete_raport_dataframe(self, frame: ctk.CTkFrame, raport_path: str) -> None:
        """Removes the frame and deletes the raport"""
        try:
            frame.forget()
            if os.path.exists(raport_path):
                os.remove(raport_path)
            self.raportManagerInstance._load_raports(self.ProductionStatus)
            self.raportTabRaportsFramesLoaded.remove(frame)
            self._reload_raports()
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._delete_raport_dataframe.__name__}] "
                f"Failed to delete the raport data frame. \n{traceBack}")
    
    def _reload_raports(self) -> None:
        # Reset state FIRST
        self.raportTabRaportsLoadedCount = 0
        self.raportTabRaportsLoading = False

        # Destroy widgets safely after idle
        for f in self.raportTabRaportsFramesLoaded:
            f.after(0, f.destroy)

        self.raportTabRaportsFramesLoaded.clear()

        # Load again AFTER UI stabilizes
        self.validatorView.after(50, self._load_more_raports)
    # ==============================================
    
    # ========== News Tab Functionality ==========
    def _load_more_news(self):
        try:
            self.newsService.get_all_news(production = self.ProductionStatus)
            
            # Determine which raports list to use
            newsList = (
                self.newsService._newsPublic
                if self.ProductionStatus else
                self.newsService._newsDevelopment)
            
            if self.NewsTabNewsLoading:
                return
            
            label = getattr(self.validatorView.NewsTabMainFrame, "noNewsLabel", None)
            if not newsList:
                if not label:
                    self.validatorView.NewsTabMainFrame.noNewsLabel = ctk.CTkLabel(
                        master=self.validatorView.NewsTabMainFrame,
                        **self._template_processing(
                            self.validatorView.template["NewsTab"]["News"]["NoNews"]
                        )
                    )
                    label = self.validatorView.NewsTabMainFrame.noNewsLabel
                if not label.winfo_ismapped():
                    label.place(anchor="center", relx=0.5, rely=0.5)
                return 

            if label and label.winfo_ismapped():
                label.place_forget()
                
            self.NewsTabNewsLoading = True

            total = len(newsList)
            startIndex = max(0, total - self.NewsTabNewsLoadedCount - self.NewsTabNewsBatchSize)
            endIndex = total - self.NewsTabNewsLoadedCount

            batch = newsList[startIndex:endIndex]
            
            # Place the raports in the UI
            for news in batch:
                self._place_news_frame(news = news)

            self.NewsTabNewsLoadedCount += len(batch)
            self.NewsTabNewsLoading = False
            self.validatorView.NewsTabScrollframe.update_idletasks()
            
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._load_more_news.__name__}] "
                f"Failed to load more news. \n{traceBack}")
    
    def _place_news_frame(self, news: dict[str, str]) -> None:
        try:
            canvas = self.validatorView.NewsTabScrollframe._parent_canvas
            scroll_position = {"value": 0}
            # Load original PIL image
            arrow_pil = Image.open(self.validatorView.images["down_arrow"])

            # Create rotated version
            arrow_rotated_pil = arrow_pil.rotate(180)

            # Create CTkImage objects
            arrowDown = ctk.CTkImage(light_image=arrow_pil, size=(20, 20))
            arrowUp = ctk.CTkImage(light_image=arrow_rotated_pil, size=(20, 20))

            # ===== Frame =====
            newsFrame = ctk.CTkFrame(
                master=self.validatorView.NewsTabScrollframe,
                **self.validatorView.template["NewsTab"]["News"]["NewsFrame"]
            )
            newsFrame.pack(anchor="center", pady=(5, 0), fill="x", padx=5)
            # IMPORTANT: allow auto resize
            newsFrame.pack_propagate(True)

            # ===== Title =====
            titleLabel = ctk.CTkLabel(
                master=newsFrame,
                text=news["Title"],
                **self._template_processing(
                    self.validatorView.template["NewsTab"]["News"]["NewsTitle"]
                )
            )
            titleLabel.pack(anchor="w", padx=10, pady=(10, 0))

            # ===== Release Date =====
            releaseLabel = ctk.CTkLabel(
                master=newsFrame,
                text=f"Data: {news['ReleaseDate']}",
                **self._template_processing(
                    self.validatorView.template["NewsTab"]["News"]["NewsLabel"]
                )
            )
            releaseLabel.pack(anchor="w", padx=10, pady=(5, 10))

            # ===== Partial Description =====
            partialLabel = ctk.CTkLabel(
                master=newsFrame,
                text=f"{news['Description'][:80]}...",
                wraplength=600,
                justify="left",
                **self._template_processing(
                    self.validatorView.template["NewsTab"]["News"]["NewsLabel"]
                )
            )
            partialLabel.pack(anchor="w", padx=10, pady=(0, 10))

            # ===== Full Description (hidden initially) =====
            fullLabel = ctk.CTkLabel(
                master=newsFrame,
                text=news["Description"],
                wraplength=550,
                justify="left",
                **self._template_processing(
                    self.validatorView.template["NewsTab"]["News"]["NewsLabel"]
                )
            )

            # ===== Toggle Function =====
            def toggle():
                if fullLabel.winfo_ismapped():
                    # Collapse
                    fullLabel.pack_forget()
                    partialLabel.pack(anchor="w", padx=10, pady=(0, 10))
                    readButton.configure(image = arrowDown)
                    canvas.yview_moveto(scroll_position["value"])
                else:
                    # Expand
                    partialLabel.pack_forget()
                    fullLabel.pack(anchor="w", padx=10, pady=(0, 10))
                    readButton.configure(image = arrowUp)
                    scroll_position["value"] = canvas.yview()[0]
                    

            # ===== Button =====
            readButton = ctk.CTkButton(
                master=newsFrame,
                image=arrowDown,
                command=toggle,
                **self._template_processing(
                    self.validatorView.template["NewsTab"]["News"]["ReadButton"]
                )
            )
            readButton.pack(anchor="e", side = "bottom", padx = 10, pady = (0, 10))

            self.NewsTabNewsFramesLoaded.append(newsFrame)

        except Exception:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._place_news_frame.__name__}] "
                f"Failed to place the news frame. \n{traceBack}"
            )
    
    def _monitor_scroll_news(self):
        try:
            canvas = self.validatorView.NewsTabScrollframe._parent_canvas
            start, end = canvas.yview()
            
            if self.ProductionStatus == True:
                if end >= 0.9:
                    if not self.NewsTabNewsLoading:
                        if self.NewsTabNewsLoadedCount < len(self.newsService._newsPublic):
                            self._load_more_news()
            else:
                if end >= 0.9:
                    if not self.NewsTabNewsLoading:
                        if self.NewsTabNewsLoadedCount < len(self.newsService._newsDevelopment):
                            self._load_more_news()

            self.validatorView.after(100, self._monitor_scroll_news)
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._monitor_scroll_news.__name__}] "
                f"Failed to monitor the news scrollframe. \n{traceBack}")
    
    def _reload_news(self):
        try:
            # Reset state FIRST
            self.NewsTabNewsLoadedCount = 0
            self.NewsTabNewsLoading = False

            # Destroy widgets safely after idle
            for f in self.NewsTabNewsFramesLoaded:
                f.after(0, f.destroy)

            self.NewsTabNewsFramesLoaded.clear()

            # Load again AFTER UI stabilizes
            self.validatorView.after(50, self._load_more_news)
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._reload_news.__name__}] "
                f"Failed to monitor the news scrollframe. \n{traceBack}")
    
    def _expand_news_frame(self, frame: ctk.CTkFrame):
        try:
            frame.configure(
                height = 500
            )
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._expand_news_frame.__name__}] "
                f"Failed to expand the news frame. \n{traceBack}")
    
    def _shrink_news_frame(self):
        try:
            pass
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._expand_news_frame.__name__}] "
                f"Failed to shrink the news frame. \n{traceBack}")
    # ============================================
    
    # ========== Packages Tab Functionality ==========
    def _load_more_validation_packages(self) -> None:
        try:
            if self.PackagesTabStatus == False:
                return
            # Determine which package list to use
            package_list = (
                self.validationPackageService._packagesAvailablePublic
                if self.ProductionStatus else
                self.validationPackageService._packagesAvailableDevelopment
            )

            if self.packagesTabPackagesLoading:
                return

            label = getattr(self.validatorView.PackagesTabMainFrame, "noPackagesLabel", None)
            if not package_list:
                if not label:
                    self.validatorView.PackagesTabMainFrame.noPackagesLabel = ctk.CTkLabel(
                        master=self.validatorView.PackagesTabMainFrame,
                        **self._template_processing(
                            self.validatorView.template["PackagesTab"]["Packages"]["NoPackages"]
                        )
                    )
                    label = self.validatorView.PackagesTabMainFrame.noPackagesLabel
                if not label.winfo_ismapped():
                    label.place(anchor="center", relx=0.5, rely=0.5)
                return  # Nothing else to load

            if label and label.winfo_ismapped():
                label.place_forget()

            self.packagesTabPackagesLoading = True

            total = len(package_list)
            startIndex = max(0, total - self.packagesTabPackagesLoadedCount - self.packagesTabPackagesBatchSize)
            endIndex = total - self.packagesTabPackagesLoadedCount

            batch = package_list[startIndex:endIndex]
            for package in batch:
                self._place_validation_packages(package=package)

            self.packagesTabPackagesLoadedCount += len(batch)
            self.packagesTabPackagesLoading = False
            self.validatorView.PackagesTabScrollframe.update_idletasks()
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._load_more_validation_packages.__name__}] "
                f"Failed to load the validation packages. \n{traceBack}")
            
    def _place_validation_packages(self, package: dict[str, str]) -> None:
        try:
            canvas = self.validatorView.PackagesTabScrollframe._parent_canvas
            scroll_position = {"value": 0}
            # Load original PIL image
            arrow_pil = Image.open(self.validatorView.images["down_arrow"])

            # Create rotated version
            arrow_rotated_pil = arrow_pil.rotate(180)

            # Create CTkImage objects
            arrowDown = ctk.CTkImage(light_image=arrow_pil, size=(20, 20))
            arrowUp = ctk.CTkImage(light_image=arrow_rotated_pil, size=(20, 20))
            
            # ===== Frame =====
            Frame = ctk.CTkFrame(
                master = self.validatorView.PackagesTabScrollframe,
                **self.validatorView.template["PackagesTab"]["Packages"]["PackageFrame"])
            # =================
            
            # ===== Package Frame =====
            packageFrame = ctk.CTkFrame(
                master = Frame,
                **self.validatorView.template["PackagesTab"]["Packages"]["InnerPackageFrame"])
            topFrame = ctk.CTkFrame(master = packageFrame, fg_color = "#FFFFFF")
            leftFrame = ctk.CTkFrame(master = topFrame, fg_color = "#FFFFFF")
            
            # =========================
            # ===== Label =====
            nameLabel = ctk.CTkLabel(
                master = leftFrame,
                text = f"{package['Title']}",
                **self._template_processing(self.validatorView.template["PackagesTab"]["Packages"]["PackageTitle"]))
            dateLabel = ctk.CTkLabel(
                master=leftFrame,
                text = f"Lansat la: "
                    f"{package['ReleaseDate']}",
                **self._template_processing(self.validatorView.template["PackagesTab"]["Packages"]["PackageLabel"]))
            # =================
            # ===== Button =====
            if package["Downloadable"] and not self.validationInProgress:
                downloadButton = ctk.CTkButton(
                    master = topFrame,
                    **self._template_processing(self.validatorView.template["PackagesTab"]["Packages"]["DownloadButton"]),
                    command = lambda production_status = self.ProductionStatus, package_url = package["DownloadLink"]: self._start_validation_package_download(production = production_status, package_url = package_url))
            if package["Downloadable"] and self.validationInProgress:
                downloadButton = ctk.CTkButton(
                    master = topFrame,
                    **self._template_processing(self.validatorView.template["PackagesTab"]["Packages"]["DownloadButton"]),
                    state = "disabled")
            elif not package["Downloadable"]:
                downloadButton = ctk.CTkButton(
                    master = topFrame,
                    **self._template_processing(self.validatorView.template["PackagesTab"]["Packages"]["DownloadedButton"]),
                    state = "disabled")
            # ==================
            # ===== Partial Description =====
            partialLabel = ctk.CTkLabel(
                master=packageFrame,
                text=f"{package['Description'][:80]}...",
                wraplength=550,
                justify="left",
                **self._template_processing(
                    self.validatorView.template["NewsTab"]["News"]["NewsLabel"]))
            # ===== Full Description (hidden initially) =====
            fullLabel = ctk.CTkLabel(
                master=packageFrame,
                text=package["Description"],
                wraplength=550,
                justify="left",
                **self._template_processing(
                    self.validatorView.template["NewsTab"]["News"]["NewsLabel"]))
            # ===== Toggle Function =====
            def toggle():
                if fullLabel.winfo_ismapped():
                    # Collapse
                    fullLabel.pack_forget()
                    partialLabel.pack(side = "bottom", anchor="w", padx=10, pady=(0, 10))
                    readButton.configure(image = arrowDown)
                    canvas.yview_moveto(scroll_position["value"])
                else:
                    # Expand
                    partialLabel.pack_forget()
                    fullLabel.pack(side = "bottom", anchor="w", padx=10, pady=(0, 10))
                    readButton.configure(image = arrowUp)
                    scroll_position["value"] = canvas.yview()[0]
            # ==========================
            # ===== Button =====
            readButton = ctk.CTkButton(
                master=packageFrame,
                image=arrowDown,
                command=toggle,
                **self._template_processing(
                    self.validatorView.template["NewsTab"]["News"]["ReadButton"]
                )
            )
            readButton.pack(anchor="e", side = "bottom", padx = 10, pady = (0, 10))
            # ==================
            # ===== Separator =====
            separator = ctk.CTkFrame(
                master = Frame,
                **self.validatorView.template["PackagesTab"]["Packages"]["SeparatorFrame"])
            # =====================
            # ===== Template Frame =====
            templateFrame = ctk.CTkFrame(
                master = Frame,
                **self.validatorView.template["PackagesTab"]["Packages"]["InnerPackageFrame"])
            # =========================
            # ===== Label =====
            templateLabel = ctk.CTkLabel(
                master = templateFrame,
                text = "Descarca Machete Documentatii:",
                **self._template_processing(self.validatorView.template["PackagesTab"]["Packages"]["PackageTitle"]))
            # =================
            # ===== Buttons =====
            pugButton = ctk.CTkButton(
                master = templateFrame,
                text = "PUG",
                fg_color = self.validatorView.template["PackagesTab"]["Packages"]["PlanAvailability"]["True"] if package["PUG"] else self.validatorView.template["PackagesTab"]["Packages"]["PlanAvailability"]["False"],
                hover_color = self.validatorView.template["PackagesTab"]["Packages"]["PlanAvailability"]["HoverTrue"] if package["PUG"] else self.validatorView.template["PackagesTab"]["Packages"]["PlanAvailability"]["HoverFalse"],
                **self.validatorView.template["PackagesTab"]["Packages"]["PlanFrame"],
                **self._template_processing(self.validatorView.template["PackagesTab"]["Packages"]["PlanLabel"]),
                state = "normal" if package["PUG"] else "disabled",
                command = lambda templateUrl = package["PUG"]: self._download_package_template(template_url = templateUrl))
            puzButton = ctk.CTkButton(
                master = templateFrame,
                text = "PUZ",
                fg_color = self.validatorView.template["PackagesTab"]["Packages"]["PlanAvailability"]["True"] if package["PUZ"] else self.validatorView.template["PackagesTab"]["Packages"]["PlanAvailability"]["False"],
                hover_color = self.validatorView.template["PackagesTab"]["Packages"]["PlanAvailability"]["HoverTrue"] if package["PUZ"] else self.validatorView.template["PackagesTab"]["Packages"]["PlanAvailability"]["HoverFalse"],
                **self.validatorView.template["PackagesTab"]["Packages"]["PlanFrame"],
                **self._template_processing(self.validatorView.template["PackagesTab"]["Packages"]["PlanLabel"]),
                state = "normal" if package["PUZ"] else "disabled",
                command = lambda templateUrl = package["PUZ"]: self._download_package_template(template_url = templateUrl))
            pudButton = ctk.CTkButton(
                master = templateFrame,
                text = "PUD",
                fg_color = self.validatorView.template["PackagesTab"]["Packages"]["PlanAvailability"]["True"] if package["PUD"] else self.validatorView.template["PackagesTab"]["Packages"]["PlanAvailability"]["False"],
                hover_color = self.validatorView.template["PackagesTab"]["Packages"]["PlanAvailability"]["HoverTrue"] if package["PUD"] else self.validatorView.template["PackagesTab"]["Packages"]["PlanAvailability"]["HoverFalse"],
                **self.validatorView.template["PackagesTab"]["Packages"]["PlanFrame"],
                **self._template_processing(self.validatorView.template["PackagesTab"]["Packages"]["PlanLabel"]),
                state = "normal" if package["PUD"] else "disabled",
                command = lambda templateUrl = package["PUD"]: self._download_package_template(template_url = templateUrl))
            patjButton = ctk.CTkButton(
                master = templateFrame,
                text = "PATJ",
                fg_color = self.validatorView.template["PackagesTab"]["Packages"]["PlanAvailability"]["True"] if package["PATJ"] else self.validatorView.template["PackagesTab"]["Packages"]["PlanAvailability"]["False"],
                hover_color = self.validatorView.template["PackagesTab"]["Packages"]["PlanAvailability"]["HoverTrue"] if package["PATJ"] else self.validatorView.template["PackagesTab"]["Packages"]["PlanAvailability"]["HoverFalse"],
                **self.validatorView.template["PackagesTab"]["Packages"]["PlanFrame"],
                **self._template_processing(self.validatorView.template["PackagesTab"]["Packages"]["PlanLabel"]),
                state = "normal" if package["PATJ"] else "disabled",
                command = lambda templateUrl = package["PATJ"]: self._download_package_template(template_url = templateUrl))
            pmudButton = ctk.CTkButton(
                master = templateFrame,
                text = "PMUD",
                fg_color = self.validatorView.template["PackagesTab"]["Packages"]["PlanAvailability"]["True"] if package["PMUD"] else self.validatorView.template["PackagesTab"]["Packages"]["PlanAvailability"]["False"],
                hover_color = self.validatorView.template["PackagesTab"]["Packages"]["PlanAvailability"]["HoverTrue"] if package["PMUD"] else self.validatorView.template["PackagesTab"]["Packages"]["PlanAvailability"]["HoverFalse"],
                **self.validatorView.template["PackagesTab"]["Packages"]["PlanFrame"],
                **self._template_processing(self.validatorView.template["PackagesTab"]["Packages"]["PlanLabel"]),
                state = "normal" if package["PMUD"] else "disabled",
                command = lambda templateUrl = package["PMUD"]: self._download_package_template(template_url = templateUrl))
            # ===================
            
            # ===== Widget Placement =====
            Frame.pack(anchor = "center", padx = (0, 0), pady = (5, 0))
            packageFrame.pack(anchor="w", padx=5, pady=5, fill="both")
            # top row
            topFrame.pack(fill="x", padx=(5,5), pady=(5,5))
            leftFrame.pack(side="left", anchor="w", padx=(5,5), pady=(5,5))
            downloadButton.pack(side="right", anchor="e", padx=(5,5), pady=(5,5))
            # labels stacked on left
            nameLabel.pack(anchor="w")
            dateLabel.pack(anchor="w")
            # middle
            partialLabel.pack(side = "left", anchor="w", padx=10, pady=(0, 10))
            # bottom
            readButton.pack(anchor="e", padx=10, pady=(0,10))
            separator.pack(anchor="center", padx=5, pady=5, fill="x")
            # ===== Template Frame =====
            templateFrame.pack(anchor="center", padx=5, pady=5)
            templateFrame.pack_propagate(False)

            templateLabel.pack(anchor="nw", padx=5, pady=5)

            pugButton.pack(side="left", padx=5, pady=(0,5))
            puzButton.pack(side="left", padx=5, pady=(0,5))
            pudButton.pack(side="left", padx=5, pady=(0,5))
            patjButton.pack(side="left", padx=5, pady=(0,5))
            pmudButton.pack(side="left", padx=5, pady=(0,5))
            
            self.packagesTabPackagesFramesLoaded.append(Frame)
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._place_validation_packages.__name__}] "
                f"Failed to place the validation packages. \n{traceBack}")
    
    def _monitor_scroll_packages(self) -> None:
        try:
            canvas = self.validatorView.PackagesTabScrollframe._parent_canvas
            start, end = canvas.yview()
            
            if self.ProductionStatus == True:
                if end >= 0.9:
                    if not self.packagesTabPackagesLoading:
                        if self.packagesTabPackagesLoadedCount < len(self.validationPackageService._packagesAvailablePublic):
                            self._load_more_validation_packages()
            else:
                if end >= 0.9:
                    if not self.packagesTabPackagesLoading:
                        if self.packagesTabPackagesLoadedCount < len(self.validationPackageService._packagesAvailableDevelopment):
                            self._load_more_validation_packages()

            # Check again after 100ms
            self.validatorView.after(100, self._monitor_scroll_packages)
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._monitor_scroll_packages.__name__}] "
                f"Failed to check the scroll level for the packages. \n{traceBack}")
            
    def _reload_validation_packages(self) -> None:
        try:
            # Reset state FIRST
            self.packagesTabPackagesLoadedCount = 0
            self.packagesTabPackagesLoading = False

            # Destroy widgets safely after idle
            for f in self.packagesTabPackagesFramesLoaded:
                f.after(0, f.destroy)

            self.packagesTabPackagesFramesLoaded.clear()

            # Load again AFTER UI stabilizes
            self.validationPackageService.get_validation_packages(self.ProductionStatus)
            self.validatorView.after(50, self._load_more_validation_packages)
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._reload_validation_packages.__name__}] "
                f"Failed to reload the validation packages. \n{traceBack}")
    
    def _start_validation_package_download(self, production: bool, package_url: str) -> None:
        try:
            # Create the Toplevel
            self.validationViewDownloader = ctk.CTkToplevel(
                self.validatorView,
                fg_color=self.validatorView.template["PackageDownloader"]["MainFrame"]["fg_color"]
            )
            
            # Load icon
            logoPath = self.validatorView.images["logo"]
            logoImage = Image.open(logoPath)
            self._logo_photo = ImageTk.PhotoImage(logoImage)
            
            # Set icon using iconphoto (works after transient/grab)
            self.validationViewDownloader.wm_iconphoto(False, self._logo_photo)
            # Prevent auto-close - grab focus but don't block
            self.validationViewDownloader.transient(self.validatorView)  # Stay on top of parent
            self.validationViewDownloader.grab_set()  # Modal behavior
            self.validationViewDownloader.title("Pachet Validare...")
            self.validationViewDownloader.resizable(False, False)
            width = self.validatorView.template["PackageDownloader"]["MainFrame"]["width"]
            height = self.validatorView.template["PackageDownloader"]["MainFrame"]["height"]
            screenWidth = self.validationViewDownloader.winfo_screenwidth()
            screenHeight = self.validationViewDownloader.winfo_screenheight()
            midWidth = (screenWidth - int(width)) // 2
            midHeight = (screenHeight - int(height)) // 2
            self.validationViewDownloader.geometry(f"{width}x{height}+{midWidth}+{midHeight}")
            
            # Label
            self.validationViewDownloaderLabel = ctk.CTkLabel(
                master=self.validationViewDownloader,
                text = "Pachetul de validare se descarca...",
                **self._template_processing(self.validatorView.template["PackageDownloader"]["DownloadLabel"])
            )
            self.validationViewDownloaderLabel.pack(anchor="center", pady=(5, 0))
            # Progress Bar
            self.validationViewDownloaderProgress = ctk.CTkProgressBar(
                master=self.validationViewDownloader,
                **self._template_processing(self.validatorView.template["PackageDownloader"]["DownloadProgress"])
            )
            self.validationViewDownloaderProgress.pack(anchor="center", pady=(5, 0))
            # Label
            self.validationViewDownloaderLabelDone = ctk.CTkLabel(
                master=self.validationViewDownloader,
                text = "",
                **self._template_processing(self.validatorView.template["PackageDownloader"]["DownloadLabelDone"])
            )
            self.validationViewDownloaderLabelDone.pack(anchor="center", pady=(5, 0))
            # Start the download in a separate thread
            thread = threading.Thread(
                target=self._validation_package_download_worker,
                args=(production, package_url, self._logo_photo),
                daemon=False  # So it closes if the main app closes
            )
            thread.start()
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._start_validation_package_download.__name__}] "
                f"Failed to start the validation download toplevel. \n{traceBack}")
    
    def _validation_package_download_worker(self, production: bool, package_url: str, logo_image) -> None:
        try:
            for progress in self.validationPackageService.download_package(production, package_url):
                # Schedule update on the main thread
                self.validationViewDownloader.after(
                    0, self._update_download_progress, progress)
                self.validationViewDownloader.wm_iconphoto(False, logo_image)

            # When done, update UI on main thread
            self.validationViewDownloader.after(10, self._on_download_complete, logo_image)
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._validation_package_download_worker.__name__}] "
                f"Failed to start the validation package worker. \n{traceBack}")
    
    def _update_download_progress(self, progress: float) -> None:
        """Update progress bar (called on main thread)"""
        try:
            self.validationViewDownloaderProgress.set(progress)
            
        except Exception:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._update_download_progress.__name__}] "
                f"Failed to update the package download progressbar. \n{traceBack}")
    
    def _on_download_complete(self, logo_image) -> None:
        """Handle download completion (called on main thread)"""
        try:
            self.validationViewDownloaderLabel.configure(text="Pachetul de validare a fost descarcat!")
            self.validationViewDownloaderLabelDone.configure(text="(Fereastra se inchide automat)")
            self.validationViewDownloaderProgress.set(1.0)
            self.validationViewDownloaderProgress.configure(
                fg_color = self.validatorView.template["PackageDownloader"]["DownloadProgress"]["progress_color"]
            )
            self.validationViewDownloader.wm_iconphoto(False, logo_image)
            self.validatorView.after(50, self._reload_validation_packages)
            self.validationViewDownloader.wm_iconphoto(False, logo_image)
            self.validatorView.after(1500, self.validationViewDownloader.destroy)
        except Exception:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._on_download_complete.__name__}] "
                f"Failed to update the package download toplevel UI. \n{traceBack}")
    
    
    def _download_package_template(self, template_url: str) -> None:
        try:
            file_data = self.validationPackageService.download_template(template_url)

            if file_data is None:
                return

            save_path = filedialog.asksaveasfilename(
                defaultextension=".gpkg",
                filetypes=[("GeoPackage files", "*.gpkg"), ("All files", "*.*")],
                title="Save Template"
            )

            if not save_path:
                return  # user cancelled

            with open(save_path, "wb") as f:
                f.write(file_data)
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._on_download_complete.__name__}] "
                f"Failed to update the package download toplevel UI. \n{traceBack}")
    # ================================================
    
    # ========== Kit Tab Functionality ==========
    def _load_more_validation_kits(self) -> None:
        try:
            # Determine which package list to use
            kitList = (
                self.validationKitService._validationKitsPublic
                if self.ProductionStatus else
                self.validationKitService._validationKitsDevelopment
            )

            if self.KitsTabKitsLoading:
                return

            label = getattr(self.validatorView.KitsTabMainFrame, "noKitsLabel", None)
            if not kitList:
                if not label:
                    self.validatorView.KitsTabMainFrame.noKitsLabel = ctk.CTkLabel(
                        master=self.validatorView.KitsTabMainFrame,
                        **self._template_processing(
                            self.validatorView.template["KitsTab"]["Kits"]["NoKits"]
                        )
                    )
                    label = self.validatorView.KitsTabMainFrame.noKitsLabel
                if not label.winfo_ismapped():
                    label.place(anchor="center", relx=0.5, rely=0.5)
                return  # Nothing else to load

            if label and label.winfo_ismapped():
                label.place_forget()

            self.KitsTabKitsLoading = True

            total = len(kitList)
            startIndex = max(0, total - self.KitsTabKitsLoadedCount - self.KitsTabKitsBatchSize)
            endIndex = total - self.KitsTabKitsLoadedCount

            batch = kitList[startIndex:endIndex]
            for kit in batch:
                self._place_validation_kits(kit = kit)

            self.KitsTabKitsLoadedCount += len(batch)
            self.KitsTabKitsLoading = False
            self.validatorView.KitsTabScrollframe.update_idletasks()
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._load_more_validation_kits.__name__}] "
                f"Failed to load the validation kits. \n{traceBack}")
            
    def _place_validation_kits(self, kit: dict[str, str]) -> None:
        try:
            # ===== Frame =====
            kitFrame = ctk.CTkFrame(
                master = self.validatorView.KitsTabScrollframe,
                **self.validatorView.template["KitsTab"]["Kits"]["KitFrame"])
            # =================
            # ===== Label =====
            nameLabel = ctk.CTkLabel(
                master = kitFrame,
                text = f"{kit['Title']}",
                **self._template_processing(self.validatorView.template["KitsTab"]["Kits"]["KitTitle"]))
            dateLabel = ctk.CTkLabel(
                master=kitFrame,
                text = f"Lansat la: "
                    f"{kit['ReleaseDate']}",
                **self._template_processing(self.validatorView.template["KitsTab"]["Kits"]["KitLabel"]))
            # =================
            # ===== Button =====
            if kit["Version"] == self.validatorView.kitVersion:
                downloadButton = ctk.CTkButton(
                    master = kitFrame,
                    **self._template_processing(self.validatorView.template["KitsTab"]["Kits"]["InstalledButton"]),
                    state = "disabled")
            elif kit["Downloadable"] and kit["DownloadLink"] is not None:
                downloadButton = ctk.CTkButton(
                    master = kitFrame,
                    **self._template_processing(self.validatorView.template["KitsTab"]["Kits"]["DownloadButton"]),
                    command = lambda kit_url = kit["DownloadLink"]: self._start_validation_kit_download(kit_url = kit_url))
            elif not self.validationInProgress:
                downloadButton = ctk.CTkButton(
                    master = kitFrame,
                    **self._template_processing(self.validatorView.template["KitsTab"]["Kits"]["DownloadButton"]),
                    state = "disabled")
            else:
                downloadButton = ctk.CTkButton(
                    master = kitFrame,
                    **self._template_processing(self.validatorView.template["KitsTab"]["Kits"]["DownloadButton"]),
                    state = "disabled")
            # ==================
            # ===== Widget Placement =====
            kitFrame.pack(anchor = "center", padx = (0, 0), pady = (5, 0))
            kitFrame.pack_propagate(False)
            nameLabel.pack(anchor = "w", padx = (10, 5), pady = (10, 0))
            dateLabel.pack(anchor = "w", padx = (10, 5), pady = (0, 0))
            downloadButton.place(relx = 0.8575, rely = 0.55)
            # ============================
            self.KitsTabKitsFramesLoaded.append(kitFrame)
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._place_validation_kits.__name__}] "
                f"Failed to place the validation kits. \n{traceBack}")
    
    def _monitor_scroll_kits(self) -> None:
        try:
            canvas = self.validatorView.KitsTabScrollframe._parent_canvas
            start, end = canvas.yview()
            
            if self.ProductionStatus == True:
                if end >= 0.9:
                    if not self.KitsTabKitsLoading:
                        if self.KitsTabKitsLoadedCount < len(self.validationKitService._validationKitsPublic):
                            self._load_more_validation_kits()
            else:
                if end >= 0.9:
                    if not self.KitsTabKitsLoading:
                        if self.KitsTabKitsLoadedCount < len(self.validationKitService._validationKitsDevelopment):
                            self._load_more_validation_kits()

            # Check again after 100ms
            self.validatorView.after(100, self._monitor_scroll_kits)
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._monitor_scroll_kits.__name__}] "
                f"Failed to check the scroll level for the kits. \n{traceBack}")
    
    def _reload_validation_kits(self) -> None:
        try:
            # Reset state FIRST
            self.KitsTabKitsLoadedCount = 0
            self.KitsTabKitsLoading = False

            # Destroy widgets safely after idle
            for f in self.KitsTabKitsFramesLoaded:
                f.after(0, f.destroy)

            self.KitsTabKitsFramesLoaded.clear()

            # Load again AFTER UI stabilizes
            self.validationKitService.get_all_validation_kits(self.ProductionStatus)
            self.validatorView.after(50, self._load_more_validation_kits)
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._reload_validation_kits.__name__}] "
                f"Failed to reload the validation kits. \n{traceBack}")
            
    def _start_validation_kit_download(self, kit_url: str) -> None:
        try:
            self.requestUpdateCallback(kit_url = kit_url)
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._start_validation_kit_download.__name__}] "
                f"Failed to download the validation kit. \n{traceBack}")
    # ===========================================
    
    # ========== Messageboxes ==========
    def handle_info(self, message: str, title: str = "Info"):
        """Pop-up for info."""
        try:
            messagebox.showinfo(title = title, message = message)
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self.handle_error.__name__}] "
                f"Failed to show info message. \n{traceBack}")
            
    def handle_warning(self, message: str, title: str = "Warning"):
        """Pop-up for warning."""
        try:
            messagebox.showwarning(title = title, message = message)
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self.handle_error.__name__}] "
                f"Failed to show warning. \n{traceBack}")
    
    def handle_error(self, message: str, title: str = "Error"):
        """Pop-up for error."""
        try:
            messagebox.showerror(title = title, message = message)
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self.handle_error.__name__}] "
                f"Failed to show error. \n{traceBack}")
    
    def handle_critical_error(self, message: str, title: str = "Critical Error"):
        """Pop-up for critical error."""
        try:
            messagebox.showerror(title = title, message = message)
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self.handle_error.__name__}] "
                f"Failed to handle encountered critical error. \n{traceBack}")
    # ==================================
    
    # ========== Helpers ==========
    def _template_processing(self, template: dict[str, str]) -> dict[str, str]:
        """Process template to fix certain aspects."""
        try:
            templateFix = template.copy()
            if "font" in templateFix and isinstance(templateFix["font"], list):
                templateFix["font"] = tuple(templateFix["font"])
            if "dropdown_font" in templateFix and isinstance(templateFix["dropdown_font"], list):
                templateFix["dropdown_font"] = tuple(templateFix["dropdown_font"])
                
            return templateFix
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self._template_processing.__name__}] "
                f"Failed to process template. \n{traceBack}")
    # ==============================
# =========================================================