import threading
import requests
import json
import webbrowser
import tkinter as tk
from tkinter import ttk, messagebox
from packaging import version
import time

class VersionChecker:
    def __init__(self, app, version):
        self.app = app
        self.current_version = version
        self.version_url = "https://www.scorchsoft.com/public/blog/text-to-mic/leatest-version.json"
        self.notification_visible = False
        self.notification_window = None
        
    def check_version(self, show_result=True):
        """
        Check if a new version is available.
        If show_result is True, show a message even if no new version is found.
        """
        thread = threading.Thread(target=self._check_version_thread, args=(show_result,))
        thread.daemon = True  # Make thread terminate when main program exits
        thread.start()
        
    def _check_version_thread(self, show_result):
        """Run the version check in a background thread to avoid blocking the UI"""
        try:
            # Add a small timeout to prevent hanging
            response = requests.get(self.version_url, timeout=5)
            if response.status_code == 200:
                data = response.json()
                latest_version = data.get("latestVersion")
                download_url = data.get("downloadUrl")
                message = data.get("notificationMessage")
                
                # Ensure these values are present
                if not latest_version or not download_url:
                    if show_result:
                        self.app.after(0, lambda: messagebox.showwarning(
                            "Version Check Failed", 
                            "The update information is incomplete. Please try again later."
                        ))
                    return
                
                # Use packaging.version for proper version comparison
                try:
                    if version.parse(latest_version) > version.parse(self.current_version):
                        # New version available - show notification in UI thread
                        self.app.after(0, lambda: self.show_update_notification(latest_version, download_url, message))
                    elif show_result:
                        # No new version, but user requested check
                        self.app.after(0, lambda: messagebox.showinfo(
                            "Version Check", 
                            f"You have the latest version ({self.current_version})."
                        ))
                except (version.InvalidVersion, TypeError) as e:
                    if show_result:
                        self.app.after(0, lambda: messagebox.showwarning(
                            "Version Check Failed", 
                            f"Could not compare versions: {str(e)}"
                        ))
            else:
                if show_result:
                    self.app.after(0, lambda: messagebox.showwarning(
                        "Version Check Failed", 
                        f"Could not check for updates. Server returned status code: {response.status_code}"
                    ))
        except requests.RequestException as e:
            error_message = f"Could not connect to update server: {str(e)}"
            print(f"Version check error: {error_message}")
            if show_result:
                self.app.after(0, lambda msg=error_message: messagebox.showwarning(
                    "Version Check Failed", 
                    msg
                ))
        except json.JSONDecodeError as e:
            error_message = "Invalid update information received."
            print(f"Version check error: {error_message}")
            if show_result:
                self.app.after(0, lambda msg=error_message: messagebox.showwarning(
                    "Version Check Failed", 
                    msg
                ))
        except Exception as e:
            error_message = f"Could not check for updates: {str(e)}"
            print(f"Version check error: {error_message}")
            if show_result:
                self.app.after(0, lambda msg=error_message: messagebox.showwarning(
                    "Version Check Failed", 
                    msg
                ))
    
    def show_update_notification(self, latest_version, download_url, message):
        """Display an update notification banner as an overlay"""
        if self.notification_visible:
            return  # Already showing notification
        
        # Create a new toplevel window for the notification
        self.notification_window = tk.Toplevel(self.app)
        self.notification_window.overrideredirect(True)  # Remove window decorations
        self.notification_window.attributes('-topmost', True)  # Keep on top
        
        # Calculate position (aligned with top of main window)
        app_x = self.app.winfo_rootx()
        app_y = self.app.winfo_rooty()
        app_width = self.app.winfo_width()
        
        # Configure the notification window
        bg_color = '#fff3cd'  # Light yellow background
        fg_color = '#856404'  # Darker yellow/brown text
        
        # Create the main frame in the notification window
        self.notification_frame = ttk.Frame(self.notification_window, style='Notification.TFrame')
        self.notification_frame.pack(fill='both', expand=True)
        
        # Configure styles
        self.app.style.configure('Notification.TFrame', background=bg_color)
        self.app.style.configure('Notification.TLabel', background=bg_color, foreground=fg_color)
        self.app.style.map('Notification.TButton',
                          background=[('active', bg_color), ('!active', bg_color)],
                          foreground=[('active', fg_color), ('!active', fg_color)])
        
        # Create notification content
        notification_text = message or f"A new version ({latest_version}) is available. You're currently using version {self.current_version}."
        
        label = ttk.Label(
            self.notification_frame, 
            text=notification_text,
            style='Notification.TLabel',
            wraplength=app_width - 150  # Allow for button width
        )
        label.grid(row=0, column=0, padx=(10, 5), pady=10, sticky="w")
        
        # Create buttons
        button_frame = ttk.Frame(self.notification_frame, style='Notification.TFrame')
        button_frame.grid(row=0, column=1, padx=5, pady=5)
        
        download_button = ttk.Button(
            button_frame,
            text="Download",
            style='Notification.TButton',
            command=lambda: self.open_download_page(download_url)
        )
        download_button.pack(side='left', padx=5)
        
        close_button = ttk.Button(
            button_frame,
            text="×",
            width=2,
            style='Notification.TButton',
            command=self.dismiss_notification
        )
        close_button.pack(side='left')
        
        # Position the window and set its size
        self.notification_window.update_idletasks()  # Update to get correct dimensions
        notification_height = self.notification_window.winfo_reqheight()
        
        # Mark notification as visible first so _reposition_notification will work
        self.notification_visible = True
        
        # Setup event binding to follow main window if it's moved
        self.app.bind("<Configure>", self._reposition_notification)
        
        # Add bindings for window minimize/restore events
        self.app.bind("<Unmap>", self._handle_window_unmap)
        self.app.bind("<Map>", self._handle_window_map)
        
        # Use the reposition function to set initial position and size
        # This ensures consistent sizing between initial load and repositioning
        self._reposition_notification()
        
    def _handle_window_unmap(self, event=None):
        """Hide the notification when main window is minimized"""
        if self.notification_visible and self.notification_window:
            self.notification_window.withdraw()
            
    def _handle_window_map(self, event=None):
        """Show the notification when main window is restored"""
        if self.notification_visible and self.notification_window:
            self.notification_window.deiconify()
            # Reposition after showing
            self._reposition_notification()
            
    def _reposition_notification(self, event=None):
        """Reposition the notification window to stay at the top of the main window"""
        if self.notification_visible and self.notification_window:
            app_x = self.app.winfo_rootx()
            app_y = self.app.winfo_rooty()
            app_width = self.app.winfo_width()
            
            # Subtract a small amount to ensure it doesn't extend beyond the window
            adjusted_width = app_width - 5  # Adjust by 4 pixels to account for borders
            
            # Update the width and position
            notification_height = self.notification_window.winfo_height()
            self.notification_window.geometry(f"{adjusted_width}x{notification_height}+{app_x}+{app_y}")
        
    def dismiss_notification(self):
        """Remove the notification banner"""
        if self.notification_window:
            # Unbind all the events first
            self.app.unbind("<Configure>")
            self.app.unbind("<Unmap>")
            self.app.unbind("<Map>")
            
            # Destroy the window
            self.notification_window.destroy()
            self.notification_window = None
            
            self.notification_visible = False
            
    def open_download_page(self, url):
        """Open the download URL in a web browser"""
        webbrowser.open(url)
        self.dismiss_notification() 