#!/usr/bin/env python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib, Gio, Gdk
import os
import datetime
import sqlite3
from pathlib import Path

class JotApp(Gtk.Application):
    def __init__(self):
        super().__init__(application_id="com.example.jot",
                         flags=Gio.ApplicationFlags.FLAGS_NONE)
        
        # Set up data structures
        self.dbPath = self.get_db_path()
        self.conn = None
        self.init_database()
        
        # Initialize settings
        self.expirationDays = self.get_setting("expirationDays", 0)
        self.confirmDelete = self.get_setting("confirmDelete", 1)
    
    def do_activate(self):
        # Create main window when app is activated
        self.window = Gtk.ApplicationWindow(application=self, title="I38 Notes")
        self.window.set_default_size(500, 400)
        
        # Connect the delete-event signal (for window close button)
        self.window.connect("delete-event", self.on_window_close)
        
        # Set up keyboard shortcuts
        self.setup_actions()
        
        # Build the main interface
        self.build_ui()
        
        # Check for expired notes
        self.check_expirations()
        
        self.window.show_all()
        
    def on_window_close(self, window, event):
        """Handle window close event"""
        # Close the database connection before quitting
        if self.conn:
            self.conn.close()
        self.quit()
        return True
    
    def get_db_path(self):
        """Get path to the SQLite database"""
        configHome = os.environ.get('XDG_CONFIG_HOME', os.path.join(os.path.expanduser('~'), '.config'))
        configDir = os.path.join(configHome, 'stormux', 'I38')
        os.makedirs(configDir, exist_ok=True)
        return os.path.join(configDir, 'notes.sqlite')
    
    def init_database(self):
        """Initialize the SQLite database"""
        try:
            self.conn = sqlite3.connect(self.dbPath)
            cursor = self.conn.cursor()
            
            # Create notes table if it doesn't exist
            cursor.execute('''
                CREATE TABLE IF NOT EXISTS notes (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    text TEXT NOT NULL,
                    created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                    expires TIMESTAMP NULL,
                    locked BOOLEAN DEFAULT 0
                )
            ''')
            
            # Create settings table if it doesn't exist
            cursor.execute('''
                CREATE TABLE IF NOT EXISTS settings (
                    key TEXT PRIMARY KEY,
                    value TEXT NOT NULL
                )
            ''')
            
            self.conn.commit()
            
            # Initialize default settings if they don't exist
            self.init_default_settings()
            
            # Check if we need to migrate from old format
            self.migrate_if_needed()
            
        except sqlite3.Error as e:
            print(f"Database error: {e}")
    
    def init_default_settings(self):
        """Initialize default settings if they don't exist"""
        defaultSettings = {
            "expirationDays": "0",
            "confirmDelete": "1"  # 1 = enabled, 0 = disabled
        }
        
        cursor = self.conn.cursor()
        for key, value in defaultSettings.items():
            cursor.execute(
                "INSERT OR IGNORE INTO settings (key, value) VALUES (?, ?)",
                (key, value)
            )
        self.conn.commit()
    
    def migrate_if_needed(self):
        """Check if we need to migrate from old format"""
        # Check for old config directory
        oldConfigHome = os.environ.get('XDG_CONFIG_HOME', os.path.join(os.path.expanduser('~'), '.config'))
        oldConfigDir = os.path.join(oldConfigHome, 'jot')
        oldNotesFile = os.path.join(oldConfigDir, 'notes')
        
        if os.path.exists(oldNotesFile):
            try:
                # Check if we already have notes in the database
                cursor = self.conn.cursor()
                cursor.execute("SELECT COUNT(*) FROM notes")
                count = cursor.fetchone()[0]
                
                # Only migrate if database is empty
                if count == 0:
                    with open(oldNotesFile, 'r') as f:
                        for line in f:
                            parts = line.strip().split(': ', 1)
                            if len(parts) == 2:
                                noteNum, noteText = parts
                                cursor.execute(
                                    "INSERT INTO notes (text, locked) VALUES (?, ?)",
                                    (noteText, 0)
                                )
                    
                    self.conn.commit()
                    print(f"Migrated notes from {oldNotesFile}")
            except Exception as e:
                print(f"Migration error: {e}")
    
    def get_setting(self, key, default=None):
        """Get a setting from the database"""
        try:
            cursor = self.conn.cursor()
            cursor.execute("SELECT value FROM settings WHERE key = ?", (key,))
            result = cursor.fetchone()
            
            if result:
                return result[0]
            else:
                # Set default if not exists
                self.save_setting(key, default)
                return default
        except sqlite3.Error:
            return default
    
    def save_setting(self, key, value):
        """Save a setting to the database"""
        try:
            cursor = self.conn.cursor()
            cursor.execute(
                "INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)",
                (key, value)
            )
            self.conn.commit()
        except sqlite3.Error as e:
            print(f"Settings error: {e}")
    
    def check_expirations(self):
        """Check and remove expired notes"""
        try:
            cursor = self.conn.cursor()
            now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            
            # Find expired notes that aren't locked
            cursor.execute(
                "SELECT id, text FROM notes WHERE expires IS NOT NULL AND expires < ? AND locked = 0",
                (now,)
            )
            expired = cursor.fetchall()
            
            if expired:
                # Delete expired notes
                cursor.execute(
                    "DELETE FROM notes WHERE expires IS NOT NULL AND expires < ? AND locked = 0",
                    (now,)
                )
                self.conn.commit()
                
                expiredCount = len(expired)
                if expiredCount > 0:
                    self.show_status_message(f"Removed {expiredCount} expired notes")
                    # Refresh notes list
                    self.populate_notes()
        except sqlite3.Error as e:
            print(f"Expiration check error: {e}")
    
    def build_ui(self):
        """Build the main user interface with tabs"""
        # Create notebook (tabbed interface)
        self.notebook = Gtk.Notebook()
        self.notebook.set_tab_pos(Gtk.PositionType.TOP)
        
        # Make tabs keyboard navigable
        self.notebook.set_can_focus(True)
        
        self.window.add(self.notebook)
        
        # Build notes tab
        notesTab = self.build_notes_tab()
        notesTabLabel = Gtk.Label(label="Notes")
        self.notebook.append_page(notesTab, notesTabLabel)
        self.notebook.set_tab_reorderable(notesTab, False)
        
        # Build settings tab
        settingsTab = self.build_settings_tab()
        settingsTabLabel = Gtk.Label(label="Settings")
        self.notebook.append_page(settingsTab, settingsTabLabel)
        self.notebook.set_tab_reorderable(settingsTab, False)
        
        # Connect tab change signal
        self.notebook.connect("switch-page", self.on_tab_switched)
    
    def build_notes_tab(self):
        """Build the notes tab"""
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
        vbox.set_border_width(10)
        
        # Notes list with scrolling
        scrolled = Gtk.ScrolledWindow()
        scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
        
        # Create list store and view
        self.notesStore = Gtk.ListStore(int, str, bool, str)  # id, text, locked, expiry
        self.notesView = Gtk.TreeView(model=self.notesStore)
        self.notesView.set_activate_on_single_click(False)
        self.notesView.connect("row-activated", self.on_row_activated)
        
        # Improve keyboard navigation in the tree view
        self.notesView.set_can_focus(True)
        self.notesView.set_headers_clickable(True)
        self.notesView.set_enable_search(True)
        self.notesView.set_search_column(1)  # Search by note text
        
        # Add columns with renderers
        self.add_columns()
        
        # Populate the list
        self.populate_notes()
        
        scrolled.add(self.notesView)
        vbox.pack_start(scrolled, True, True, 0)
        
        # Action buttons
        actionBox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
        
        # Copy button
        copyButton = Gtk.Button.new_with_label("Copy to Clipboard")
        copyButton.connect("clicked", self.on_copy_clicked)
        copyButton.set_can_focus(True)
        actionBox.pack_start(copyButton, False, False, 0)
        
        # Delete button
        deleteButton = Gtk.Button.new_with_label("Delete Note")
        deleteButton.connect("clicked", self.on_delete_button_clicked)
        deleteButton.set_can_focus(True)
        actionBox.pack_start(deleteButton, False, False, 0)
        
        vbox.pack_start(actionBox, False, False, 0)
        
        # Entry for adding new notes
        entryBox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
        self.newNoteEntry = Gtk.Entry()
        self.newNoteEntry.set_placeholder_text("Type a new note and press Enter")
        self.newNoteEntry.connect("activate", self.on_entry_activate)
        self.newNoteEntry.set_can_focus(True)
        entryBox.pack_start(self.newNoteEntry, True, True, 0)
        
        # Add button
        addButton = Gtk.Button.new_with_label("Add Note")
        addButton.connect("clicked", self.on_add_clicked)
        addButton.set_can_focus(True)
        entryBox.pack_start(addButton, False, False, 0)
        
        vbox.pack_start(entryBox, False, False, 0)
        
        # Status bar
        self.statusbar = Gtk.Statusbar()
        self.statusbarCtx = self.statusbar.get_context_id("jot")
        vbox.pack_start(self.statusbar, False, False, 0)
        
        return vbox

    def build_settings_tab(self):
        """Build the settings tab"""
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
        vbox.set_border_width(20)
        
        # Create a frame for expiration settings
        expiryFrame = Gtk.Frame(label="Note Expiration")
        vbox.pack_start(expiryFrame, False, False, 0)
        
        # Container for frame content
        expiryBox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
        expiryBox.set_border_width(10)
        expiryFrame.add(expiryBox)
        
        # Note expiration setting
        # First radio button for "Never expire"
        self.neverExpireRadio = Gtk.RadioButton.new_with_label_from_widget(None, "Never expire notes")
        self.neverExpireRadio.set_can_focus(True)
        expiryBox.pack_start(self.neverExpireRadio, False, False, 0)
        
        # Container for expiration days selection
        expireDaysBox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
        
        # Radio button for custom expiration
        self.customExpireRadio = Gtk.RadioButton.new_with_label_from_widget(
            self.neverExpireRadio, 
            "Expire notes after"
        )
        self.customExpireRadio.set_can_focus(True)
        expireDaysBox.pack_start(self.customExpireRadio, False, False, 0)
        
        # Spin button for days
        adjustment = Gtk.Adjustment(
            value=max(1, int(self.expirationDays)) if int(self.expirationDays) > 0 else 7,
            lower=1,
            upper=30,
            step_increment=1
        )
        self.daysSpinButton = Gtk.SpinButton()
        self.daysSpinButton.set_adjustment(adjustment)
        self.daysSpinButton.set_can_focus(True)
        expireDaysBox.pack_start(self.daysSpinButton, False, False, 0)
        
        # Label for "days"
        daysLabel = Gtk.Label(label="days")
        expireDaysBox.pack_start(daysLabel, False, False, 0)
        
        expiryBox.pack_start(expireDaysBox, False, False, 0)
        
        # Create a frame for confirmation settings
        confirmFrame = Gtk.Frame(label="Confirmations")
        vbox.pack_start(confirmFrame, False, False, 10)
        
        # Container for confirmation settings
        confirmBox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
        confirmBox.set_border_width(10)
        confirmFrame.add(confirmBox)
        
        # Delete confirmation checkbox
        self.confirmDeleteCheck = Gtk.CheckButton(label="Confirm before deleting notes")
        self.confirmDeleteCheck.set_active(bool(int(self.confirmDelete)))
        self.confirmDeleteCheck.set_can_focus(True)
        confirmBox.pack_start(self.confirmDeleteCheck, False, False, 0)
        
        # Set the active radio button based on current setting
        if int(self.expirationDays) > 0:
            self.customExpireRadio.set_active(True)
        else:
            self.neverExpireRadio.set_active(True)
        
        # Connect signals
        self.neverExpireRadio.connect("toggled", self.on_expiry_radio_toggled)
        self.customExpireRadio.connect("toggled", self.on_expiry_radio_toggled)
        
        # Enable/disable the spin button based on the selected radio
        self.on_expiry_radio_toggled(None)
        
        # Save button
        saveButton = Gtk.Button.new_with_label("Save Settings")
        saveButton.connect("clicked", self.on_save_settings)
        saveButton.set_can_focus(True)
        vbox.pack_start(saveButton, False, False, 10)
        
        return vbox
    
    def add_columns(self):
        """Add columns to the TreeView"""
        # ID Column
        renderer = Gtk.CellRendererText()
        column = Gtk.TreeViewColumn("ID", renderer, text=0)
        column.set_sort_column_id(0)
        self.notesView.append_column(column)
        
        # Note Text Column
        renderer = Gtk.CellRendererText()
        renderer.set_property("ellipsize", True)
        column = Gtk.TreeViewColumn("Note", renderer, text=1)
        column.set_expand(True)
        self.notesView.append_column(column)
        
        # Locked Column
        renderer = Gtk.CellRendererToggle()
        renderer.connect("toggled", self.on_locked_toggled)
        column = Gtk.TreeViewColumn("Locked", renderer, active=2)
        self.notesView.append_column(column)
        
        # Expiration Column
        renderer = Gtk.CellRendererText()
        column = Gtk.TreeViewColumn("Expires", renderer, text=3)
        self.notesView.append_column(column)
    
    def populate_notes(self):
        """Populate the list store with notes from the database"""
        self.notesStore.clear()
        
        try:
            cursor = self.conn.cursor()
            cursor.execute(
                "SELECT id, text, locked, expires FROM notes ORDER BY id"
            )
            notes = cursor.fetchall()
            
            for note in notes:
                noteId, text, locked, expires = note
                
                # Format expiry date if it exists
                expiryText = ""
                if expires:
                    try:
                        expiryDate = datetime.datetime.strptime(expires, "%Y-%m-%d %H:%M:%S")
                        expiryText = expiryDate.strftime("%Y-%m-%d")
                    except:
                        expiryText = "Invalid date"
                
                self.notesStore.append([
                    noteId,        # Database ID
                    text,          # Note text
                    bool(locked),  # Locked status
                    expiryText     # Expiry date
                ])
        except sqlite3.Error as e:
            print(f"Error populating notes: {e}")
    
    def setup_actions(self):
        """Set up keyboard shortcuts"""
        # Delete selected note
        deleteAction = Gio.SimpleAction.new("delete", None)
        deleteAction.connect("activate", self.on_delete_clicked)
        self.add_action(deleteAction)
        self.set_accels_for_action("app.delete", ["Delete"])
        
        # Toggle lock on selected note
        lockAction = Gio.SimpleAction.new("lock", None)
        lockAction.connect("activate", self.on_lock_toggled)
        self.add_action(lockAction)
        self.set_accels_for_action("app.lock", ["l"])
        
        # Copy note to clipboard
        copyAction = Gio.SimpleAction.new("copy", None)
        copyAction.connect("activate", self.on_copy_clicked)
        self.add_action(copyAction)
        self.set_accels_for_action("app.copy", ["<Control>c"])
        
        # Switch to notes tab
        notesTabAction = Gio.SimpleAction.new("notes_tab", None)
        notesTabAction.connect("activate", lambda a, p: self.notebook.set_current_page(0))
        self.add_action(notesTabAction)
        self.set_accels_for_action("app.notes_tab", ["<Alt>1"])
        
        # Switch to settings tab
        settingsTabAction = Gio.SimpleAction.new("settings_tab", None)
        settingsTabAction.connect("activate", lambda a, p: self.notebook.set_current_page(1))
        self.add_action(settingsTabAction)
        self.set_accels_for_action("app.settings_tab", ["<Alt>2"])
        
        # Quit application
        quitAction = Gio.SimpleAction.new("quit", None)
        quitAction.connect("activate", lambda a, p: self.quit())
        self.add_action(quitAction)
        self.set_accels_for_action("app.quit", ["Escape"])
    
    def show_status_message(self, message):
        """Show a message in the statusbar"""
        self.statusbar.push(self.statusbarCtx, message)
        # Auto-remove after 5 seconds
        GLib.timeout_add_seconds(5, self.statusbar.pop, self.statusbarCtx)
    
    def on_tab_switched(self, notebook, page, page_num):
        """Handler for tab switching"""
        # Reset status bar on tab switch
        self.statusbar.pop(self.statusbarCtx)
        
        # Set focus appropriately
        if page_num == 0:  # Notes tab
            self.newNoteEntry.grab_focus()
        elif page_num == 1:  # Settings tab
            if self.neverExpireRadio.get_active():
                self.neverExpireRadio.grab_focus()
            else:
                self.customExpireRadio.grab_focus()
    
    def on_expiry_radio_toggled(self, widget):
        """Handler for expiry radio button toggles"""
        # Enable/disable spin button based on which radio is active
        self.daysSpinButton.set_sensitive(self.customExpireRadio.get_active())
    
    def on_save_settings(self, button):
        """Handler for Save Settings button"""
        if self.neverExpireRadio.get_active():
            self.expirationDays = 0
        else:
            self.expirationDays = self.daysSpinButton.get_value_as_int()
        
        # Get delete confirmation setting
        self.confirmDelete = 1 if self.confirmDeleteCheck.get_active() else 0
        
        # Save to database
        self.save_setting("expirationDays", self.expirationDays)
        self.save_setting("confirmDelete", self.confirmDelete)
        
        # Apply expiration to notes if needed
        if self.expirationDays > 0:
            try:
                cursor = self.conn.cursor()
                now = datetime.datetime.now()
                expiryDate = now + datetime.timedelta(days=self.expirationDays)
                expiryStr = expiryDate.strftime("%Y-%m-%d %H:%M:%S")
                
                # Set expiration for notes that aren't locked and don't have expiration
                cursor.execute(
                    "UPDATE notes SET expires = ? WHERE locked = 0 AND expires IS NULL",
                    (expiryStr,)
                )
                self.conn.commit()
                
                # Refresh the notes list
                self.populate_notes()
            except sqlite3.Error as e:
                print(f"Error updating expirations: {e}")
        
        self.show_status_message("Settings saved")
        
        # Switch back to notes tab
        self.notebook.set_current_page(0)

    def on_entry_activate(self, entry):
        """Handle Enter key in the entry field"""
        self.add_new_note(entry.get_text())
        entry.set_text("")
    
    def on_add_clicked(self, button):
        """Handle Add Note button click"""
        self.add_new_note(self.newNoteEntry.get_text())
        self.newNoteEntry.set_text("")
    
    def add_new_note(self, text):
        """Add a new note to the database"""
        if not text.strip():
            self.show_status_message("Note text cannot be empty")
            return
        
        try:
            cursor = self.conn.cursor()
            
            # Set expiration if enabled
            expires = None
            if int(self.expirationDays) > 0:
                expiryDate = datetime.datetime.now() + datetime.timedelta(days=int(self.expirationDays))
                expires = expiryDate.strftime("%Y-%m-%d %H:%M:%S")
            
            # Insert the new note
            cursor.execute(
                "INSERT INTO notes (text, expires, locked) VALUES (?, ?, ?)",
                (text, expires, 0)
            )
            self.conn.commit()
            
            # Refresh the notes list
            self.populate_notes()
            self.show_status_message("Note added")
        except sqlite3.Error as e:
            self.show_status_message(f"Error adding note: {e}")
    
    def on_row_activated(self, view, path, column):
        """Handle double-click on a note - edit the note"""
        model = view.get_model()
        noteId = model[path][0]
        
        # Get the note from the database
        try:
            cursor = self.conn.cursor()
            cursor.execute("SELECT text, locked, expires FROM notes WHERE id = ?", (noteId,))
            note = cursor.fetchone()
            
            if note:
                self.edit_note_dialog(noteId, note)
        except sqlite3.Error as e:
            self.show_status_message(f"Error retrieving note: {e}")
    
    def on_locked_toggled(self, renderer, path):
        """Handle toggling the locked state from the view"""
        model = self.notesView.get_model()
        noteId = model[path][0]
        currentLocked = model[path][2]
        newLocked = not currentLocked
        
        try:
            cursor = self.conn.cursor()
            
            # Update locked status
            cursor.execute(
                "UPDATE notes SET locked = ? WHERE id = ?",
                (1 if newLocked else 0, noteId)
            )
            
            # If unlocking and expiration is enabled, set expiration
            if not newLocked and int(self.expirationDays) > 0:
                expiryDate = datetime.datetime.now() + datetime.timedelta(days=int(self.expirationDays))
                expiryStr = expiryDate.strftime("%Y-%m-%d %H:%M:%S")
                
                cursor.execute(
                    "UPDATE notes SET expires = ? WHERE id = ?",
                    (expiryStr, noteId)
                )
                
                # Update the expiry text in the model
                model[path][3] = expiryDate.strftime("%Y-%m-%d")
                
                self.show_status_message("Note unlocked - expiration set")
            elif newLocked:
                self.show_status_message("Note locked - will not expire")
            else:
                self.show_status_message("Note unlocked")
            
            # Update the model
            model[path][2] = newLocked
            
            self.conn.commit()
        except sqlite3.Error as e:
            self.show_status_message(f"Error updating note: {e}")
    
    def on_lock_toggled(self, action, parameter):
        """Handle keyboard shortcut to toggle lock"""
        selection = self.notesView.get_selection()
        model, treeiter = selection.get_selected()
        if treeiter:
            noteId = model[treeiter][0]
            currentLocked = model[treeiter][2]
            
            # Simulate clicking the toggle
            path = model.get_path(treeiter)
            self.on_locked_toggled(None, path)
    
    def on_copy_clicked(self, action=None, parameter=None):
        """Copy the selected note to clipboard"""
        selection = self.notesView.get_selection()
        model, treeiter = selection.get_selected()
        if treeiter:
            noteText = model[treeiter][1]
            
            # Get the clipboard
            clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
            clipboard.set_text(noteText, -1)
            
            self.show_status_message("Note copied to clipboard")
        else:
            self.show_status_message("No note selected")
    
    def on_delete_button_clicked(self, button):
        """Handle Delete button click"""
        selection = self.notesView.get_selection()
        model, treeiter = selection.get_selected()
        if treeiter:
            noteId = model[treeiter][0]
            noteText = model[treeiter][1]
            
            if int(self.confirmDelete):
                self.confirm_delete_note(noteId, noteText)
            else:
                self.delete_note(noteId)
    
    def on_delete_clicked(self, action, parameter):
        """Handle Delete key to remove a note"""
        selection = self.notesView.get_selection()
        model, treeiter = selection.get_selected()
        if treeiter:
            noteId = model[treeiter][0]
            noteText = model[treeiter][1]
            
            if int(self.confirmDelete):
                self.confirm_delete_note(noteId, noteText)
            else:
                self.delete_note(noteId)
    
    def delete_note(self, noteId):
        """Delete a note by ID without confirmation"""
        try:
            cursor = self.conn.cursor()
            cursor.execute("DELETE FROM notes WHERE id = ?", (noteId,))
            self.conn.commit()
            
            self.populate_notes()
            self.show_status_message("Note deleted")
        except sqlite3.Error as e:
            self.show_status_message(f"Error deleting note: {e}")
    
    def confirm_delete_note(self, noteId, noteText):
        """Show confirmation dialog before deleting a note"""
        if len(noteText) > 30:
            noteText = noteText[:30] + "..."
        
        dialog = Gtk.MessageDialog(
            transient_for=self.window,
            flags=0,
            message_type=Gtk.MessageType.QUESTION,
            buttons=Gtk.ButtonsType.YES_NO,
            text=f"Delete note: {noteText}?"
        )
        dialog.set_default_response(Gtk.ResponseType.NO)
        
        response = dialog.run()
        if response == Gtk.ResponseType.YES:
            self.delete_note(noteId)
        
        dialog.destroy()
    
    def edit_note_dialog(self, noteId, note):
        """Show dialog to edit a note"""
        text, locked, expires = note
        
        dialog = Gtk.Dialog(
            title="Edit Note",
            parent=self.window,
            flags=0,
            buttons=(
                Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
                Gtk.STOCK_SAVE, Gtk.ResponseType.OK
            )
        )
        dialog.set_default_size(400, 200)
        
        # Make the dialog accessible
        dialog.set_role("dialog")
        dialog.set_property("has-tooltip", True)
        
        # Create a text view for the note
        entryBox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
        entryBox.set_border_width(10)
        
        label = Gtk.Label(label="Edit note text:")
        label.set_halign(Gtk.Align.START)
        entryBox.pack_start(label, False, False, 0)
        
        # Scrolled window for text view
        scrolled = Gtk.ScrolledWindow()
        scrolled.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        scrolled.set_shadow_type(Gtk.ShadowType.IN)
        
        # Text view for multi-line editing
        textBuffer = Gtk.TextBuffer()
        textBuffer.set_text(text)
        textView = Gtk.TextView.new_with_buffer(textBuffer)
        textView.set_wrap_mode(Gtk.WrapMode.WORD)
        textView.set_can_focus(True)
        
        scrolled.add(textView)
        entryBox.pack_start(scrolled, True, True, 0)
        
        # Add lock checkbox
        lockCheck = Gtk.CheckButton(label="Lock note (prevent expiration)")
        lockCheck.set_active(bool(locked))
        lockCheck.set_can_focus(True)
        entryBox.pack_start(lockCheck, False, False, 0)
        
        # Show expiration date if it exists
        if expires and not locked:
            try:
                expiryDate = datetime.datetime.strptime(expires, "%Y-%m-%d %H:%M:%S")
                expiryLabel = Gtk.Label(label=f"Expires on: {expiryDate.strftime('%Y-%m-%d')}")
                expiryLabel.set_halign(Gtk.Align.START)
                entryBox.pack_start(expiryLabel, False, False, 0)
            except:
                pass
        
        dialog.get_content_area().add(entryBox)
        dialog.set_default_response(Gtk.ResponseType.OK)
        dialog.show_all()
        
        # Set focus to text view
        textView.grab_focus()
        
        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            # Get the text from the buffer
            start, end = textBuffer.get_bounds()
            newText = textBuffer.get_text(start, end, False)
            newLocked = lockCheck.get_active()
            
            try:
                cursor = self.conn.cursor()
                
                # Update the note
                cursor.execute(
                    "UPDATE notes SET text = ?, locked = ? WHERE id = ?",
                    (newText, 1 if newLocked else 0, noteId)
                )
                
                # Update expiration if needed
                if not newLocked and int(self.expirationDays) > 0:
                    expiryDate = datetime.datetime.now() + datetime.timedelta(days=int(self.expirationDays))
                    expiryStr = expiryDate.strftime("%Y-%m-%d %H:%M:%S")
                    
                    cursor.execute(
                        "UPDATE notes SET expires = ? WHERE id = ?",
                        (expiryStr, noteId)
                    )
                
                self.conn.commit()
                self.populate_notes()
                self.show_status_message("Note updated")
            except sqlite3.Error as e:
                self.show_status_message(f"Error updating note: {e}")
            
        dialog.destroy()

def main():
    app = JotApp()
    return app.run(None)

if __name__ == "__main__":
    main()