/**
 * Freelance Studio Admin JavaScript
 */

(function($) {
    'use strict';

    // Initialize when document is ready
    $(document).ready(function() {
        FS_Admin.init();
    });

    // Card dropdown menus (used on clients, projects card views)
    document.addEventListener('click', function(e) {
        var trigger = e.target.closest('.fs-card-menu-trigger');
        if (trigger) {
            e.stopPropagation();
            var menu = trigger.closest('.fs-card-actions-menu');
            var card = trigger.closest('.fs-project-card, .fs-client-card');
            var isOpen = menu.classList.contains('open');
            // Close all other open menus and reset z-index
            document.querySelectorAll('.fs-card-actions-menu.open').forEach(function(m) {
                m.classList.remove('open');
                var parentCard = m.closest('.fs-project-card, .fs-client-card');
                if (parentCard) parentCard.style.zIndex = '';
            });
            if (!isOpen) {
                menu.classList.add('open');
                if (card) card.style.zIndex = '10';
            }
            return;
        }
        // Click outside closes all menus
        document.querySelectorAll('.fs-card-actions-menu.open').forEach(function(m) {
            m.classList.remove('open');
            var parentCard = m.closest('.fs-project-card, .fs-client-card');
            if (parentCard) parentCard.style.zIndex = '';
        });
    });

    // Mobile Tab Dropdown - run immediately on ready (before any page-specific code)
    $(document).ready(function() {
        // Use matchMedia to detect mobile - works with DevTools responsive mode too
        if (!window.matchMedia('(max-width: 782px)').matches) {
            return;
        }
        
        var $navs = $('.fs-tab-nav');
        
        $navs.each(function() {
            var $nav = $(this);
            var $tabs = $nav.find('.fs-tab-link, .fs-tab');
            
            if ($tabs.length < 3) return;
            if ($nav.data('mobile-dropdown')) return;
            $nav.data('mobile-dropdown', true);
            
            var $active = $tabs.filter('.active');
            var activeLabel = $active.length ? $active.text().trim() : $tabs.first().text().trim();
            
            var $wrapper = $('<div class="fs-mobile-tab-dropdown"></div>');
            var $trigger = $('<button type="button" class="fs-mobile-tab-trigger">' + 
                '<span class="fs-mobile-tab-label">' + activeLabel + '</span>' +
                '<span class="fs-mobile-tab-arrow">▾</span></button>');
            var $menu = $('<div class="fs-mobile-tab-menu"></div>');
            
            $tabs.each(function() {
                var $tab = $(this);
                var $item = $('<a></a>')
                    .attr('href', $tab.attr('href'))
                    .text($tab.text().trim())
                    .addClass('fs-mobile-tab-option');
                if ($tab.hasClass('active')) {
                    $item.addClass('active');
                }
                $menu.append($item);
            });
            
            $wrapper.append($trigger).append($menu);
            $nav.before($wrapper);
            $nav.addClass('fs-tab-nav-hidden-mobile');
            
            $trigger.on('click', function(e) {
                e.stopPropagation();
                $menu.toggleClass('open');
            });
            
            $(document).on('click', function() {
                $menu.removeClass('open');
            });
        });
    });

    // Main Admin Object
    var FS_Admin = {
        
        init: function() {
            console.log('Freelance Studio Admin JS v3.2.17 loaded');
            this.initResponsiveTables();
            this.initDatepickers();
            this.initDeleteHandlers();
            this.initDynamicForms();
            this.initCalculations();
            this.initContactHandlers();
            this.initInvoiceStatusHandlers();
            this.initProposalForm();
            this.initSettingsSignaturePad();
            this.initSendEmailModal();
            this.initFilterToggle();
            this.initExpenseBillback();
            this.initKeyboardShortcuts();
            this.initBrowserNotifications();
            this.initQuickAddForm();
            this.initInvoiceProjectModal();
        },

        // Universal responsive tables: wrap tables in scroll containers on mobile
        initResponsiveTables: function() {
            if (!window.matchMedia('(max-width: 782px)').matches) {
                return;
            }
            
            // Wrap all tables inside .fs-wrap that aren't already wrapped
            $('.fs-wrap table').each(function() {
                var $table = $(this);
                
                // Skip if already wrapped
                if ($table.parent().hasClass('fs-table-responsive')) {
                    return;
                }
                
                // Skip tables inside modals (they have their own handling)
                if ($table.closest('.fs-modal').length) {
                    return;
                }
                
                $table.wrap('<div class="fs-table-responsive"></div>');
            });
            
            // Also watch for dynamically added tables (e.g. AJAX-loaded services)
            var observer = new MutationObserver(function(mutations) {
                mutations.forEach(function(mutation) {
                    $(mutation.addedNodes).find('table').addBack('table').each(function() {
                        var $table = $(this);
                        if (!$table.parent().hasClass('fs-table-responsive') && 
                            !$table.closest('.fs-modal').length &&
                            $table.closest('.fs-wrap').length) {
                            $table.wrap('<div class="fs-table-responsive"></div>');
                        }
                    });
                });
            });
            
            var wrap = document.querySelector('.fs-wrap');
            if (wrap) {
                observer.observe(wrap, { childList: true, subtree: true });
            }
        },

        // Initialize datepickers
        initDatepickers: function() {
            $('.fs-datepicker, input[type="date"]').each(function() {
                if ($(this).attr('type') !== 'date') {
                    $(this).datepicker({
                        dateFormat: 'yy-mm-dd',
                        changeMonth: true,
                        changeYear: true
                    });
                }
            });
        },

        // Initialize delete handlers
        initDeleteHandlers: function() {
            // Generic delete handler
            $(document).on('click', '.fs-delete-btn', function(e) {
                e.preventDefault();
                
                var $btn = $(this);
                var type = $btn.data('type');
                var id = $btn.data('id');
                var redirect = $btn.data('redirect');
                var confirmMsg = fs_ajax.strings.confirm_delete;
                
                if (!confirm(confirmMsg)) {
                    return;
                }
                
                $btn.prop('disabled', true).text(fs_ajax.strings.deleting);
                
                $.ajax({
                    url: fs_ajax.ajax_url,
                    type: 'POST',
                    data: {
                        action: 'fs_delete_' + type,
                        id: id,
                        nonce: fs_ajax.nonce
                    },
                    success: function(response) {
                        if (response.success) {
                            // If redirect URL provided, go there
                            if (redirect) {
                                window.location.href = redirect;
                                return;
                            }
                            
                            // Remove the row or reload
                            var $row = $btn.closest('tr');
                            if ($row.length) {
                                $row.fadeOut(300, function() {
                                    $(this).remove();
                                    // Show success notice
                                    var msg = response.data && response.data.message ? response.data.message : 'Deleted successfully.';
                                    var $notice = $('<div class="fs-notice success fs-ajax-notice">' + $('<span>').text(msg).html() + '</div>');
                                    var $wrap = $btn.closest('.fs-wrap');
                                    // Remove any existing ajax notices
                                    $wrap.find('.fs-ajax-notice').remove();
                                    // Insert after header
                                    var $header = $wrap.find('.fs-list-header, .fs-breadcrumb').first();
                                    if ($header.length) {
                                        $header.after($notice);
                                    } else {
                                        $wrap.prepend($notice);
                                    }
                                    // Auto-dismiss after 5 seconds
                                    setTimeout(function() { $notice.fadeOut(300, function() { $(this).remove(); }); }, 5000);
                                });
                            } else {
                                location.reload();
                            }
                        } else {
                            alert(response.data.message || fs_ajax.strings.error);
                            $btn.prop('disabled', false).text('Delete');
                        }
                    },
                    error: function() {
                        alert(fs_ajax.strings.error);
                        $btn.prop('disabled', false).text('Delete');
                    }
                });
            });
        },

        // Initialize dynamic form features
        initDynamicForms: function() {
            // Note: Contact add/remove handlers are in clients/form.php inline script
            // (they include auto-primary and primary reassignment logic)

            // Add invoice item button
            $(document).on('click', '.fs-add-item', function(e) {
                e.preventDefault();
                var template = $('#item-template').html();
                var index = $('.fs-invoice-item').length;
                template = template.replace(/\{index\}/g, index);
                $('.fs-invoice-items').append(template);
            });

            // Remove invoice item button
            $(document).on('click', '.fs-remove-item', function(e) {
                e.preventDefault();
                $(this).closest('.fs-invoice-item').remove();
                FS_Admin.calculateInvoiceTotals();
            });

            // Client dropdown change - load projects
            $(document).on('change', '#client_id', function() {
                var clientId = $(this).val();
                var $projectSelect = $('#project_id');
                
                if (!$projectSelect.length) return;
                
                $projectSelect.prop('disabled', true);
                
                $.ajax({
                    url: fs_ajax.ajax_url,
                    type: 'POST',
                    data: {
                        action: 'fs_get_client_projects',
                        client_id: clientId,
                        nonce: fs_ajax.nonce
                    },
                    success: function(response) {
                        $projectSelect.empty().append('<option value="">— Select Project —</option>');
                        
                        if (response.success && response.data.length) {
                            $.each(response.data, function(i, project) {
                                $projectSelect.append(
                                    $('<option></option>').val(project.id).text(project.name)
                                );
                            });
                        }
                        
                        $projectSelect.prop('disabled', false);
                    }
                });
            });

            // Billing type change
            $(document).on('change', '#billing_type', function() {
                var type = $(this).val();
                
                // Hide all conditional fields first
                $('.fs-conditional-fields').removeClass('visible');
                
                if (type === 'fixed') {
                    $('.hourly-fields').removeClass('visible');
                    $('.fixed-fields').addClass('visible');
                    $('.retainer-fields').removeClass('visible');
                } else if (type === 'retainer') {
                    $('.hourly-fields').addClass('visible');
                    $('.fixed-fields').removeClass('visible');
                    $('.retainer-fields').addClass('visible');
                } else {
                    // Hourly (default)
                    $('.hourly-fields').addClass('visible');
                    $('.fixed-fields').removeClass('visible');
                    $('.retainer-fields').removeClass('visible');
                }
            });

            // Trigger initial state
            $('#billing_type').trigger('change');
        },

        // Initialize calculations
        initCalculations: function() {
            // Invoice item calculations
            $(document).on('input', '.item-quantity, .item-rate', function() {
                var $row = $(this).closest('.fs-invoice-item');
                var qty = parseFloat($row.find('.item-quantity').val()) || 0;
                var rate = parseFloat($row.find('.item-rate').val()) || 0;
                var amount = qty * rate;
                
                $row.find('.item-amount').val(amount.toFixed(2));
                FS_Admin.calculateInvoiceTotals();
            });

            // Tax rate change
            $(document).on('input', '#tax_rate, #discount_value', function() {
                FS_Admin.calculateInvoiceTotals();
            });

            // Discount type change
            $(document).on('change', '#discount_type', function() {
                FS_Admin.calculateInvoiceTotals();
            });
            
            // Calculate totals on page load if we're on an invoice form
            if ($('#tax_rate').length && $('.fs-invoice-item').length) {
                FS_Admin.calculateInvoiceTotals();
            }
        },

        // Calculate invoice totals
        calculateInvoiceTotals: function() {
            var subtotal = 0;
            
            $('.fs-invoice-item').each(function() {
                var amount = parseFloat($(this).find('.item-amount').val()) || 0;
                subtotal += amount;
            });
            
            var taxRate = parseFloat($('#tax_rate').val()) || 0;
            var discountType = $('#discount_type').val();
            var discountValue = parseFloat($('#discount_value').val()) || 0;
            
            var discountAmount = 0;
            if (discountType === 'percentage') {
                discountAmount = subtotal * (discountValue / 100);
            } else if (discountType === 'fixed') {
                discountAmount = discountValue;
            }
            
            var taxAmount = (subtotal - discountAmount) * (taxRate / 100);
            var total = subtotal - discountAmount + taxAmount;
            
            $('#subtotal-display').text(fs_ajax.currency_symbol + subtotal.toFixed(2));
            $('#discount-display').text('-' + fs_ajax.currency_symbol + discountAmount.toFixed(2));
            $('#tax-display').text(fs_ajax.currency_symbol + taxAmount.toFixed(2));
            $('#total-display').text(fs_ajax.currency_symbol + total.toFixed(2));
            
            // Update hidden fields if they exist
            $('input[name="subtotal"]').val(subtotal.toFixed(2));
        },

        // Generate invoice from project
        generateInvoiceFromProject: function(projectId, includeTime, includeRetainer, retainerPeriodId) {
            $.ajax({
                url: fs_ajax.ajax_url,
                type: 'POST',
                data: {
                    action: 'fs_generate_invoice_from_project',
                    project_id: projectId,
                    include_time: includeTime ? '1' : '0',
                    include_retainer: includeRetainer ? '1' : '0',
                    retainer_period_id: retainerPeriodId || '',
                    nonce: fs_ajax.nonce
                },
                beforeSend: function() {
                    $('#fs-create-invoice-btn').prop('disabled', true).text('Creating...');
                },
                success: function(response) {
                    if (response.success) {
                        window.location.href = response.data.redirect_url;
                    } else {
                        alert(response.data.message || fs_ajax.strings.error);
                        $('#fs-create-invoice-btn').prop('disabled', false).text('Create Invoice');
                    }
                },
                error: function() {
                    alert(fs_ajax.strings.error);
                    $('#fs-create-invoice-btn').prop('disabled', false).text('Create Invoice');
                }
            });
        },

        // Initialize invoice project modal handlers
        initInvoiceProjectModal: function() {
            var self = this;
            
            // Initialize the new Invoice Wizard
            this.initInvoiceWizard();
            
            // Legacy: Open old modal when Invoice Project button clicked (fallback)
            $(document).on('click', '#fs-invoice-project-btn:not(.fs-open-invoice-wizard)', function(e) {
                e.preventDefault();
                $('#fs-invoice-project-modal').show();
            });
            
            // Open new Invoice Wizard
            $(document).on('click', '.fs-open-invoice-wizard, #fs-invoice-wizard-btn', function(e) {
                e.preventDefault();
                var projectId = $(this).data('project-id');
                var clientId = $(this).data('client-id');
                self.openInvoiceWizard(projectId, clientId);
            });
            
            // Close modal
            $(document).on('click', '#fs-invoice-project-modal .fs-modal-close, #fs-invoice-project-modal .fs-modal-cancel', function() {
                $('#fs-invoice-project-modal').hide();
            });
            
            // Close on backdrop click
            $(document).on('click', '#fs-invoice-project-modal', function(e) {
                if ($(e.target).is('#fs-invoice-project-modal')) {
                    $('#fs-invoice-project-modal').hide();
                }
            });
            
            // Create invoice button (legacy)
            $(document).on('click', '#fs-create-invoice-btn:not(.fs-wizard-create)', function(e) {
                e.preventDefault();
                var $btn = $(this);
                var projectId = $btn.data('project-id');
                var includeTime = $('#invoice-include-time').is(':checked');
                var includeRetainer = $('#invoice-include-retainer').is(':checked');
                var retainerPeriodId = $btn.data('retainer-period-id') || '';
                self.generateInvoiceFromProject(projectId, includeTime, includeRetainer, retainerPeriodId);
            });
        },
        
        // Invoice Wizard State
        wizardData: {
            step: 1,
            invoiceType: 'combined',
            projects: [],
            selectedProjects: [],
            timeEntries: {},
            services: {},
            selectedTimeEntries: {},
            selectedServices: {},
            timeGrouping: 'activity',
            showEntryDates: false,
            showDescriptions: false,
            // Date grouping specific options
            showDateActivity: true,
            showDateTask: false,
            showDateDescriptions: false,
            serviceBillingOptions: {},
            previewData: null
        },
        
        // Initialize Invoice Wizard
        initInvoiceWizard: function() {
            var self = this;
            
            // Close wizard modal
            $(document).on('click', '#fs-invoice-wizard-modal .fs-modal-close', function() {
                self.closeInvoiceWizard();
            });
            
            // Close on backdrop click
            $(document).on('click', '#fs-invoice-wizard-modal', function(e) {
                if ($(e.target).is('#fs-invoice-wizard-modal')) {
                    self.closeInvoiceWizard();
                }
            });
            
            // Invoice type selection
            $(document).on('change', 'input[name="invoice_type"]', function() {
                self.wizardData.invoiceType = $(this).val();
            });
            
            // Client filter change
            $(document).on('change', '#fs-wizard-client-filter', function() {
                // Clear current selections when filter changes
                self.wizardData.selectedProjects = [];
                self.renderProjectList();
            });
            
            // Project selection
            $(document).on('change', '#fs-wizard-project-list input[type="checkbox"]', function() {
                var projectId = $(this).val();
                if ($(this).is(':checked')) {
                    if (self.wizardData.selectedProjects.indexOf(projectId) === -1) {
                        self.wizardData.selectedProjects.push(projectId);
                    }
                } else {
                    self.wizardData.selectedProjects = self.wizardData.selectedProjects.filter(function(id) {
                        return id !== projectId;
                    });
                }
            });
            
            // Time grouping change
            $(document).on('change', '#fs-time-grouping', function() {
                self.wizardData.timeGrouping = $(this).val();
                self.updateGroupingOptionsVisibility();
                self.checkTasksAvailability();
                self.updateLivePreview();
            });
            
            // Show dates checkbox for Activity grouping
            $(document).on('change', '#fs-show-activity-dates', function() {
                self.wizardData.showEntryDates = $(this).is(':checked');
                self.updateLivePreview();
            });
            
            // Show descriptions checkbox for Activity grouping
            $(document).on('change', '#fs-show-activity-descriptions', function() {
                self.wizardData.showDescriptions = $(this).is(':checked');
                self.updateLivePreview();
            });
            
            // Show dates checkbox for Task grouping
            $(document).on('change', '#fs-show-task-dates', function() {
                self.wizardData.showEntryDates = $(this).is(':checked');
                self.updateLivePreview();
            });
            
            // Show descriptions checkbox for Task grouping
            $(document).on('change', '#fs-show-task-descriptions', function() {
                self.wizardData.showDescriptions = $(this).is(':checked');
                self.updateLivePreview();
            });
            
            // Date grouping options
            $(document).on('change', '#fs-show-date-activity', function() {
                self.wizardData.showDateActivity = $(this).is(':checked');
                self.updateLivePreview();
            });
            
            $(document).on('change', '#fs-show-date-task', function() {
                self.wizardData.showDateTask = $(this).is(':checked');
                self.updateLivePreview();
            });
            
            $(document).on('change', '#fs-show-date-descriptions', function() {
                self.wizardData.showDateDescriptions = $(this).is(':checked');
                self.updateLivePreview();
            });
            
            // Time entry selection
            $(document).on('change', '.fs-time-entry-checkbox', function() {
                var entryId = $(this).val();
                var projectId = $(this).data('project-id');
                
                if (!self.wizardData.selectedTimeEntries[projectId]) {
                    self.wizardData.selectedTimeEntries[projectId] = [];
                }
                
                if ($(this).is(':checked')) {
                    if (self.wizardData.selectedTimeEntries[projectId].indexOf(entryId) === -1) {
                        self.wizardData.selectedTimeEntries[projectId].push(entryId);
                    }
                } else {
                    self.wizardData.selectedTimeEntries[projectId] = self.wizardData.selectedTimeEntries[projectId].filter(function(id) {
                        return id !== entryId;
                    });
                }
                self.updateLivePreview();
            });
            
            // Service selection
            $(document).on('change', '.fs-service-checkbox', function() {
                var serviceIndex = $(this).val();
                var projectId = $(this).data('project-id');
                
                if (!self.wizardData.selectedServices[projectId]) {
                    self.wizardData.selectedServices[projectId] = [];
                }
                
                if ($(this).is(':checked')) {
                    if (self.wizardData.selectedServices[projectId].indexOf(serviceIndex) === -1) {
                        self.wizardData.selectedServices[projectId].push(serviceIndex);
                    }
                } else {
                    self.wizardData.selectedServices[projectId] = self.wizardData.selectedServices[projectId].filter(function(idx) {
                        return idx !== serviceIndex;
                    });
                }
                self.updateLivePreview();
            });
            
            // Service billing option change
            $(document).on('change', '.fs-service-billing-option', function() {
                var projectId = $(this).data('project-id');
                var serviceIndex = $(this).data('service-index');
                var option = $(this).val();
                
                if (!self.wizardData.serviceBillingOptions[projectId]) {
                    self.wizardData.serviceBillingOptions[projectId] = {};
                }
                if (!self.wizardData.serviceBillingOptions[projectId][serviceIndex]) {
                    self.wizardData.serviceBillingOptions[projectId][serviceIndex] = {};
                }
                
                self.wizardData.serviceBillingOptions[projectId][serviceIndex].option = option;
                
                // Toggle visibility of percentage/custom fields
                var $container = $(this).closest('.fs-service-item');
                $container.find('.fs-billing-percentage').toggle(option === 'percentage');
                $container.find('.fs-billing-custom').toggle(option === 'custom');
                
                self.updateLivePreview();
            });
            
            // Percentage input change
            $(document).on('change', '.fs-billing-percentage-input', function() {
                var projectId = $(this).data('project-id');
                var serviceIndex = $(this).data('service-index');
                
                if (!self.wizardData.serviceBillingOptions[projectId]) {
                    self.wizardData.serviceBillingOptions[projectId] = {};
                }
                if (!self.wizardData.serviceBillingOptions[projectId][serviceIndex]) {
                    self.wizardData.serviceBillingOptions[projectId][serviceIndex] = {};
                }
                
                self.wizardData.serviceBillingOptions[projectId][serviceIndex].percentage = $(this).val();
                self.updateLivePreview();
            });
            
            // Custom amount input change
            $(document).on('change', '.fs-billing-custom-input', function() {
                var projectId = $(this).data('project-id');
                var serviceIndex = $(this).data('service-index');
                
                if (!self.wizardData.serviceBillingOptions[projectId]) {
                    self.wizardData.serviceBillingOptions[projectId] = {};
                }
                if (!self.wizardData.serviceBillingOptions[projectId][serviceIndex]) {
                    self.wizardData.serviceBillingOptions[projectId][serviceIndex] = {};
                }
                
                self.wizardData.serviceBillingOptions[projectId][serviceIndex].customAmount = $(this).val();
                self.updateLivePreview();
            });
            
            // Milestone label input change
            $(document).on('change', '.fs-billing-label-input', function() {
                var projectId = $(this).data('project-id');
                var serviceIndex = $(this).data('service-index');
                
                if (!self.wizardData.serviceBillingOptions[projectId]) {
                    self.wizardData.serviceBillingOptions[projectId] = {};
                }
                if (!self.wizardData.serviceBillingOptions[projectId][serviceIndex]) {
                    self.wizardData.serviceBillingOptions[projectId][serviceIndex] = {};
                }
                
                self.wizardData.serviceBillingOptions[projectId][serviceIndex].label = $(this).val();
            });
            
            // Select all time entries for a project
            $(document).on('click', '.fs-select-all-time', function() {
                var projectId = $(this).data('project-id');
                var $checkboxes = $(this).closest('.fs-project-time-entries').find('.fs-time-entry-checkbox');
                $checkboxes.prop('checked', true).trigger('change');
            });
            
            // Select all services for a project
            $(document).on('click', '.fs-select-all-services', function() {
                var projectId = $(this).data('project-id');
                var $checkboxes = $(this).closest('.fs-project-services').find('.fs-service-checkbox');
                $checkboxes.prop('checked', true).trigger('change');
            });
            
            // Navigation buttons
            $(document).on('click', '#fs-wizard-next', function() {
                self.wizardNextStep();
            });
            
            $(document).on('click', '#fs-wizard-prev', function() {
                self.wizardPrevStep();
            });
            
            // Create invoice button
            $(document).on('click', '#fs-wizard-create', function() {
                self.createInvoiceFromWizard();
            });
        },
        
        // Open Invoice Wizard
        openInvoiceWizard: function(preselectedProjectId, preselectedClientId) {
            var self = this;
            
            // Reset wizard state
            this.resetInvoiceWizard();
            
            // Show modal
            $('#fs-invoice-wizard-modal').addClass('active');
            
            // Load wizard data
            $.ajax({
                url: fs_ajax.ajax_url,
                type: 'POST',
                data: {
                    action: 'fs_get_invoice_wizard_data',
                    nonce: fs_ajax.nonce,
                    project_id: preselectedProjectId || '',
                    client_id: preselectedClientId || ''
                },
                success: function(response) {
                    if (response.success) {
                        self.wizardData.projects = response.data.projects;
                        self.populateClientFilter();
                        self.renderProjectList(preselectedProjectId, preselectedClientId);
                    } else {
                        alert(response.data.message || 'Error loading wizard data');
                    }
                },
                error: function() {
                    alert('Error loading wizard data');
                }
            });
        },
        
        // Close Invoice Wizard
        closeInvoiceWizard: function() {
            $('#fs-invoice-wizard-modal').removeClass('active');
            this.resetInvoiceWizard();
        },
        
        // Reset wizard state
        resetInvoiceWizard: function() {
            this.wizardData = {
                step: 1,
                invoiceType: 'combined',
                projects: [],
                selectedProjects: [],
                timeEntries: {},
                services: {},
                selectedTimeEntries: {},
                selectedServices: {},
                timeGrouping: 'activity',
                showEntryDates: false,
                showDescriptions: false,
                showDateActivity: true,
                showDateTask: false,
                showDateDescriptions: false,
                serviceBillingOptions: {},
                previewData: null
            };
            
            // Reset UI
            $('.fs-wizard-panel').hide();
            $('.fs-wizard-panel[data-step="1"]').show();
            $('.fs-wizard-step').removeClass('active completed');
            $('.fs-wizard-step[data-step="1"]').addClass('active');
            $('input[name="invoice_type"][value="combined"]').prop('checked', true);
            $('#fs-time-grouping').val('activity');
            
            // Reset all grouping checkboxes
            $('#fs-show-activity-dates').prop('checked', false);
            $('#fs-show-activity-descriptions').prop('checked', false);
            $('#fs-show-task-dates').prop('checked', false);
            $('#fs-show-task-descriptions').prop('checked', false);
            $('#fs-show-date-activity').prop('checked', true);
            $('#fs-show-date-task').prop('checked', false);
            $('#fs-show-date-descriptions').prop('checked', false);
            
            // Show/hide appropriate grouping options
            this.updateGroupingOptionsVisibility();
            
            $('#fs-wizard-client-filter').val('');
            $('#fs-wizard-prev').hide();
            $('#fs-wizard-next').show();
            $('#fs-wizard-create').hide();
        },
        
        // Update visibility of grouping options based on selected grouping
        updateGroupingOptionsVisibility: function() {
            var grouping = this.wizardData.timeGrouping;
            
            // Hide all option rows first
            $('#fs-grouping-options-activity').hide();
            $('#fs-grouping-options-task').hide();
            $('#fs-grouping-options-date').hide();
            $('#fs-no-tasks-warning').hide();
            
            // Show the appropriate options row
            if (grouping === 'activity') {
                $('#fs-grouping-options-activity').show();
            } else if (grouping === 'task') {
                $('#fs-grouping-options-task').show();
            } else if (grouping === 'date') {
                $('#fs-grouping-options-date').show();
            }
            // entry and project groupings don't need additional options
        },
        
        // Check if selected projects have tasks and show warning if using task grouping
        checkTasksAvailability: function() {
            var self = this;
            var grouping = this.wizardData.timeGrouping;
            
            if (grouping !== 'task') {
                $('#fs-no-tasks-warning').hide();
                return;
            }
            
            // Check if any selected projects have time entries with task_id set
            var hasTaskedEntries = false;
            
            this.wizardData.selectedProjects.forEach(function(projectId) {
                var entries = self.wizardData.timeEntries[projectId] || [];
                entries.forEach(function(entry) {
                    if (entry.task_id && entry.task_id !== '0' && entry.task_id !== 0) {
                        hasTaskedEntries = true;
                    }
                });
            });
            
            // Show warning if no entries have tasks
            if (!hasTaskedEntries && Object.keys(this.wizardData.timeEntries).length > 0) {
                $('#fs-no-tasks-warning').show();
            } else {
                $('#fs-no-tasks-warning').hide();
            }
        },
        
        // Populate client filter dropdown
        populateClientFilter: function() {
            var $select = $('#fs-wizard-client-filter');
            var clients = {};
            
            // Extract unique clients from projects
            this.wizardData.projects.forEach(function(project) {
                if (project.client_id && project.client_name) {
                    clients[project.client_id] = project.client_name;
                }
            });
            
            // Clear and rebuild options
            $select.find('option:not(:first)').remove();
            
            // Sort clients alphabetically
            var sortedClients = Object.keys(clients).sort(function(a, b) {
                return clients[a].localeCompare(clients[b]);
            });
            
            sortedClients.forEach(function(clientId) {
                $select.append('<option value="' + clientId + '">' + FS_Admin.escapeHtml(clients[clientId]) + '</option>');
            });
        },
        
        // Render project list
        renderProjectList: function(preselectedProjectId, filterClientId) {
            var self = this;
            var $list = $('#fs-wizard-project-list');
            $list.empty();
            
            // Get current client filter value
            var clientFilter = filterClientId || $('#fs-wizard-client-filter').val();
            
            // Filter projects by client if a filter is set
            var filteredProjects = this.wizardData.projects;
            if (clientFilter) {
                filteredProjects = this.wizardData.projects.filter(function(project) {
                    return project.client_id == clientFilter;
                });
                // Set the dropdown to match
                $('#fs-wizard-client-filter').val(clientFilter);
            }
            
            if (filteredProjects.length === 0) {
                $list.html('<p class="fs-no-items">No active projects found.</p>');
                return;
            }
            
            filteredProjects.forEach(function(project) {
                var isChecked = preselectedProjectId && project.id == preselectedProjectId;
                var unbilledInfo = [];
                
                if (project.unbilled_hours > 0) {
                    unbilledInfo.push(project.unbilled_hours + ' hrs unbilled');
                }
                if (project.unbilled_services > 0) {
                    unbilledInfo.push(project.unbilled_services + ' services');
                }
                
                var html = '<label class="fs-checkbox-item">' +
                    '<input type="checkbox" value="' + project.id + '"' + (isChecked ? ' checked' : '') + '>' +
                    '<span class="fs-checkbox-label">' +
                        '<strong>' + self.escapeHtml(project.name) + '</strong>' +
                        (project.client_name ? '<br><small>' + self.escapeHtml(project.client_name) + '</small>' : '') +
                        (unbilledInfo.length ? '<br><small class="fs-text-muted">' + unbilledInfo.join(' • ') + '</small>' : '') +
                    '</span>' +
                '</label>';
                
                $list.append(html);
                
                if (isChecked) {
                    self.wizardData.selectedProjects.push(project.id.toString());
                }
            });
        },
        
        // Navigate to next step
        wizardNextStep: function() {
            var self = this;
            
            console.log('=== wizardNextStep v3.2.16 ===');
            console.log('current step:', this.wizardData.step);
            console.log('selectedProjects:', this.wizardData.selectedProjects);
            console.log('invoiceType:', this.wizardData.invoiceType);
            console.log('timeEntries:', this.wizardData.timeEntries);
            console.log('selectedTimeEntries:', this.wizardData.selectedTimeEntries);
            
            if (this.wizardData.step === 1) {
                // Validate step 1
                if (this.wizardData.selectedProjects.length === 0) {
                    alert('Please select at least one project.');
                    return;
                }
                
                console.log('Step 1 validated, loading step 2 data...');
                // Load step 2 data
                this.loadStep2Data();
            } else if (this.wizardData.step === 2) {
                // Validate step 2
                var hasSelection = false;
                var invoiceType = this.wizardData.invoiceType;
                
                console.log('Step 2 validation - invoiceType:', invoiceType);
                console.log('Step 2 validation - selectedTimeEntries:', this.wizardData.selectedTimeEntries);
                console.log('Step 2 validation - selectedServices:', this.wizardData.selectedServices);
                
                if (invoiceType === 'time_entries' || invoiceType === 'combined') {
                    for (var projectId in this.wizardData.selectedTimeEntries) {
                        console.log('Checking project', projectId, '- entries:', this.wizardData.selectedTimeEntries[projectId]);
                        if (this.wizardData.selectedTimeEntries[projectId].length > 0) {
                            hasSelection = true;
                            break;
                        }
                    }
                }
                
                if (invoiceType === 'project_services' || invoiceType === 'combined') {
                    for (var projectId in this.wizardData.selectedServices) {
                        if (this.wizardData.selectedServices[projectId].length > 0) {
                            hasSelection = true;
                            break;
                        }
                    }
                }
                
                console.log('Step 2 validation - hasSelection:', hasSelection);
                
                if (!hasSelection) {
                    alert('Please select at least one item to invoice.');
                    return;
                }
                
                // Load preview
                this.loadPreview();
            }
        },
        
        // Navigate to previous step
        wizardPrevStep: function() {
            if (this.wizardData.step > 1) {
                this.wizardData.step--;
                this.updateWizardUI();
            }
        },
        
        // Update wizard UI based on current step
        updateWizardUI: function() {
            var step = this.wizardData.step;
            
            // Update step indicators
            $('.fs-wizard-step').removeClass('active completed');
            for (var i = 1; i < step; i++) {
                $('.fs-wizard-step[data-step="' + i + '"]').addClass('completed');
            }
            $('.fs-wizard-step[data-step="' + step + '"]').addClass('active');
            
            // Show/hide panels
            $('.fs-wizard-panel').hide();
            $('.fs-wizard-panel[data-step="' + step + '"]').show();
            
            // Update navigation buttons
            $('#fs-wizard-prev').toggle(step > 1);
            $('#fs-wizard-next').toggle(step < 3);
            $('#fs-wizard-create').toggle(step === 3);
        },
        
        // Load Step 2 data
        loadStep2Data: function() {
            var self = this;
            var invoiceType = this.wizardData.invoiceType;
            var projectIds = this.wizardData.selectedProjects;
            
            console.log('loadStep2Data called', {invoiceType: invoiceType, projectIds: projectIds});
            
            // Show loading state
            $('#fs-wizard-time-entries-container').html('<p class="fs-loading">Loading time entries...</p>');
            $('#fs-wizard-services-container').html('<p class="fs-loading">Loading services...</p>');
            
            // Determine which sections to show
            $('#fs-wizard-time-section').toggle(invoiceType === 'time_entries' || invoiceType === 'combined');
            $('#fs-wizard-services-section').toggle(invoiceType === 'project_services' || invoiceType === 'combined');
            
            var loadPromises = [];
            
            // Load time entries if needed
            if (invoiceType === 'time_entries' || invoiceType === 'combined') {
                projectIds.forEach(function(projectId) {
                    loadPromises.push(self.loadTimeEntries(projectId));
                });
            }
            
            // Load services if needed
            if (invoiceType === 'project_services' || invoiceType === 'combined') {
                projectIds.forEach(function(projectId) {
                    loadPromises.push(self.loadServices(projectId));
                });
            }
            
            // When all data is loaded, move to step 2
            $.when.apply($, loadPromises).done(function() {
                console.log('All promises resolved, moving to step 2');
                self.renderStep2();
                self.wizardData.step = 2;
                self.updateWizardUI();
            }).fail(function(jqXHR, textStatus, errorThrown) {
                console.error('AJAX error:', textStatus, errorThrown);
                alert('Error loading invoice data: ' + (errorThrown || textStatus));
            });
        },
        
        // Load time entries for a project
        loadTimeEntries: function(projectId) {
            var self = this;
            console.log('Loading time entries for project', projectId);
            return $.ajax({
                url: fs_ajax.ajax_url,
                type: 'POST',
                data: {
                    action: 'fs_get_unbilled_time_entries',
                    nonce: fs_ajax.nonce,
                    project_id: projectId
                },
                success: function(response) {
                    console.log('Time entries response for project ' + projectId, response);
                    if (response.success && response.data && response.data.entries) {
                        var entries = response.data.entries;
                        console.log('Found ' + entries.length + ' time entries for project ' + projectId, entries);
                        self.wizardData.timeEntries[projectId] = entries;
                        // Pre-select all entries
                        self.wizardData.selectedTimeEntries[projectId] = entries.map(function(e) { return e.id.toString(); });
                        console.log('Pre-selected entries:', self.wizardData.selectedTimeEntries[projectId]);
                    } else {
                        console.log('No time entries found for project ' + projectId);
                        self.wizardData.timeEntries[projectId] = [];
                        self.wizardData.selectedTimeEntries[projectId] = [];
                    }
                },
                error: function(jqXHR, textStatus, errorThrown) {
                    console.error('AJAX error loading time entries:', textStatus, errorThrown, jqXHR.responseText);
                }
            });
        },
        
        // Load services for a project
        loadServices: function(projectId) {
            var self = this;
            var project = this.wizardData.projects.find(function(p) { return p.id == projectId; });
            
            if (project && project.services) {
                self.wizardData.services[projectId] = project.services;
                // Pre-select unbilled services (using original array indices)
                self.wizardData.selectedServices[projectId] = [];
                project.services.forEach(function(service, idx) {
                    if (!service.fully_billed) {
                        self.wizardData.selectedServices[projectId].push(idx.toString());
                    }
                });
            }
            
            return $.Deferred().resolve();
        },
        
        // Render Step 2 content
        renderStep2: function() {
            var self = this;
            var invoiceType = this.wizardData.invoiceType;
            
            // Render time entries
            if (invoiceType === 'time_entries' || invoiceType === 'combined') {
                var $timeContainer = $('#fs-wizard-time-entries-container');
                $timeContainer.empty();
                
                this.wizardData.selectedProjects.forEach(function(projectId) {
                    var project = self.wizardData.projects.find(function(p) { return p.id == projectId; });
                    var entries = self.wizardData.timeEntries[projectId] || [];
                    
                    if (entries.length === 0) return;
                    
                    var html = '<div class="fs-project-time-entries" data-project-id="' + projectId + '">' +
                        '<div class="fs-project-header">' +
                            '<strong>' + self.escapeHtml(project.name) + '</strong>' +
                            '<button type="button" class="button button-small fs-select-all-time" data-project-id="' + projectId + '">Select All</button>' +
                        '</div>' +
                        '<div class="fs-entry-list">';
                    
                    entries.forEach(function(entry) {
                        var isSelected = self.wizardData.selectedTimeEntries[projectId] && 
                            self.wizardData.selectedTimeEntries[projectId].indexOf(entry.id.toString()) !== -1;
                        
                        html += '<label class="fs-checkbox-item">' +
                            '<input type="checkbox" class="fs-time-entry-checkbox" value="' + entry.id + '" data-project-id="' + projectId + '"' + (isSelected ? ' checked' : '') + '>' +
                            '<span class="fs-entry-info">' +
                                '<span class="fs-entry-date">' + entry.date + '</span>' +
                                '<span class="fs-entry-desc">' + self.escapeHtml(entry.description || entry.activity || 'No description') + '</span>' +
                                '<span class="fs-entry-hours">' + entry.hours + ' hrs @ $' + parseFloat(entry.rate).toFixed(2) + '</span>' +
                            '</span>' +
                        '</label>';
                    });
                    
                    html += '</div></div>';
                    $timeContainer.append(html);
                });
                
                if ($timeContainer.children().length === 0) {
                    $timeContainer.html('<p class="fs-no-items">No unbilled time entries found.</p>');
                }
            }
            
            // Render services
            if (invoiceType === 'project_services' || invoiceType === 'combined') {
                var $serviceContainer = $('#fs-wizard-services-container');
                $serviceContainer.empty();
                
                this.wizardData.selectedProjects.forEach(function(projectId) {
                    var project = self.wizardData.projects.find(function(p) { return p.id == projectId; });
                    var services = self.wizardData.services[projectId] || [];
                    
                    if (services.length === 0) return;
                    
                    var html = '<div class="fs-project-services" data-project-id="' + projectId + '">' +
                        '<div class="fs-project-header">' +
                            '<strong>' + self.escapeHtml(project.name) + '</strong>' +
                            '<button type="button" class="button button-small fs-select-all-services" data-project-id="' + projectId + '">Select All</button>' +
                        '</div>' +
                        '<div class="fs-service-list">';
                    
                    services.forEach(function(service, idx) {
                        var isSelected = self.wizardData.selectedServices[projectId] && 
                            self.wizardData.selectedServices[projectId].indexOf(idx.toString()) !== -1;
                        
                        var billedStatus = service.fully_billed ? ' (Fully Billed)' : 
                            (service.billed_amount > 0 ? ' ($' + parseFloat(service.billed_amount).toFixed(2) + ' billed)' : '');
                        
                        html += '<div class="fs-service-item">' +
                            '<label class="fs-checkbox-item">' +
                                '<input type="checkbox" class="fs-service-checkbox" value="' + idx + '" data-project-id="' + projectId + '"' + (isSelected && !service.fully_billed ? ' checked' : '') + (service.fully_billed ? ' disabled' : '') + '>' +
                                '<span class="fs-service-info">' +
                                    '<span class="fs-service-name">' + self.escapeHtml(service.name) + billedStatus + '</span>' +
                                    '<span class="fs-service-amount">$' + parseFloat(service.amount).toFixed(2) + '</span>' +
                                '</span>' +
                            '</label>';
                        
                        if (!service.fully_billed) {
                            html += '<div class="fs-billing-options" style="margin-left: 24px; margin-top: 8px;">' +
                                '<select class="fs-service-billing-option" data-project-id="' + projectId + '" data-service-index="' + idx + '">' +
                                    '<option value="full">Bill Full Amount</option>' +
                                    '<option value="percentage">Bill Percentage</option>' +
                                    '<option value="custom">Custom Amount</option>' +
                                '</select>' +
                                '<div class="fs-billing-percentage" style="display: none; margin-top: 8px;">' +
                                    '<div class="fs-input-suffix">' +
                                        '<input type="number" class="fs-billing-percentage-input" data-project-id="' + projectId + '" data-service-index="' + idx + '" value="50" min="1" max="100" style="width: 80px;">' +
                                        '<span>%</span>' +
                                    '</div>' +
                                    '<input type="text" class="fs-billing-label-input" data-project-id="' + projectId + '" data-service-index="' + idx + '" placeholder="Label (e.g., Deposit)" style="margin-top: 4px;">' +
                                '</div>' +
                                '<div class="fs-billing-custom" style="display: none; margin-top: 8px;">' +
                                    '<div class="fs-input-prefix">' +
                                        '<span>$</span>' +
                                        '<input type="number" class="fs-billing-custom-input" data-project-id="' + projectId + '" data-service-index="' + idx + '" value="" min="0" step="0.01" style="width: 100px;">' +
                                    '</div>' +
                                    '<input type="text" class="fs-billing-label-input" data-project-id="' + projectId + '" data-service-index="' + idx + '" placeholder="Label (e.g., Phase 1)" style="margin-top: 4px;">' +
                                '</div>' +
                            '</div>';
                        }
                        
                        html += '</div>';
                    });
                    
                    html += '</div></div>';
                    $serviceContainer.append(html);
                });
                
                if ($serviceContainer.children().length === 0) {
                    $serviceContainer.html('<p class="fs-no-items">No services found.</p>');
                }
            }
            
            // Update grouping options visibility
            this.updateGroupingOptionsVisibility();
            
            // Check if tasks are available for task grouping
            this.checkTasksAvailability();
            
            // Trigger initial live preview after rendering
            this.updateLivePreview();
        },
        
        // Update live preview in Step 2
        updateLivePreview: function() {
            var self = this;
            var $content = $('#fs-wizard-live-preview-content');
            var $totalAmount = $('#fs-live-preview-total-amount');
            var invoiceType = this.wizardData.invoiceType;
            var grouping = this.wizardData.timeGrouping || 'activity';
            var showDates = this.wizardData.showEntryDates;
            
            var html = '';
            var grandTotal = 0;
            var hasItems = false;
            
            // Get grouping label for display
            var groupingLabels = {
                'activity': 'Activity Type',
                'task': 'Task',
                'date': 'Date',
                'entry': 'Individual Entry',
                'project': 'Project Total'
            };
            
            // Process each selected project
            this.wizardData.selectedProjects.forEach(function(projectId) {
                var project = self.wizardData.projects.find(function(p) { return p.id == projectId; });
                if (!project) return;
                
                var projectItems = [];
                var projectTotal = 0;
                
                // Process time entries if applicable
                if (invoiceType === 'time_entries' || invoiceType === 'combined') {
                    var entries = self.wizardData.timeEntries[projectId] || [];
                    var selectedEntryIds = self.wizardData.selectedTimeEntries[projectId] || [];
                    
                    // Filter to selected entries
                    var selectedEntries = entries.filter(function(e) {
                        return selectedEntryIds.indexOf(e.id.toString()) !== -1;
                    });
                    
                    if (selectedEntries.length > 0) {
                        hasItems = true;
                        
                        // Group entries based on selected grouping
                        var groupedItems = self.groupTimeEntries(selectedEntries, grouping, showDates);
                        groupedItems.forEach(function(item) {
                            projectItems.push(item);
                            projectTotal += item.amount;
                        });
                    }
                }
                
                // Process services if applicable
                if (invoiceType === 'project_services' || invoiceType === 'combined') {
                    var services = self.wizardData.services[projectId] || [];
                    var selectedServiceIndices = self.wizardData.selectedServices[projectId] || [];
                    
                    selectedServiceIndices.forEach(function(idx) {
                        var service = services[parseInt(idx)];
                        if (!service || service.fully_billed) return;
                        
                        hasItems = true;
                        var amount = parseFloat(service.amount);
                        var label = service.name;
                        
                        // Check for billing options
                        var billingOpts = self.wizardData.serviceBillingOptions[projectId] && 
                            self.wizardData.serviceBillingOptions[projectId][idx];
                        
                        if (billingOpts) {
                            if (billingOpts.option === 'percentage' && billingOpts.percentage) {
                                amount = amount * (parseFloat(billingOpts.percentage) / 100);
                                label = service.name + ' (' + billingOpts.percentage + '%)';
                            } else if (billingOpts.option === 'custom' && billingOpts.customAmount) {
                                amount = parseFloat(billingOpts.customAmount);
                                label = service.name + ' (Custom)';
                            }
                            
                            if (billingOpts.label) {
                                label = billingOpts.label + ' - ' + service.name;
                            }
                        }
                        
                        projectItems.push({
                            description: label,
                            amount: amount,
                            date: null
                        });
                        projectTotal += amount;
                    });
                }
                
                // Render project section if it has items
                if (projectItems.length > 0) {
                    html += '<div class="fs-live-preview-project">';
                    html += '<div class="fs-live-preview-project-name">' + self.escapeHtml(project.name) + '</div>';
                    
                    projectItems.forEach(function(item) {
                        html += '<div class="fs-live-preview-item">';
                        html += '<span class="fs-live-preview-item-desc">' + self.escapeHtml(item.description);
                        if (item.date && showDates) {
                            html += '<span class="fs-live-preview-item-date">' + item.date + '</span>';
                        }
                        html += '</span>';
                        html += '<span class="fs-live-preview-item-amount">$' + item.amount.toFixed(2) + '</span>';
                        html += '</div>';
                    });
                    
                    html += '<div class="fs-live-preview-subtotal">';
                    html += '<span>Subtotal</span>';
                    html += '<span>$' + projectTotal.toFixed(2) + '</span>';
                    html += '</div>';
                    html += '</div>';
                    
                    grandTotal += projectTotal;
                }
            });
            
            // Show grouping indicator for time entries
            if ((invoiceType === 'time_entries' || invoiceType === 'combined') && hasItems) {
                var indicator = '<div class="fs-grouping-indicator">';
                indicator += '<span class="dashicons dashicons-list-view" style="font-size:14px;width:14px;height:14px;"></span>';
                indicator += 'Grouped by: <strong>' + groupingLabels[grouping] + '</strong>';
                if (showDates) {
                    indicator += ' &middot; With dates';
                }
                indicator += '</div>';
                html = indicator + html;
            }
            
            if (!hasItems) {
                html = '<p class="fs-no-items">Select items to see preview</p>';
            }
            
            $content.html(html);
            $totalAmount.text('$' + grandTotal.toFixed(2));
        },
        
        // Group time entries based on grouping option
        groupTimeEntries: function(entries, grouping, showDates) {
            var self = this;
            var items = [];
            var showDescriptions = this.wizardData.showDescriptions;
            var showDateActivity = this.wizardData.showDateActivity;
            var showDateTask = this.wizardData.showDateTask;
            var showDateDescriptions = this.wizardData.showDateDescriptions;
            
            if (grouping === 'entry') {
                // Individual entries
                entries.forEach(function(entry) {
                    // Capitalize first letter of activity if used
                    var activityCapitalized = entry.activity ? entry.activity.charAt(0).toUpperCase() + entry.activity.slice(1) : 'Time Entry';
                    var desc = entry.description || activityCapitalized;
                    var hours = parseFloat(entry.hours);
                    var rate = parseFloat(entry.rate);
                    items.push({
                        description: desc + ' (' + hours + ' hrs @ $' + rate.toFixed(2) + ')',
                        amount: hours * rate,
                        date: showDates ? entry.date : null
                    });
                });
            } else if (grouping === 'project') {
                // Project total - single line
                var totalHours = 0;
                var totalAmount = 0;
                var avgRate = 0;
                
                entries.forEach(function(entry) {
                    var hours = parseFloat(entry.hours);
                    var rate = parseFloat(entry.rate);
                    totalHours += hours;
                    totalAmount += hours * rate;
                });
                
                if (totalHours > 0) {
                    avgRate = totalAmount / totalHours;
                }
                
                var dateRange = '';
                if (showDates && entries.length > 0) {
                    var sortedDates = entries.map(function(e) { return e.date; }).filter(function(d) { return d; }).sort();
                    if (sortedDates.length > 1) {
                        dateRange = sortedDates[0] + ' - ' + sortedDates[sortedDates.length - 1];
                    } else if (sortedDates.length === 1) {
                        dateRange = sortedDates[0];
                    }
                }
                
                items.push({
                    description: 'Development & Design (' + totalHours.toFixed(2) + ' hrs)',
                    amount: totalAmount,
                    date: dateRange
                });
            } else if (grouping === 'activity') {
                // Group by activity type
                var byActivity = {};
                entries.forEach(function(entry) {
                    var activity = entry.activity || 'Other';
                    if (!byActivity[activity]) {
                        byActivity[activity] = { hours: 0, amount: 0, dates: [], descriptions: [] };
                    }
                    var hours = parseFloat(entry.hours);
                    var rate = parseFloat(entry.rate);
                    byActivity[activity].hours += hours;
                    byActivity[activity].amount += hours * rate;
                    byActivity[activity].dates.push(entry.date);
                    if (entry.description && entry.description !== activity) {
                        byActivity[activity].descriptions.push(entry.description);
                    }
                });
                
                for (var activity in byActivity) {
                    var data = byActivity[activity];
                    var dateRange = '';
                    if (showDates && data.dates.length > 0) {
                        var sortedDates = data.dates.filter(function(d) { return d; }).sort();
                        if (sortedDates.length > 1) {
                            dateRange = sortedDates[0] + ' - ' + sortedDates[sortedDates.length - 1];
                        } else if (sortedDates.length === 1) {
                            dateRange = sortedDates[0];
                        }
                    }
                    
                    // Capitalize first letter of activity
                    var capitalizedActivity = activity.charAt(0).toUpperCase() + activity.slice(1);
                    var desc = capitalizedActivity + ' (' + data.hours.toFixed(2) + ' hrs)';
                    if (showDescriptions && data.descriptions.length > 0) {
                        var uniqueDescs = data.descriptions.filter(function(d, i, arr) { return arr.indexOf(d) === i; });
                        desc += ' - ' + uniqueDescs.join(', ');
                    }
                    
                    items.push({
                        description: desc,
                        amount: data.amount,
                        date: dateRange
                    });
                }
            } else if (grouping === 'task') {
                // Group by task (from task dropdown, not description field)
                // Use group_task_id which resolves parent for recurring task instances
                var byTask = {};
                entries.forEach(function(entry) {
                    var taskKey = entry.group_task_id || entry.task_id || 'no_task';
                    var taskName = entry.task_name || 'General Work';
                    if (!byTask[taskKey]) {
                        byTask[taskKey] = { name: taskName, hours: 0, amount: 0, dates: [], descriptions: [] };
                    }
                    var hours = parseFloat(entry.hours);
                    var rate = parseFloat(entry.rate);
                    byTask[taskKey].hours += hours;
                    byTask[taskKey].amount += hours * rate;
                    byTask[taskKey].dates.push(entry.date);
                    if (entry.description) {
                        byTask[taskKey].descriptions.push(entry.description);
                    }
                });
                
                for (var taskKey in byTask) {
                    var data = byTask[taskKey];
                    var dateRange = '';
                    if (showDates && data.dates.length > 0) {
                        var sortedDates = data.dates.filter(function(d) { return d; }).sort();
                        if (sortedDates.length > 1) {
                            dateRange = sortedDates[0] + ' - ' + sortedDates[sortedDates.length - 1];
                        } else if (sortedDates.length === 1) {
                            dateRange = sortedDates[0];
                        }
                    }
                    
                    var desc = data.name + ' (' + data.hours.toFixed(2) + ' hrs)';
                    if (showDescriptions && data.descriptions.length > 0) {
                        var uniqueDescs = data.descriptions.filter(function(d, i, arr) { return arr.indexOf(d) === i; });
                        desc += ' - ' + uniqueDescs.join(', ');
                    }
                    
                    items.push({
                        description: desc,
                        amount: data.amount,
                        date: dateRange
                    });
                }
            } else if (grouping === 'date') {
                // Group by date
                var byDate = {};
                entries.forEach(function(entry) {
                    var date = entry.date || 'No Date';
                    if (!byDate[date]) {
                        byDate[date] = { hours: 0, amount: 0, activities: [], tasks: [], descriptions: [] };
                    }
                    var hours = parseFloat(entry.hours);
                    var rate = parseFloat(entry.rate);
                    byDate[date].hours += hours;
                    byDate[date].amount += hours * rate;
                    if (entry.activity) {
                        byDate[date].activities.push(entry.activity);
                    }
                    if (entry.task_name) {
                        byDate[date].tasks.push(entry.task_name);
                    }
                    if (entry.description) {
                        byDate[date].descriptions.push(entry.description);
                    }
                });
                
                // Sort dates
                var sortedDates = Object.keys(byDate).sort();
                
                sortedDates.forEach(function(date) {
                    var data = byDate[date];
                    var descParts = [];
                    
                    // Format date for display
                    var formattedDate = date;
                    if (date !== 'No Date') {
                        var d = new Date(date);
                        formattedDate = (d.getMonth() + 1) + '/' + d.getDate() + '/' + d.getFullYear();
                    }
                    
                    descParts.push(formattedDate);
                    
                    if (showDateActivity && data.activities.length > 0) {
                        var uniqueActivities = data.activities.filter(function(a, i, arr) { return arr.indexOf(a) === i; });
                        // Capitalize first letter of each activity
                        var capitalizedActivities = uniqueActivities.map(function(a) {
                            return a.charAt(0).toUpperCase() + a.slice(1);
                        });
                        descParts.push(capitalizedActivities.join(', '));
                    }
                    
                    if (showDateTask && data.tasks.length > 0) {
                        var uniqueTasks = data.tasks.filter(function(t, i, arr) { return arr.indexOf(t) === i; });
                        descParts.push('[' + uniqueTasks.join(', ') + ']');
                    }
                    
                    if (showDateDescriptions && data.descriptions.length > 0) {
                        var uniqueDescs = data.descriptions.filter(function(d, i, arr) { return arr.indexOf(d) === i; });
                        descParts.push(uniqueDescs.join(', '));
                    }
                    
                    descParts.push('(' + data.hours.toFixed(2) + ' hrs)');
                    
                    items.push({
                        description: descParts.join(' - '),
                        amount: data.amount,
                        date: null // Date is already in the description
                    });
                });
            }
            
            return items;
        },
        
        // Load preview
        loadPreview: function() {
            var self = this;
            
            $('#fs-wizard-preview').html('<p class="fs-loading">Generating preview...</p>');
            
            // Build projects array in the format PHP expects
            var projectsData = [];
            this.wizardData.selectedProjects.forEach(function(projectId) {
                var projData = {
                    id: projectId,
                    time_entry_ids: self.wizardData.selectedTimeEntries[projectId] || [],
                    service_indices: self.wizardData.selectedServices[projectId] || [],
                    time_grouping: self.wizardData.timeGrouping,
                    show_dates: self.wizardData.showEntryDates ? 'true' : 'false',
                    show_descriptions: self.wizardData.showDescriptions ? 'true' : 'false',
                    show_date_activity: self.wizardData.showDateActivity ? 'true' : 'false',
                    show_date_task: self.wizardData.showDateTask ? 'true' : 'false',
                    show_date_descriptions: self.wizardData.showDateDescriptions ? 'true' : 'false'
                };
                
                // Add billing options if set for this project
                var billingOpts = self.wizardData.serviceBillingOptions[projectId];
                if (billingOpts) {
                    projData.billing_option = billingOpts.option || 'full';
                    projData.percentage = billingOpts.percentage || 100;
                    projData.custom_amount = billingOpts.custom_amount || 0;
                    projData.milestone_label = billingOpts.milestone_label || '';
                }
                
                projectsData.push(projData);
            });
            
            console.log('loadPreview sending:', {
                invoice_type: this.wizardData.invoiceType,
                projects: projectsData,
                selectedTimeEntries: this.wizardData.selectedTimeEntries,
                selectedServices: this.wizardData.selectedServices
            });
            
            $.ajax({
                url: fs_ajax.ajax_url,
                type: 'POST',
                data: {
                    action: 'fs_preview_invoice_items',
                    nonce: fs_ajax.nonce,
                    invoice_type: this.wizardData.invoiceType,
                    projects: projectsData
                },
                success: function(response) {
                    console.log('Preview response:', response);
                    if (response.success) {
                        self.wizardData.previewData = response.data;
                        self.renderPreview(response.data);
                        self.wizardData.step = 3;
                        self.updateWizardUI();
                    } else {
                        alert(response.data.message || 'Error generating preview');
                    }
                },
                error: function() {
                    alert('Error generating preview');
                }
            });
        },
        
        // Render preview
        renderPreview: function(data) {
            var self = this;
            var html = '';
            var grandTotal = 0;
            
            // Use project_items from API response
            var projectItems = data.project_items || [];
            
            console.log('renderPreview project_items:', projectItems);
            
            if (projectItems.length === 0) {
                html += '<p class="fs-no-items">No items to invoice. Please go back and select items.</p>';
            } else {
                projectItems.forEach(function(project) {
                    html += '<div class="fs-preview-project">' +
                        '<div class="fs-preview-project-header">' + self.escapeHtml(project.project_name) + '</div>';
                    
                    var subtotal = 0;
                    
                    project.items.forEach(function(item) {
                        html += '<div class="fs-preview-item">' +
                            '<span class="fs-preview-item-desc">' + self.escapeHtml(item.description) + '</span>' +
                            '<span class="fs-preview-item-amount">$' + parseFloat(item.amount).toFixed(2) + '</span>' +
                        '</div>';
                        subtotal += parseFloat(item.amount);
                    });
                    
                    html += '<div class="fs-preview-subtotal">' +
                        '<span>Subtotal</span>' +
                        '<span>$' + subtotal.toFixed(2) + '</span>' +
                    '</div></div>';
                    
                    grandTotal += subtotal;
                });
            }
            
            html += '<div class="fs-preview-total">' +
                '<span>Total</span>' +
                '<span>$' + grandTotal.toFixed(2) + '</span>' +
            '</div>';
            
            $('#fs-wizard-preview').html(html);
            
            // Set default due date (30 days from now)
            var dueDate = new Date();
            dueDate.setDate(dueDate.getDate() + 30);
            $('#fs-wizard-due-date').val(dueDate.toISOString().split('T')[0]);
        },
        
        // Create invoice from wizard
        createInvoiceFromWizard: function() {
            var self = this;
            var $btn = $('#fs-wizard-create');
            
            $btn.prop('disabled', true).text('Creating...');
            
            // Build projects array in the format PHP expects
            var projectsData = [];
            this.wizardData.selectedProjects.forEach(function(projectId) {
                var projData = {
                    id: projectId,
                    time_entry_ids: self.wizardData.selectedTimeEntries[projectId] || [],
                    service_indices: self.wizardData.selectedServices[projectId] || [],
                    time_grouping: self.wizardData.timeGrouping,
                    show_dates: self.wizardData.showEntryDates ? 'true' : 'false',
                    show_descriptions: self.wizardData.showDescriptions ? 'true' : 'false',
                    show_date_activity: self.wizardData.showDateActivity ? 'true' : 'false',
                    show_date_task: self.wizardData.showDateTask ? 'true' : 'false',
                    show_date_descriptions: self.wizardData.showDateDescriptions ? 'true' : 'false'
                };
                
                // Add billing options if set for this project
                var billingOpts = self.wizardData.serviceBillingOptions[projectId];
                if (billingOpts) {
                    projData.billing_option = billingOpts.option || 'full';
                    projData.percentage = billingOpts.percentage || 100;
                    projData.custom_amount = billingOpts.custom_amount || 0;
                    projData.milestone_label = billingOpts.milestone_label || '';
                }
                
                projectsData.push(projData);
            });
            
            $.ajax({
                url: fs_ajax.ajax_url,
                type: 'POST',
                data: {
                    action: 'fs_create_invoice_from_wizard',
                    nonce: fs_ajax.nonce,
                    invoice_type: this.wizardData.invoiceType,
                    projects: projectsData,
                    due_date: $('#fs-wizard-due-date').val(),
                    notes: $('#fs-wizard-notes').val()
                },
                success: function(response) {
                    if (response.success) {
                        window.location.href = response.data.redirect_url;
                    } else {
                        alert(response.data.message || 'Error creating invoice');
                        $btn.prop('disabled', false).text('Create Invoice');
                    }
                },
                error: function() {
                    alert('Error creating invoice');
                    $btn.prop('disabled', false).text('Create Invoice');
                }
            });
        },
        
        // Escape HTML
        escapeHtml: function(text) {
            if (!text) return '';
            var div = document.createElement('div');
            div.appendChild(document.createTextNode(text));
            return div.innerHTML;
        },

        // Initialize contact modal handlers
        initContactHandlers: function() {
            var self = this;
            
            // Open add contact modal
            $(document).on('click', '.fs-add-contact-btn', function(e) {
                e.preventDefault();
                self.openContactModal();
            });
            
            // Open edit contact modal
            $(document).on('click', '.fs-edit-contact', function(e) {
                e.preventDefault();
                var contactId = $(this).data('contact-id');
                self.openContactModal(contactId);
            });
            
            // Close modal
            $(document).on('click', '.fs-modal-close, .fs-modal-cancel, .fs-modal', function(e) {
                if ($(this).hasClass('fs-modal-cancel') || $(this).hasClass('fs-modal-close') || e.target === this) {
                    self.closeContactModal();
                }
            });
            
            // Save contact form
            $(document).on('submit', '#fs-contact-form', function(e) {
                e.preventDefault();
                self.saveContact($(this));
            });
            
            // Delete contact
            $(document).on('click', '.fs-delete-contact', function(e) {
                e.preventDefault();
                if (confirm(fs_ajax.strings.confirm_delete)) {
                    var contactId = $(this).data('contact-id');
                    self.deleteContact(contactId);
                }
            });
        },
        
        openContactModal: function(contactId) {
            var $modal = $('#fs-contact-modal');
            var $form = $('#fs-contact-form');
            
            // Reset form
            $form[0].reset();
            $form.find('input[name="contact_id"]').val('');
            $modal.find('.fs-modal-header h3').text(contactId ? 'Edit Contact' : 'Add Contact');
            
            if (contactId) {
                // Load contact data
                $.ajax({
                    url: fs_ajax.ajax_url,
                    type: 'POST',
                    data: {
                        action: 'fs_get_contact',
                        contact_id: contactId,
                        nonce: fs_ajax.nonce
                    },
                    success: function(response) {
                        if (response.success) {
                            var contact = response.data;
                            $form.find('input[name="contact_id"]').val(contact.id);
                            $form.find('input[name="contact_name"]').val(contact.name);
                            $form.find('input[name="contact_role"]').val(contact.role);
                            $form.find('input[name="contact_email"]').val(contact.email);
                            $form.find('input[name="contact_phone"]').val(contact.phone);
                            $form.find('input[name="contact_is_primary"]').prop('checked', contact.is_primary == 1);
                            $form.find('input[name="contact_is_billing"]').prop('checked', contact.is_billing_contact == 1);
                            $form.find('input[name="contact_is_project"]').prop('checked', contact.is_project_contact == 1);
                        }
                    }
                });
            }
            
            $modal.addClass('active');
        },
        
        closeContactModal: function() {
            $('#fs-contact-modal').removeClass('active');
        },
        
        saveContact: function($form) {
            var self = this;
            var formData = $form.serialize();
            formData += '&action=fs_save_contact&nonce=' + fs_ajax.nonce;
            
            $.ajax({
                url: fs_ajax.ajax_url,
                type: 'POST',
                data: formData,
                beforeSend: function() {
                    $form.find('button[type="submit"]').prop('disabled', true).text('Saving...');
                },
                success: function(response) {
                    if (response.success) {
                        self.closeContactModal();
                        location.reload();
                    } else {
                        alert(response.data.message || fs_ajax.strings.error);
                        $form.find('button[type="submit"]').prop('disabled', false).text('Save Contact');
                    }
                },
                error: function() {
                    alert(fs_ajax.strings.error);
                    $form.find('button[type="submit"]').prop('disabled', false).text('Save Contact');
                }
            });
        },
        
        deleteContact: function(contactId) {
            $.ajax({
                url: fs_ajax.ajax_url,
                type: 'POST',
                data: {
                    action: 'fs_delete_contact',
                    contact_id: contactId,
                    nonce: fs_ajax.nonce
                },
                success: function(response) {
                    if (response.success) {
                        location.reload();
                    } else {
                        alert(response.data.message || fs_ajax.strings.error);
                    }
                },
                error: function() {
                    alert(fs_ajax.strings.error);
                }
            });
        },

        // Initialize invoice status inline change handlers
        initInvoiceStatusHandlers: function() {
            $(document).on('change', '.fs-inline-status-select', function() {
                var $select = $(this);
                
                // Skip dashboard task status selects - they have their own handler
                if ($select.hasClass('fs-dashboard-task-status')) {
                    return;
                }
                
                var invoiceId = $select.data('invoice-id');
                var newStatus = $select.val();
                var originalStatus = $select.data('original');
                
                $select.prop('disabled', true);
                
                $.ajax({
                    url: fs_ajax.ajax_url,
                    type: 'POST',
                    data: {
                        action: 'fs_update_invoice_status',
                        invoice_id: invoiceId,
                        status: newStatus,
                        nonce: fs_ajax.nonce
                    },
                    success: function(response) {
                        if (response.success) {
                            $select.data('original', newStatus);
                            // Update status badge class
                            $select.removeClass('status-' + originalStatus).addClass('status-' + newStatus);
                            // Brief visual feedback
                            $select.addClass('fs-status-updated');
                            setTimeout(function() {
                                $select.removeClass('fs-status-updated');
                            }, 1500);
                        } else {
                            alert(response.data.message || fs_ajax.strings.error);
                            $select.val(originalStatus);
                        }
                        $select.prop('disabled', false);
                    },
                    error: function() {
                        alert(fs_ajax.strings.error);
                        $select.val(originalStatus);
                        $select.prop('disabled', false);
                    }
                });
            });
        },

        // Initialize proposal form handlers
        initProposalForm: function() {
            var self = this;
            var $container = $('#fs-services-container');
            
            // Only initialize if container exists
            if (!$container.length) {
                return;
            }
            
            var serviceIndex = $container.find('.fs-service-item').length;
            
            // Add service button - unbind first to prevent duplicates
            $('.fs-add-service-btn').off('click').on('click', function(e) {
                e.preventDefault();
                self.addServiceItem(serviceIndex++);
            });
            
            // Remove service button (delegated)
            $container.off('click', '.fs-remove-service').on('click', '.fs-remove-service', function() {
                $(this).closest('.fs-service-item').slideUp(200, function() {
                    $(this).remove();
                    self.calculateServicesTotal();
                });
            });
            
            // Calculate amounts on input change (delegated)
            $container.off('input', '.fs-service-rate, .fs-service-quantity').on('input', '.fs-service-rate, .fs-service-quantity', function() {
                var $item = $(this).closest('.fs-service-item');
                self.calculateServiceAmount($item);
            });
            
            // v3.9.0 - Recalculate totals when retainer amount changes
            $container.off('input', '.fs-retainer-amount').on('input', '.fs-retainer-amount', function() {
                self.calculateServicesTotal();
            });
            
            // Pricing type change - show/hide hourly options
            $container.off('change', '.fs-service-pricing-type').on('change', '.fs-service-pricing-type', function() {
                var $item = $(this).closest('.fs-service-item');
                var type = $(this).val();
                
                if (type === 'hourly') {
                    $item.find('.fs-hourly-options').show();
                    $item.find('.fs-pricing-fields').show();
                    $item.find('.fs-retainer-service-fields').hide();
                    // Trigger hourly source change to set up correctly
                    $item.find('.fs-hourly-source').trigger('change');
                } else if (type === 'retainer') {
                    // v3.9.0 - Retainer as service pricing type
                    $item.find('.fs-hourly-options').hide();
                    $item.find('.fs-pricing-fields').hide();
                    $item.find('.fs-retainer-service-fields').show();
                    $item.find('.fs-hour-cap-fields').hide();
                    // Auto-set name if empty
                    var $nameInput = $item.find('.fs-service-name-input');
                    if (!$nameInput.val()) {
                        $nameInput.val('Monthly Retainer');
                    }
                    // Suggest monthly billing
                    if ($('#billing_frequency').val() === 'once') {
                        $('#billing_frequency').val('monthly');
                    }
                } else {
                    $item.find('.fs-hourly-options').hide();
                    $item.find('.fs-pricing-fields').show();
                    $item.find('.fs-retainer-service-fields').hide();
                    $item.find('.fs-hour-cap-fields').hide();
                    $item.find('.fs-qty-separator').show();
                    $item.find('.fs-service-quantity').show();
                    $item.find('.fs-tracked-hours-note').hide();
                }
                
                self.calculateServiceAmount($item);
                self.calculateServicesTotal();
            });
            
            // Hourly source change - toggle between estimated and tracked
            $container.off('change', '.fs-hourly-source').on('change', '.fs-hourly-source', function() {
                var $item = $(this).closest('.fs-service-item');
                var source = $(this).val();
                
                if (source === 'tracked') {
                    $item.find('.fs-qty-separator').hide();
                    $item.find('.fs-service-quantity').hide();
                    $item.find('.fs-tracked-hours-note').show();
                    $item.find('.fs-service-activity').show(); // Show activity dropdown
                    $item.find('.fs-hour-cap-fields').show(); // Show cap option
                    // Set amount to 0 for tracked - will be calculated from actual time
                    $item.find('.fs-service-amount').val('0.00');
                } else {
                    $item.find('.fs-qty-separator').show();
                    $item.find('.fs-service-quantity').show();
                    $item.find('.fs-tracked-hours-note').hide();
                    $item.find('.fs-service-activity').hide(); // Hide activity dropdown
                    $item.find('.fs-hour-cap-fields').hide(); // Hide cap option
                    self.calculateServiceAmount($item);
                }
                
                self.calculateServicesTotal();
            });
            
            // Hour cap checkbox toggle
            $container.off('change', '.fs-hour-cap-checkbox').on('change', '.fs-hour-cap-checkbox', function() {
                var $item = $(this).closest('.fs-service-item');
                var $options = $item.find('.fs-hour-cap-options');
                if ($(this).is(':checked')) {
                    $options.slideDown(200);
                    // Pre-fill overage rate from the service rate if empty
                    var $overageRate = $item.find('.fs-hour-cap-overage-rate');
                    if (!$overageRate.val()) {
                        $overageRate.attr('placeholder', $item.find('.fs-service-rate').val() || '');
                    }
                } else {
                    $options.slideUp(200);
                }
            });
            
            // Hour cap notify client checkbox - toggle contact select
            $container.off('change', '.fs-hour-cap-notify-checkbox').on('change', '.fs-hour-cap-notify-checkbox', function() {
                var $item = $(this).closest('.fs-service-item');
                var $select = $item.find('.fs-hour-cap-contact-select');
                if ($(this).is(':checked')) {
                    // Load contacts if select is empty (only has placeholder)
                    if ($select.find('option').length <= 1) {
                        var clientId = $('#client_id').val();
                        if (clientId) {
                            $.ajax({
                                url: typeof ajaxurl !== 'undefined' ? ajaxurl : (typeof fs_ajax !== 'undefined' ? fs_ajax.ajax_url : ''),
                                type: 'POST',
                                data: {
                                    action: 'fs_get_client_contacts',
                                    client_id: clientId,
                                    nonce: typeof fs_ajax !== 'undefined' ? fs_ajax.nonce : ''
                                },
                                success: function(response) {
                                    if (response.success) {
                                        var html = '';
                                        if (response.data.primary_email) {
                                            html += '<option value="primary">' + (response.data.client_name || 'Primary') + ' — ' + response.data.primary_email + '</option>';
                                        }
                                        if (response.data.contacts) {
                                            response.data.contacts.forEach(function(c) {
                                                if (c.email) {
                                                    html += '<option value="' + c.id + '">' + c.name + ' — ' + c.email + '</option>';
                                                }
                                            });
                                        }
                                        if (html) {
                                            $select.html(html);
                                        }
                                    }
                                }
                            });
                        }
                    }
                    $select.show();
                } else {
                    $select.hide();
                }
            });

            // Early warning threshold checkbox - toggle the % input
            $container.off('change', '.fs-hour-cap-warn-checkbox').on('change', '.fs-hour-cap-warn-checkbox', function() {
                var $fields = $(this).closest('.fs-hour-cap-warn-row').find('.fs-hour-cap-warn-fields');
                if ($(this).is(':checked')) {
                    $fields.slideDown(150);
                    var $input = $fields.find('.fs-hour-cap-warn-threshold');
                    if (!$input.val()) {
                        $input.val(80);
                    }
                } else {
                    $fields.slideUp(150);
                    $fields.find('.fs-hour-cap-warn-threshold').val('');
                }
            });
            
            // Initial calculation for existing items
            $container.find('.fs-service-item').each(function() {
                self.calculateServiceAmount($(this));
            });
            
            // Billing frequency toggle
            $('#billing_frequency').off('change').on('change', function() {
                if ($(this).val() === 'once') {
                    $('.fs-first-invoice-field').hide();
                } else {
                    $('.fs-first-invoice-field').show();
                }
            });
            
            // Deposit toggle
            $('#deposit_required').off('change').on('change', function() {
                if ($(this).is(':checked')) {
                    $('.fs-deposit-fields').slideDown(200);
                } else {
                    $('.fs-deposit-fields').slideUp(200);
                }
            });
            
            // Deposit type toggle
            $('#deposit_type').off('change').on('change', function() {
                var type = $(this).val();
                if (type === 'percentage') {
                    $('.fs-deposit-percentage-field').show();
                    $('.fs-deposit-amount-field').hide();
                } else {
                    $('.fs-deposit-percentage-field').hide();
                    $('.fs-deposit-amount-field').show();
                }
                self.updateDepositDisplay();
            });
            
            // Deposit amount inputs
            $('#deposit_percentage, #deposit_fixed_amount').off('input change').on('input change', function() {
                self.updateDepositDisplay();
            });
            
            // Load preset services dropdown
            self.loadPresetServicesDropdown();
            
            // Handle preset service selection
            $('#fs-load-preset-service').off('change').on('change', function() {
                var serviceId = $(this).val();
                if (!serviceId) return;
                
                var presetService = self.presetServices.find(function(s) { return s.id == serviceId; });
                if (presetService) {
                    self.addPresetService(presetService, serviceIndex++);
                }
                
                // Reset dropdown
                $(this).val('');
            });
        },
        
        // Preset services cache
        presetServices: [],
        
        // Load preset services into dropdown
        loadPresetServicesDropdown: function() {
            var self = this;
            var $dropdown = $('#fs-load-preset-service');
            
            if (!$dropdown.length) return;
            
            if (typeof fs_ajax === 'undefined') return;
            
            $.ajax({
                url: fs_ajax.ajax_url,
                type: 'POST',
                data: {
                    action: 'fs_get_preset_services',
                    nonce: fs_ajax.nonce
                },
                success: function(response) {
                    if (response.success && response.data.services) {
                        self.presetServices = response.data.services;
                        
                        $dropdown.find('option:not(:first)').remove();
                        
                        response.data.services.forEach(function(service) {
                            var label = service.name;
                            if (service.default_rate > 0) {
                                label += ' ($' + parseFloat(service.default_rate).toFixed(2) + ')';
                            }
                            $dropdown.append('<option value="' + service.id + '">' + label + '</option>');
                        });
                    }
                }
            });
        },
        
        // Add a preset service to the list
        addPresetService: function(preset, index) {
            var template = $('#fs-service-template').html();
            if (!template) return;
            
            template = template.replace(/\{\{index\}\}/g, index);
            
            var $item = $(template).hide();
            $('#fs-services-container').append($item);
            
            // Fill in preset values - check both class names for project vs proposal/contract forms
            var $nameInput = $item.find('.fs-service-name-input');
            if (!$nameInput.length) {
                $nameInput = $item.find('.fs-service-name');
            }
            $nameInput.val(preset.name);
            $item.find('.fs-service-description').val(preset.description || '');
            $item.find('.fs-service-pricing-type').val(preset.pricing_type || 'fixed');
            $item.find('.fs-service-rate').val(preset.default_rate || 0);
            $item.find('.fs-service-quantity').val(preset.default_quantity || 1);
            
            // Calculate amount
            var amount = (parseFloat(preset.default_rate) || 0) * (parseFloat(preset.default_quantity) || 1);
            $item.find('.fs-service-amount').val(amount.toFixed(2));
            
            $item.slideDown(200);
            
            this.calculateServicesTotal();
        },
        
        // Update deposit display amount
        updateDepositDisplay: function() {
            var total = parseFloat($('#hidden_fixed_price').val()) || 0;
            var type = $('#deposit_type').val();
            var depositAmount = 0;
            
            if ($('#deposit_required').is(':checked')) {
                if (type === 'percentage') {
                    var percentage = parseFloat($('#deposit_percentage').val()) || 0;
                    depositAmount = total * (percentage / 100);
                } else {
                    depositAmount = parseFloat($('#deposit_fixed_amount').val()) || 0;
                }
            }
            
            $('#deposit-amount-display').text('$' + depositAmount.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ','));
        },
        
        // Add a new service item
        addServiceItem: function(index) {
            var template = $('#fs-service-template').html();
            if (!template) return;
            
            template = template.replace(/\{\{index\}\}/g, index);
            
            var $item = $(template).hide();
            $('#fs-services-container').append($item);
            $item.slideDown(200);
            
            // Focus the name field
            $item.find('.fs-service-name-input').focus();
        },
        
        // Calculate single service amount
        calculateServiceAmount: function($item) {
            var pricingType = $item.find('.fs-service-pricing-type').val();
            var amount = 0;
            
            if (pricingType === 'retainer') {
                // For retainer, use the retainer_amount field
                amount = parseFloat($item.find('.fs-retainer-amount').val()) || 0;
            } else {
                // For fixed/hourly, calculate rate × quantity
                var rate = parseFloat($item.find('.fs-service-rate').val()) || 0;
                var quantity = parseFloat($item.find('.fs-service-quantity').val()) || 1;
                amount = rate * quantity;
            }
            
            $item.find('.fs-service-amount').val(amount.toFixed(2));
            
            this.calculateServicesTotal();
        },
        
        // Calculate services total (works for both proposals and projects)
        calculateServicesTotal: function() {
            var self = this;
            var total = 0;
            
            $('#fs-services-container .fs-service-item').each(function() {
                var pricingType = $(this).find('.fs-service-pricing-type').val();
                var amount = 0;
                
                if (pricingType === 'retainer') {
                    // For retainer services, use the retainer_amount
                    amount = parseFloat($(this).find('.fs-retainer-amount').val()) || 0;
                } else {
                    amount = parseFloat($(this).find('.fs-service-amount').val()) || 0;
                }
                total += amount;
            });
            
            var symbol = typeof fs_ajax !== 'undefined' ? fs_ajax.currency_symbol : '$';
            var formattedTotal = symbol + total.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
            
            // Update proposal total if exists
            $('#fs-proposal-total').text(formattedTotal);
            
            // Update project services total if exists
            $('#fs-services-total').text(formattedTotal);
            
            // Update contract sidebar total if exists
            $('#fs-sidebar-total').text(formattedTotal);
            
            // Update hidden fixed_price field for projects
            $('#hidden_fixed_price').val(total.toFixed(2));
            
            // Update deposit display if on project form
            this.updateDepositDisplay();
        },
        
        // Keep old method name for backwards compatibility
        calculateProposalTotal: function() {
            this.calculateServicesTotal();
        },

        // Delete proposal
        deleteProposal: function(id) {
            $.ajax({
                url: fs_ajax.ajax_url,
                type: 'POST',
                data: {
                    action: 'fs_delete_proposal',
                    id: id,
                    nonce: fs_ajax.nonce
                },
                success: function(response) {
                    if (response.success) {
                        window.location.href = fs_ajax.admin_url + 'admin.php?page=fs-proposals&message=deleted';
                    } else {
                        alert(response.data.message || fs_ajax.strings.error);
                    }
                },
                error: function() {
                    alert(fs_ajax.strings.error);
                }
            });
        },

        // Delete contract
        deleteContract: function(id) {
            $.ajax({
                url: fs_ajax.ajax_url,
                type: 'POST',
                data: {
                    action: 'fs_delete_contract',
                    id: id,
                    nonce: fs_ajax.nonce
                },
                success: function(response) {
                    if (response.success) {
                        window.location.href = fs_ajax.admin_url + 'admin.php?page=fs-contracts&message=deleted';
                    } else {
                        alert(response.data.message || fs_ajax.strings.error);
                    }
                },
                error: function() {
                    alert(fs_ajax.strings.error);
                }
            });
        },

        // Initialize settings signature pad
        initSettingsSignaturePad: function() {
            var canvas = document.getElementById('fs-contractor-signature-pad');
            var ctx = null;
            var isDrawing = false;
            var lastX = 0;
            var lastY = 0;
            var hasSignature = false;
            
            // Save signature (shared function for both draw and upload)
            function saveSignature(signatureData) {
                $('#contractor_signature').val(signatureData);
                
                // Show preview
                $('#fs-contractor-signature-preview img').attr('src', signatureData);
                $('#fs-contractor-signature-preview').show();
                $('#fs-signature-input-wrapper').hide();
            }
            
            // Handle file upload
            function handleFileUpload(file) {
                // Validate file type
                if (!file.type.match(/^image\/(png|jpeg|gif)$/)) {
                    alert('Please upload a PNG, JPEG, or GIF image.');
                    return;
                }
                
                // Validate file size (max 2MB)
                if (file.size > 2 * 1024 * 1024) {
                    alert('File size must be less than 2MB.');
                    return;
                }
                
                var reader = new FileReader();
                reader.onload = function(e) {
                    saveSignature(e.target.result);
                };
                reader.readAsDataURL(file);
            }
            
            // Tab switching - always register
            $(document).on('click', '.fs-signature-tab', function() {
                var tab = $(this).data('tab');
                
                // Update tab buttons
                $('.fs-signature-tab').removeClass('active');
                $(this).addClass('active');
                
                // Update tab content
                $('.fs-signature-tab-content').removeClass('active');
                $('#fs-signature-tab-' + tab).addClass('active');
            });
            
            // File upload - browse button - always register
            $(document).on('click', '#fs-signature-browse-btn', function(e) {
                e.preventDefault();
                $('#fs-signature-file-input').click();
            });
            
            // File upload - dropzone click - always register
            $(document).on('click', '#fs-signature-dropzone', function(e) {
                if (e.target.tagName !== 'BUTTON') {
                    $('#fs-signature-file-input').click();
                }
            });
            
            // File input change - always register
            $(document).on('change', '#fs-signature-file-input', function() {
                if (this.files && this.files[0]) {
                    handleFileUpload(this.files[0]);
                }
            });
            
            // Clear/remove saved signature - always register
            $(document).on('click', '#fs-clear-contractor-signature', function() {
                $('#contractor_signature').val('');
                $('#fs-contractor-signature-preview').hide();
                $('#fs-signature-input-wrapper').show();
                if (ctx && canvas) {
                    ctx.clearRect(0, 0, canvas.width, canvas.height);
                }
                hasSignature = false;
            });
            
            // File upload - drag and drop
            var dropzone = document.getElementById('fs-signature-dropzone');
            if (dropzone) {
                dropzone.addEventListener('dragover', function(e) {
                    e.preventDefault();
                    dropzone.classList.add('dragover');
                });
                
                dropzone.addEventListener('dragleave', function(e) {
                    e.preventDefault();
                    dropzone.classList.remove('dragover');
                });
                
                dropzone.addEventListener('drop', function(e) {
                    e.preventDefault();
                    dropzone.classList.remove('dragover');
                    
                    var files = e.dataTransfer.files;
                    if (files.length > 0) {
                        handleFileUpload(files[0]);
                    }
                });
            }
            
            // Canvas-specific code - only if canvas exists
            if (!canvas) return;
            
            ctx = canvas.getContext('2d');
            
            // Set canvas drawing properties
            ctx.strokeStyle = '#000';
            ctx.lineWidth = 2;
            ctx.lineCap = 'round';
            ctx.lineJoin = 'round';
            
            function getCoords(e) {
                var rect = canvas.getBoundingClientRect();
                var x, y;
                
                if (e.touches && e.touches.length > 0) {
                    x = e.touches[0].clientX - rect.left;
                    y = e.touches[0].clientY - rect.top;
                } else {
                    x = e.clientX - rect.left;
                    y = e.clientY - rect.top;
                }
                
                // Scale coordinates if canvas is resized via CSS
                x = x * (canvas.width / rect.width);
                y = y * (canvas.height / rect.height);
                
                return { x: x, y: y };
            }
            
            function startDrawing(e) {
                e.preventDefault();
                isDrawing = true;
                var coords = getCoords(e);
                lastX = coords.x;
                lastY = coords.y;
            }
            
            function draw(e) {
                if (!isDrawing) return;
                e.preventDefault();
                
                var coords = getCoords(e);
                
                ctx.beginPath();
                ctx.moveTo(lastX, lastY);
                ctx.lineTo(coords.x, coords.y);
                ctx.stroke();
                
                lastX = coords.x;
                lastY = coords.y;
                hasSignature = true;
            }
            
            function stopDrawing() {
                isDrawing = false;
            }
            
            // Mouse events
            canvas.addEventListener('mousedown', startDrawing);
            canvas.addEventListener('mousemove', draw);
            canvas.addEventListener('mouseup', stopDrawing);
            canvas.addEventListener('mouseout', stopDrawing);
            
            // Touch events
            canvas.addEventListener('touchstart', startDrawing);
            canvas.addEventListener('touchmove', draw);
            canvas.addEventListener('touchend', stopDrawing);
            
            // Clear signature pad
            $('#fs-clear-signature-pad').on('click', function() {
                ctx.clearRect(0, 0, canvas.width, canvas.height);
                hasSignature = false;
            });
            
            // Save signature from drawing
            $('#fs-save-signature').on('click', function() {
                if (!hasSignature) {
                    alert('Please draw your signature first.');
                    return;
                }
                
                var signatureData = canvas.toDataURL('image/png');
                saveSignature(signatureData);
            });
        },

        // Initialize send email modal
        initSendEmailModal: function() {
            var self = this;
            
            // Create modal HTML if not exists
            if (!$('#fs-send-email-modal').length) {
                var modalHtml = 
                    '<div id="fs-send-email-modal" class="fs-modal">' +
                        '<div class="fs-modal-content">' +
                            '<div class="fs-modal-header">' +
                                '<h2 class="fs-modal-title"></h2>' +
                                '<button type="button" class="fs-modal-close">&times;</button>' +
                            '</div>' +
                            '<div class="fs-modal-body">' +
                                '<div class="fs-form-group fs-send-to-section">' +
                                    '<label>' + (fs_ajax.strings.send_to_label || 'Send To') + '</label>' +
                                    '<div class="fs-contact-checkboxes-container">' +
                                        '<div class="fs-contact-loading">' + (fs_ajax.strings.loading || 'Loading contacts...') + '</div>' +
                                    '</div>' +
                                    '<p class="description">' + (fs_ajax.strings.send_to_description || 'Choose who receives the email directly (To) and who gets a copy (CC).') + '</p>' +
                                '</div>' +
                                '<div class="fs-form-group">' +
                                    '<label for="fs-custom-subject">' + (fs_ajax.strings.custom_subject_label || 'Email Subject') + '</label>' +
                                    '<input type="text" id="fs-custom-subject" class="fs-custom-subject-input regular-text" placeholder="' + (fs_ajax.strings.custom_subject_placeholder || 'Loading subject...') + '">' +
                                    '<p class="description">Edit to customize, or leave as-is to use your template default.</p>' +
                                '</div>' +
                                '<div class="fs-form-group">' +
                                    '<label for="fs-custom-message">' + (fs_ajax.strings.custom_message_label || 'Add a personal message (optional)') + '</label>' +
                                    '<textarea id="fs-custom-message" class="fs-custom-message-input" rows="5" placeholder="' + (fs_ajax.strings.custom_message_placeholder || 'Write a personal message to include with this email...') + '"></textarea>' +
                                    '<p class="description">This message will appear in the email before the main content.</p>' +
                                '</div>' +
                            '</div>' +
                            '<div class="fs-modal-footer">' +
                                '<button type="button" class="fs-btn fs-btn-secondary fs-modal-cancel">' + (fs_ajax.strings.cancel || 'Cancel') + '</button>' +
                                '<button type="button" class="fs-btn fs-btn-primary fs-modal-send">' + (fs_ajax.strings.send_email || 'Send Email') + '</button>' +
                            '</div>' +
                        '</div>' +
                    '</div>';
                $('body').append(modalHtml);
            }
            
            var $modal = $('#fs-send-email-modal');
            var $textarea = $('#fs-custom-message');
            var $subject = $('#fs-custom-subject');
            var $contactContainer = $modal.find('.fs-contact-checkboxes-container');
            var currentType = '';
            var currentId = 0;
            var currentClientId = 0;
            
            // Open modal on send button click
            $(document).on('click', '.fs-send-email-btn', function(e) {
                e.preventDefault();
                
                currentType = $(this).data('type');
                currentId = $(this).data('id');
                currentClientId = $(this).data('client-id') || 0;
                
                // Update modal title and button text based on type
                var title;
                if (currentType === 'invoice') {
                    title = fs_ajax.strings.send_invoice || 'Send Invoice';
                } else if (currentType === 'contract') {
                    title = fs_ajax.strings.send_contract || 'Send Contract';
                } else if (currentType === 'payment_receipt') {
                    title = 'Send Payment Receipt';
                } else {
                    title = fs_ajax.strings.send_proposal || 'Send Proposal';
                }
                
                $modal.find('.fs-modal-title').text(title);
                $modal.find('.fs-modal-send').text(title);
                
                // Clear previous inputs
                $textarea.val('');
                $subject.val('');
                
                // Fetch and populate the default subject line from template
                $.ajax({
                    url: fs_ajax.ajax_url,
                    type: 'POST',
                    data: {
                        action: 'fs_get_email_subject',
                        type: currentType,
                        id: currentId,
                        nonce: fs_ajax.nonce
                    },
                    success: function(response) {
                        if (response.success && response.data.subject) {
                            $subject.val(response.data.subject);
                        }
                    }
                });
                
                // Load contacts for client
                if (currentClientId) {
                    $contactContainer.html('<div class="fs-contact-loading">' + (fs_ajax.strings.loading || 'Loading contacts...') + '</div>');
                    $modal.find('.fs-send-to-section').show();
                    
                    $.ajax({
                        url: fs_ajax.ajax_url,
                        type: 'POST',
                        data: {
                            action: 'fs_get_client_contacts_for_send',
                            client_id: currentClientId,
                            nonce: fs_ajax.nonce
                        },
                        success: function(response) {
                            if (response.success && response.data.contacts) {
                                var html = '';
                                var contacts = response.data.contacts;
                                var primaryEmail = response.data.primary_email;
                                
                                // Add primary client email first
                                if (primaryEmail) {
                                    html += '<div class="fs-contact-send-row">' +
                                        '<div class="fs-contact-info-col">' +
                                            '<span class="fs-contact-name">' + (response.data.client_name || 'Primary Contact') + '</span>' +
                                            '<span class="fs-contact-email">' + primaryEmail + '</span>' +
                                            '<span class="fs-contact-badge primary">Primary</span>' +
                                        '</div>' +
                                        '<div class="fs-contact-action-col">' +
                                            '<label class="fs-send-role-label" title="Send directly to this contact"><input type="radio" name="send_to_primary" value="primary" checked> <span>To</span></label>' +
                                            '<label class="fs-send-role-label" title="CC this contact"><input type="checkbox" name="send_cc_contacts[]" value="primary" disabled> <span>CC</span></label>' +
                                        '</div>' +
                                    '</div>';
                                }
                                
                                // Add additional contacts
                                if (contacts && contacts.length > 0) {
                                    for (var i = 0; i < contacts.length; i++) {
                                        var contact = contacts[i];
                                        html += '<div class="fs-contact-send-row">' +
                                            '<div class="fs-contact-info-col">' +
                                                '<span class="fs-contact-name">' + (contact.name || '') + '</span>' +
                                                '<span class="fs-contact-email">' + (contact.email || '') + '</span>' +
                                                (contact.role ? '<span class="fs-contact-role">' + contact.role + '</span>' : '') +
                                            '</div>' +
                                            '<div class="fs-contact-action-col">' +
                                                '<label class="fs-send-role-label" title="Send directly to this contact"><input type="radio" name="send_to_primary" value="' + contact.id + '"> <span>To</span></label>' +
                                                '<label class="fs-send-role-label" title="CC this contact"><input type="checkbox" name="send_cc_contacts[]" value="' + contact.id + '" checked> <span>CC</span></label>' +
                                            '</div>' +
                                        '</div>';
                                    }
                                }
                                
                                if (!html) {
                                    html = '<p class="fs-no-contacts">' + (fs_ajax.strings.no_contacts || 'No contacts found. Invoice will be sent to client\'s primary email.') + '</p>';
                                }
                                
                                $contactContainer.html(html);
                            } else {
                                $contactContainer.html('<p class="fs-no-contacts">' + (fs_ajax.strings.no_contacts || 'No contacts found. Invoice will be sent to client\'s primary email.') + '</p>');
                            }
                        },
                        error: function() {
                            $contactContainer.html('<p class="fs-contact-error">' + (fs_ajax.strings.error_loading_contacts || 'Could not load contacts.') + '</p>');
                        }
                    });
                } else {
                    $modal.find('.fs-send-to-section').hide();
                }
                
                // Show modal
                $modal.addClass('active');
                $subject.focus();
            });
            
            // Close modal handlers
            $modal.on('click', '.fs-modal-close, .fs-modal-cancel', function() {
                $modal.removeClass('active');
            });
            
            // To/CC radio-checkbox interaction:
            // When "To" radio selected, disable that row's CC checkbox and enable the previous To row's CC
            $modal.on('change', 'input[name="send_to_primary"]', function() {
                var $row = $(this).closest('.fs-contact-send-row');
                // Enable all CC checkboxes first
                $modal.find('input[name="send_cc_contacts[]"]').prop('disabled', false);
                // Disable CC checkbox in the "To" row and uncheck it
                $row.find('input[name="send_cc_contacts[]"]').prop('disabled', true).prop('checked', false);
            });
            
            // Close on overlay click
            $modal.on('click', function(e) {
                if (e.target === this) {
                    $modal.removeClass('active');
                }
            });
            
            // Close on escape key
            $(document).on('keydown', function(e) {
                if (e.key === 'Escape' && $modal.hasClass('active')) {
                    $modal.removeClass('active');
                }
            });
            
            // Send email handler
            $modal.on('click', '.fs-modal-send', function() {
                var $btn = $(this);
                var customMessage = $textarea.val().trim();
                var customSubject = $subject.val().trim();
                var action, idField;
                
                // Collect selected contacts - primary "To" and CC contacts
                var primaryRecipient = $modal.find('input[name="send_to_primary"]:checked').val() || '';
                var ccContacts = [];
                $modal.find('input[name="send_cc_contacts[]"]:checked').each(function() {
                    ccContacts.push($(this).val());
                });
                
                if (currentType === 'invoice') {
                    action = 'fs_send_invoice_with_message';
                    idField = 'invoice_id';
                } else if (currentType === 'contract') {
                    action = 'fs_send_contract_with_message';
                    idField = 'contract_id';
                } else if (currentType === 'payment_receipt') {
                    action = 'fs_send_payment_receipt';
                    idField = 'invoice_id';
                } else {
                    action = 'fs_send_proposal_with_message';
                    idField = 'proposal_id';
                }
                
                // Disable button and show loading
                $btn.prop('disabled', true).text(fs_ajax.strings.sending || 'Sending...');
                
                var data = {
                    action: action,
                    nonce: fs_ajax.nonce,
                    custom_message: customMessage,
                    custom_subject: customSubject,
                    primary_recipient: primaryRecipient,
                    cc_contacts: ccContacts
                };
                data[idField] = currentId;
                
                $.ajax({
                    url: fs_ajax.ajax_url,
                    type: 'POST',
                    data: data,
                    success: function(response) {
                        if (response.success) {
                            // Redirect to the view page with success message
                            if (response.data.redirect_url) {
                                window.location.href = response.data.redirect_url;
                            } else {
                                window.location.reload();
                            }
                        } else {
                            alert(response.data.message || fs_ajax.strings.error);
                            var btnText;
                            if (currentType === 'invoice') {
                                btnText = fs_ajax.strings.send_invoice || 'Send Invoice';
                            } else if (currentType === 'contract') {
                                btnText = fs_ajax.strings.send_contract || 'Send Contract';
                            } else if (currentType === 'payment_receipt') {
                                btnText = 'Send Payment Receipt';
                            } else {
                                btnText = fs_ajax.strings.send_proposal || 'Send Proposal';
                            }
                            $btn.prop('disabled', false).text(btnText);
                        }
                    },
                    error: function() {
                        alert(fs_ajax.strings.error);
                        var btnText;
                        if (currentType === 'invoice') {
                            btnText = fs_ajax.strings.send_invoice || 'Send Invoice';
                        } else if (currentType === 'contract') {
                            btnText = fs_ajax.strings.send_contract || 'Send Contract';
                        } else if (currentType === 'payment_receipt') {
                            btnText = 'Send Payment Receipt';
                        } else {
                            btnText = fs_ajax.strings.send_proposal || 'Send Proposal';
                        }
                        $btn.prop('disabled', false).text(btnText);
                    }
                });
            });
        },

        // Initialize filter toggle
        initFilterToggle: function() {
            $(document).on('click', '.fs-filter-toggle', function(e) {
                e.preventDefault();
                var $panel = $(this).closest('.fs-wrap').find('.fs-filter-panel');
                $panel.slideToggle(200);
            });
        },

        // Initialize expense billback functionality
        initExpenseBillback: function() {
            var $section = $('.fs-expense-billback-section');
            if (!$section.length) return;

            var $markupInput = $('#expense_markup');
            var $expenseTable = $section.find('.fs-expense-table');
            var $selectAll = $('#select-all-expenses');

            // Calculate marked up amounts
            function calculateMarkup() {
                var markup = parseFloat($markupInput.val()) || 0;
                var totalBase = 0;
                var totalMarkedUp = 0;

                $expenseTable.find('tbody tr').each(function() {
                    var $row = $(this);
                    var $checkbox = $row.find('input[type="checkbox"]');
                    var baseAmount = parseFloat($row.find('.expense-base-amount').data('amount')) || 0;
                    var markedUpAmount = baseAmount * (1 + markup / 100);

                    $row.find('.expense-marked-up').text('$' + markedUpAmount.toFixed(2));

                    if ($checkbox.is(':checked')) {
                        $row.addClass('selected');
                        totalBase += baseAmount;
                        totalMarkedUp += markedUpAmount;
                    } else {
                        $row.removeClass('selected');
                    }
                });

                // Update totals display
                $('#expense-total-base').text('$' + totalBase.toFixed(2));
                $('#expense-total-markup').text('$' + totalMarkedUp.toFixed(2));
                
                // Update hidden input for form submission
                $('#expense_total_with_markup').val(totalMarkedUp.toFixed(2));
            }

            // Load expenses for a project via AJAX
            function loadProjectExpenses(projectId) {
                if (!projectId) {
                    $section.find('.fs-expense-table, .fs-expense-totals').remove();
                    if (!$section.find('.fs-expense-empty').length) {
                        $section.find('.fs-expense-options').after(
                            '<div class="fs-expense-empty"><p>' + (fs_ajax.strings.no_expenses || 'No billable expenses available for this project.') + '</p></div>'
                        );
                    }
                    return;
                }
                
                var invoiceId = $('input[name="invoice_id"]').val() || 0;
                
                $.ajax({
                    url: fs_ajax.ajax_url,
                    type: 'POST',
                    data: {
                        action: 'fs_get_project_expenses',
                        project_id: projectId,
                        invoice_id: invoiceId,
                        nonce: fs_ajax.nonce
                    },
                    success: function(response) {
                        if (response.success && response.data.expenses.length > 0) {
                            // Build the expense table HTML
                            var html = '<table class="widefat fs-table fs-expense-table"><thead><tr>' +
                                '<th class="check-column"><input type="checkbox" id="select-all-expenses"></th>' +
                                '<th>Date</th><th>Description</th><th>Category</th>' +
                                '<th class="text-right">Amount</th><th class="text-right">With Markup</th>' +
                                '</tr></thead><tbody>';
                            
                            $.each(response.data.expenses, function(i, exp) {
                                html += '<tr class="fs-expense-row' + (exp.included ? ' selected' : '') + '">' +
                                    '<td class="check-column"><input type="checkbox" name="expenses[]" value="' + exp.id + '"' + (exp.included ? ' checked' : '') + '></td>' +
                                    '<td>' + exp.date_formatted + '</td>' +
                                    '<td>' + exp.description + '</td>' +
                                    '<td>' + exp.category + '</td>' +
                                    '<td class="text-right expense-base-amount" data-amount="' + exp.amount + '">' + exp.amount_formatted + '</td>' +
                                    '<td class="text-right expense-marked-up">' + exp.amount_formatted + '</td>' +
                                    '</tr>';
                            });
                            
                            html += '</tbody></table>' +
                                '<div class="fs-expense-totals">' +
                                '<div class="fs-expense-total-item"><label>Base Total</label><span class="amount" id="expense-total-base">$0.00</span></div>' +
                                '<div class="fs-expense-total-item"><label>With Markup</label><span class="amount" id="expense-total-markup">$0.00</span></div>' +
                                '</div>' +
                                '<input type="hidden" name="expense_total_with_markup" id="expense_total_with_markup" value="0">';
                            
                            $section.find('.fs-expense-table, .fs-expense-totals, .fs-expense-empty, #expense_total_with_markup').remove();
                            $section.find('.fs-expense-options').after(html);
                            
                            // Re-bind elements
                            $expenseTable = $section.find('.fs-expense-table');
                            $selectAll = $('#select-all-expenses');
                            
                            // Rebind select all
                            $selectAll.off('change').on('change', function() {
                                var checked = $(this).is(':checked');
                                $expenseTable.find('tbody input[type="checkbox"]').prop('checked', checked);
                                calculateMarkup();
                            });
                            
                            calculateMarkup();
                        } else {
                            $section.find('.fs-expense-table, .fs-expense-totals, #expense_total_with_markup').remove();
                            if (!$section.find('.fs-expense-empty').length) {
                                $section.find('.fs-expense-options').after(
                                    '<div class="fs-expense-empty"><p>No billable expenses available for this project.</p>' +
                                    (projectId ? '<a href="' + fs_ajax.admin_url + 'admin.php?page=fs-finances&tab=expenses&action=add&project_id=' + projectId + '" class="button">Add Expense</a>' : '') +
                                    '</div>'
                                );
                            }
                        }
                    }
                });
            }

            // Events
            $markupInput.on('input change', calculateMarkup);
            
            $expenseTable.on('change', 'input[type="checkbox"]', function() {
                calculateMarkup();
                
                // Handle select all state
                var allChecked = $expenseTable.find('tbody input[type="checkbox"]:not(:checked)').length === 0;
                $selectAll.prop('checked', allChecked);
            });

            $selectAll.on('change', function() {
                var checked = $(this).is(':checked');
                $expenseTable.find('tbody input[type="checkbox"]').prop('checked', checked);
                calculateMarkup();
            });

            // Load expenses when project changes
            $('#project_id').on('change', function() {
                loadProjectExpenses($(this).val());
            });

            // Initial calculation
            calculateMarkup();
        },
        
        // Initialize keyboard shortcuts
        initKeyboardShortcuts: function() {
            // Check if shortcuts are enabled (check for data attribute or use default)
            var shortcutsEnabled = typeof fs_settings !== 'undefined' ? fs_settings.keyboard_shortcuts : true;
            
            if (!shortcutsEnabled) {
                return;
            }
            
            $(document).on('keydown', function(e) {
                // Only trigger if Alt key is pressed and not in an input field
                if (!e.altKey || $(e.target).is('input, textarea, select')) {
                    return;
                }
                
                switch(e.key.toLowerCase()) {
                    case 's': // Alt + S: Start/Stop timer
                        e.preventDefault();
                        var $toggleBtn = $('#fs-timer-toggle, #fs-stop-timer');
                        if ($toggleBtn.length) {
                            $toggleBtn.first().click();
                        } else {
                            // Try to submit the quick timer form
                            var $startForm = $('#fs-quick-timer-form');
                            if ($startForm.length) {
                                $startForm.submit();
                            }
                        }
                        break;
                        
                    case 'p': // Alt + P: Pause/Resume timer
                        e.preventDefault();
                        var $pauseBtn = $('#fs-pause-timer');
                        var $resumeBtn = $('#fs-resume-timer');
                        if ($pauseBtn.length) {
                            $pauseBtn.click();
                        } else if ($resumeBtn.length) {
                            $resumeBtn.click();
                        }
                        break;
                        
                    case 't': // Alt + T: Go to Time Tracking
                        e.preventDefault();
                        window.location.href = ajaxurl.replace('admin-ajax.php', 'admin.php?page=fs-time-tracking');
                        break;
                        
                    case 'd': // Alt + D: Go to Dashboard
                        e.preventDefault();
                        window.location.href = ajaxurl.replace('admin-ajax.php', 'admin.php?page=freelance-studio');
                        break;
                        
                    case '/': // Alt + /: Show shortcuts help
                        e.preventDefault();
                        FS_Admin.toggleShortcutsHelp();
                        break;
                }
            });
            
            // Add shortcuts help overlay to body if it doesn't exist
            if (!$('#fs-shortcuts-help').length) {
                var isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
                var modKey = isMac ? '⌥' : 'Alt';
                var modLabel = isMac ? 'Option' : 'Alt';
                
                var helpHtml = '<div id="fs-shortcuts-help" class="fs-shortcuts-help">' +
                    '<h4>Keyboard Shortcuts <span class="fs-platform-note">(' + (isMac ? 'Mac' : 'PC') + ')</span></h4>' +
                    '<div class="fs-shortcut-item"><span>Start/Stop Timer</span><span class="fs-shortcut-key">' + modKey + ' + S</span></div>' +
                    '<div class="fs-shortcut-item"><span>Pause/Resume</span><span class="fs-shortcut-key">' + modKey + ' + P</span></div>' +
                    '<div class="fs-shortcut-item"><span>Time Tracking</span><span class="fs-shortcut-key">' + modKey + ' + T</span></div>' +
                    '<div class="fs-shortcut-item"><span>Dashboard</span><span class="fs-shortcut-key">' + modKey + ' + D</span></div>' +
                    '<div class="fs-shortcut-item"><span>Show/Hide Help</span><span class="fs-shortcut-key">' + modKey + ' + /</span></div>' +
                    '</div>';
                $('body').append(helpHtml);
            }
        },
        
        toggleShortcutsHelp: function() {
            $('#fs-shortcuts-help').toggleClass('visible');
            
            // Auto-hide after 5 seconds
            if ($('#fs-shortcuts-help').hasClass('visible')) {
                setTimeout(function() {
                    $('#fs-shortcuts-help').removeClass('visible');
                }, 5000);
            }
        },
        
        // Initialize browser notifications
        initBrowserNotifications: function() {
            var self = this;
            
            // Check if notifications are enabled
            var notificationsEnabled = typeof fs_settings !== 'undefined' ? fs_settings.browser_notifications : true;
            
            if (!notificationsEnabled || !('Notification' in window)) {
                return;
            }
            
            // Request permission if not already granted
            if (Notification.permission === 'default') {
                // Don't request immediately, wait for user interaction
                $(document).one('click', function() {
                    Notification.requestPermission();
                });
            }
            
            // Test notification button in settings
            $('#fs-test-notification').on('click', function() {
                self.sendNotification(
                    'Freelance Studio',
                    'Notifications are working! You\'ll be notified about your timer.'
                );
            });
            
            // Check for running timer and set up warnings
            var $runningTimerId = $('#running-timer-id, #fs-running-timer-id');
            if ($runningTimerId.length && $runningTimerId.val()) {
                self.setupTimerWarnings();
            }
            
            // Page visibility change - remind about running timer
            document.addEventListener('visibilitychange', function() {
                if (document.visibilityState === 'visible') {
                    // User returned to the page, check if timer is still running
                    var $runningTimer = $('#running-timer-id, #fs-running-timer-id');
                    if ($runningTimer.length && $runningTimer.val()) {
                        // Timer is running - they might have forgotten
                    }
                }
            });
        },
        
        setupTimerWarnings: function() {
            var self = this;
            var warningHours = typeof fs_settings !== 'undefined' ? (fs_settings.timer_warning_hours || 8) : 8;
            var warningMs = warningHours * 60 * 60 * 1000;
            var warningShown = false;
            
            // Get timer start time
            var $startTime = $('#timer-start-time, #fs-running-timer-start');
            if (!$startTime.length) return;
            
            var startTimestamp = parseInt($startTime.val()) * 1000;
            var now = Date.now();
            var elapsed = now - startTimestamp;
            
            // If already past warning time, show immediately
            if (elapsed >= warningMs && !warningShown) {
                self.sendNotification(
                    'Timer Running Long',
                    'Your timer has been running for over ' + warningHours + ' hours. Did you forget to stop it?'
                );
                warningShown = true;
            } else {
                // Set timeout for when we hit the warning time
                var timeUntilWarning = warningMs - elapsed;
                if (timeUntilWarning > 0) {
                    setTimeout(function() {
                        if (!warningShown) {
                            self.sendNotification(
                                'Timer Running Long',
                                'Your timer has been running for ' + warningHours + ' hours. Did you forget to stop it?'
                            );
                            warningShown = true;
                        }
                    }, timeUntilWarning);
                }
            }
            
            // Also check every hour after the initial warning
            setInterval(function() {
                var $runningTimer = $('#running-timer-id, #fs-running-timer-id');
                if ($runningTimer.length && $runningTimer.val()) {
                    var currentElapsed = Date.now() - startTimestamp;
                    var hours = Math.floor(currentElapsed / (60 * 60 * 1000));
                    if (hours > warningHours && hours % 2 === 0) { // Every 2 hours after warning
                        self.sendNotification(
                            'Timer Still Running',
                            'Your timer has been running for ' + hours + ' hours.'
                        );
                    }
                }
            }, 60 * 60 * 1000); // Check every hour
        },
        
        sendNotification: function(title, body) {
            if (!('Notification' in window)) {
                return;
            }
            
            if (Notification.permission === 'granted') {
                var notification = new Notification(title, {
                    body: body,
                    icon: typeof fs_settings !== 'undefined' ? fs_settings.plugin_url + '/admin/images/icon.png' : '',
                    badge: typeof fs_settings !== 'undefined' ? fs_settings.plugin_url + '/admin/images/badge.png' : '',
                    tag: 'fs-timer-notification',
                    requireInteraction: false
                });
                
                notification.onclick = function() {
                    window.focus();
                    notification.close();
                };
                
                // Auto close after 10 seconds
                setTimeout(function() {
                    notification.close();
                }, 10000);
            } else if (Notification.permission === 'default') {
                Notification.requestPermission().then(function(permission) {
                    if (permission === 'granted') {
                        FS_Admin.sendNotification(title, body);
                    }
                });
            }
        },

        // Initialize Google Calendar settings handlers
        initGoogleCalendarSettings: function() {
            // Copy to clipboard button
            $(document).on('click', '.fs-copy-btn', function(e) {
                e.preventDefault();
                var textToCopy = $(this).data('copy');
                
                if (navigator.clipboard && navigator.clipboard.writeText) {
                    navigator.clipboard.writeText(textToCopy).then(function() {
                        FS_Admin.showNotice('Copied to clipboard!', 'success');
                    });
                } else {
                    // Fallback for older browsers
                    var tempInput = $('<input>');
                    $('body').append(tempInput);
                    tempInput.val(textToCopy).select();
                    document.execCommand('copy');
                    tempInput.remove();
                    FS_Admin.showNotice('Copied to clipboard!', 'success');
                }
            });
            
            // Email template variable click to copy
            $(document).on('click', '.fs-variable-tag', function(e) {
                e.preventDefault();
                var $tag = $(this);
                var variable = $tag.data('variable') || $tag.text();
                
                if (navigator.clipboard && navigator.clipboard.writeText) {
                    navigator.clipboard.writeText(variable).then(function() {
                        // Visual feedback
                        var originalText = $tag.text();
                        $tag.text('Copied!').addClass('copied');
                        setTimeout(function() {
                            $tag.text(originalText).removeClass('copied');
                        }, 1500);
                    });
                } else {
                    // Fallback for older browsers
                    var tempInput = $('<input>');
                    $('body').append(tempInput);
                    tempInput.val(variable).select();
                    document.execCommand('copy');
                    tempInput.remove();
                    
                    // Visual feedback
                    var originalText = $tag.text();
                    $tag.text('Copied!').addClass('copied');
                    setTimeout(function() {
                        $tag.text(originalText).removeClass('copied');
                    }, 1500);
                }
            });

            // Toggle password visibility
            $(document).on('click', '.fs-toggle-password', function(e) {
                e.preventDefault();
                var input = $(this).prev('input');
                var icon = $(this).find('.dashicons');
                
                if (input.attr('type') === 'password') {
                    input.attr('type', 'text');
                    icon.removeClass('dashicons-visibility').addClass('dashicons-hidden');
                } else {
                    input.attr('type', 'password');
                    icon.removeClass('dashicons-hidden').addClass('dashicons-visibility');
                }
            });

            // Full sync button
            $(document).on('click', '#fs-gcal-full-sync-btn', function(e) {
                e.preventDefault();
                var $btn = $(this);
                var originalText = $btn.html();
                
                $btn.prop('disabled', true).html('<span class="dashicons dashicons-update spin"></span> Syncing...');
                
                $.ajax({
                    url: fs_ajax.ajax_url,
                    type: 'POST',
                    data: {
                        action: 'fs_gcal_full_sync',
                        nonce: fs_ajax.nonce
                    },
                    success: function(response) {
                        if (response.success) {
                            var synced = response.data.synced;
                            var message = 'Synced: ' + 
                                synced.projects + ' projects, ' +
                                synced.milestones + ' milestones, ' +
                                synced.tasks + ' tasks, ' +
                                synced.invoices + ' invoices, ' +
                                synced.contracts + ' contracts, ' +
                                synced.proposals + ' proposals';
                            FS_Admin.showNotice(message, 'success');
                        } else {
                            FS_Admin.showNotice(response.data.message || 'Sync failed', 'error');
                        }
                    },
                    error: function() {
                        FS_Admin.showNotice('Sync request failed', 'error');
                    },
                    complete: function() {
                        $btn.prop('disabled', false).html(originalText);
                    }
                });
            });

            // Inbound sync from Google Calendar button
            $(document).on('click', '#fs-gcal-inbound-sync-btn', function(e) {
                e.preventDefault();
                var $btn = $(this);
                var originalText = $btn.html();
                
                $btn.prop('disabled', true).html('<span class="dashicons dashicons-update spin"></span> Syncing from Google...');
                
                $.ajax({
                    url: fs_ajax.ajax_url,
                    type: 'POST',
                    data: {
                        action: 'fs_gcal_inbound_sync',
                        nonce: fs_ajax.nonce
                    },
                    success: function(response) {
                        if (response.success) {
                            FS_Admin.showNotice(response.data.message, 'success');
                            // Reload page after short delay to show updated last sync time
                            setTimeout(function() {
                                location.reload();
                            }, 1500);
                        } else {
                            FS_Admin.showNotice(response.data.message || 'Sync failed', 'error');
                        }
                    },
                    error: function() {
                        FS_Admin.showNotice('Sync request failed', 'error');
                    },
                    complete: function() {
                        $btn.prop('disabled', false).html(originalText);
                    }
                });
            });

            // Preview digest button
            $(document).on('click', '#fs-preview-digest-btn', function(e) {
                e.preventDefault();
                var $btn = $(this);
                var originalText = $btn.html();
                
                $btn.prop('disabled', true).html('<span class="dashicons dashicons-update spin"></span> Loading...');
                
                $.ajax({
                    url: fs_ajax.ajax_url,
                    type: 'POST',
                    data: {
                        action: 'fs_gcal_preview_digest',
                        nonce: fs_ajax.nonce
                    },
                    success: function(response) {
                        if (response.success) {
                            $('#fs-digest-preview-content').html(response.data.html);
                            $('#fs-digest-preview-modal').addClass('active');
                        } else {
                            FS_Admin.showNotice('Failed to load preview', 'error');
                        }
                    },
                    error: function() {
                        FS_Admin.showNotice('Request failed', 'error');
                    },
                    complete: function() {
                        $btn.prop('disabled', false).html(originalText);
                    }
                });
            });

            // Send test digest button
            $(document).on('click', '#fs-send-test-digest-btn', function(e) {
                e.preventDefault();
                var $btn = $(this);
                var originalText = $btn.html();
                
                $btn.prop('disabled', true).html('<span class="dashicons dashicons-update spin"></span> Sending...');
                
                $.ajax({
                    url: fs_ajax.ajax_url,
                    type: 'POST',
                    data: {
                        action: 'fs_gcal_send_test_digest',
                        nonce: fs_ajax.nonce
                    },
                    success: function(response) {
                        if (response.success) {
                            FS_Admin.showNotice(response.data.message, 'success');
                        } else {
                            FS_Admin.showNotice(response.data.message || 'Failed to send', 'error');
                        }
                    },
                    error: function() {
                        FS_Admin.showNotice('Request failed', 'error');
                    },
                    complete: function() {
                        $btn.prop('disabled', false).html(originalText);
                    }
                });
            });

            // Close modal
            $(document).on('click', '.fs-modal-close, .fs-modal-cancel, .fs-modal', function(e) {
                if ($(this).hasClass('fs-modal-cancel') || $(this).hasClass('fs-modal-close') || e.target === this) {
                    $('.fs-modal').removeClass('active');
                }
            });

            // Close on escape key
            $(document).on('keydown', function(e) {
                if (e.key === 'Escape') {
                    $('.fs-modal').removeClass('active');
                }
            });
        },

        // Show notice helper
        showNotice: function(message, type) {
            type = type || 'success';
            var $notice = $('<div class="notice notice-' + type + ' is-dismissible"><p>' + message + '</p></div>');
            
            // Remove existing notices
            $('.wrap > .notice').remove();
            
            // Add new notice
            $('.wrap h1').first().after($notice);
            
            // Make it dismissible
            $notice.find('.notice-dismiss').on('click', function() {
                $notice.fadeOut(200, function() { $(this).remove(); });
            });

            // Auto dismiss after 5 seconds
            setTimeout(function() {
                $notice.fadeOut(200, function() { $(this).remove(); });
            }, 5000);
        },
        
        // Initialize Quick Add Time Entry Form
        initQuickAddForm: function() {
            var $form = $('#fs-quickadd-form');
            if (!$form.length) return;
            
            // Form submission
            $form.on('submit', function(e) {
                e.preventDefault();
                
                var hours = parseInt($('#quickadd-hours').val()) || 0;
                var minutes = parseInt($('#quickadd-minutes').val()) || 0;
                var duration = (hours * 3600) + (minutes * 60);
                
                if (duration <= 0) {
                    alert('Please enter a duration');
                    return;
                }
                
                var $btn = $(this).find('button[type="submit"]');
                var originalHtml = $btn.html();
                $btn.prop('disabled', true).html('<span class="dashicons dashicons-update-alt spin"></span> Adding...');
                
                var nonce = typeof fs_ajax !== 'undefined' ? fs_ajax.nonce : '';
                
                $.ajax({
                    url: fs_ajax.ajax_url,
                    type: 'POST',
                    data: {
                        action: 'fs_save_time_entry',
                        nonce: nonce,
                        project_id: $('#quickadd-project').val(),
                        task_id: $('#quickadd-task').val(),
                        activity: $('#quickadd-activity').val(),
                        description: $('#quickadd-description').val(),
                        start_date: $('#quickadd-date').val(),
                        duration: duration,
                        duration_hours: hours,
                        duration_minutes: minutes,
                        hourly_rate: $('#quickadd-rate').val(),
                        billable: $('#quickadd-billable').is(':checked') ? 1 : 0
                    },
                    success: function(response) {
                        if (response.success) {
                            // Reset form
                            $('#quickadd-description').val('');
                            $('#quickadd-hours').val('0');
                            $('#quickadd-minutes').val('0');
                            // Reload to show new entry
                            location.reload();
                        } else {
                            alert(response.data || 'Error saving entry');
                            $btn.prop('disabled', false).html(originalHtml);
                        }
                    },
                    error: function() {
                        alert('Error saving entry');
                        $btn.prop('disabled', false).html(originalHtml);
                    }
                });
            });
            
            // Update hourly rate when project changes
            $('#quickadd-project').on('change', function() {
                var rate = $(this).find(':selected').data('rate');
                if (rate) {
                    $('#quickadd-rate').val(rate);
                }
            });
            
            // Task/Project sync for quickadd - when task is selected, auto-select its project
            // Skip when in project context (project is locked via hidden input)
            $(document).on('change', '#quickadd-task', function() {
                var $card = $(this).closest('.fs-quickadd-card');
                if ($card.data('context') === 'project') return;
                var selectedOption = $(this).find('option:selected');
                var projectId = selectedOption.data('project');
                if (projectId && $('#quickadd-project').length) {
                    $('#quickadd-project').val(projectId);
                }
            });
            
            // Task/Project sync for timer
            // Skip when in project context (project is already pre-selected)
            $(document).on('change', '#fs-timer-task', function() {
                var $card = $(this).closest('.fs-timer-card');
                if ($card.data('context') === 'project') return;
                var selectedOption = $(this).find('option:selected');
                var projectId = selectedOption.data('project');
                if (projectId && $('#fs-timer-project').length) {
                    $('#fs-timer-project').val(projectId);
                }
            });
        },
        
        // Dashboard mini calendar navigation
        initDashboardMiniCalendar: function() {
            var monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 
                              'July', 'August', 'September', 'October', 'November', 'December'];
            
            $(document).on('click', '.fs-dashboard-cal-nav', function(e) {
                e.preventDefault();
                
                var $btn = $(this);
                var $calendar = $btn.closest('.fs-dashboard-mini-cal');
                var direction = $btn.data('direction');
                var year = parseInt($calendar.data('year'));
                var month = parseInt($calendar.data('month'));
                
                // Calculate new month/year
                if (direction === 'prev') {
                    month--;
                    if (month < 1) { month = 12; year--; }
                } else {
                    month++;
                    if (month > 12) { month = 1; year++; }
                }
                
                // Show loading state
                $calendar.css('opacity', '0.5');
                
                // AJAX request to get new calendar data
                $.ajax({
                    url: fs_ajax.ajax_url,
                    type: 'POST',
                    data: {
                        action: 'fs_get_mini_calendar',
                        nonce: fs_ajax.nonce,
                        year: year,
                        month: month
                    },
                    success: function(response) {
                        if (response.success && response.data.html) {
                            $calendar.replaceWith(response.data.html);
                        }
                        $calendar.css('opacity', '1');
                    },
                    error: function() {
                        $calendar.css('opacity', '1');
                    }
                });
            });
        }
    };

    // Expose to global scope
    window.FS_Admin = FS_Admin;

    // Initialize Google Calendar settings if on that page
    $(document).ready(function() {
        if ($('.fs-gcal-status-card').length) {
            FS_Admin.initGoogleCalendarSettings();
        }
        
        // Initialize dashboard mini calendar if present
        if ($('.fs-dashboard-mini-cal').length) {
            FS_Admin.initDashboardMiniCalendar();
        }
        
        // Initialize Template Library
        FS_Admin.initTemplateLibrary();
    });
    
    // Template Library functionality
    FS_Admin.initTemplateLibrary = function() {
        var $modal = $('#fs-template-library-modal');
        
        if (!$modal.length) return;
        
        // Open modal
        $('#fs-open-template-library').on('click', function() {
            $modal.addClass('active').show();
        });
        
        // Close modal
        $modal.on('click', '.fs-modal-close, .fs-modal-close-btn', function() {
            $modal.removeClass('active').hide();
        });
        
        // Close on backdrop click
        $modal.on('click', function(e) {
            if (e.target === this) {
                $modal.removeClass('active').hide();
            }
        });
        
        // Import selected templates
        $('#fs-import-templates-btn').on('click', function() {
            var $btn = $(this);
            var selected = [];
            
            $modal.find('input[name="import_templates[]"]:checked').each(function() {
                selected.push($(this).val());
            });
            
            if (selected.length === 0) {
                alert('Please select at least one template to import.');
                return;
            }
            
            $btn.prop('disabled', true).text('Importing...');
            
            $.ajax({
                url: fs_ajax.ajax_url,
                type: 'POST',
                data: {
                    action: 'fs_import_library_template',
                    nonce: fs_ajax.nonce,
                    slugs: selected
                },
                success: function(response) {
                    if (response.success) {
                        alert(response.data.message);
                        location.reload();
                    } else {
                        alert('Error: ' + (response.data || 'Failed to import templates'));
                        $btn.prop('disabled', false).text('Import Selected');
                    }
                },
                error: function() {
                    alert('An error occurred. Please try again.');
                    $btn.prop('disabled', false).text('Import Selected');
                }
            });
        });
    };

    // ============================================
    // v3.8.0 - Batch Invoice Actions
    // ============================================
    $(document).ready(function() {
        var selectedInvoices = [];
        
        // Select all checkbox
        $('#fs-select-all-invoices').on('change', function() {
            var isChecked = $(this).is(':checked');
            $('.fs-invoice-checkbox').prop('checked', isChecked);
            updateSelectedInvoices();
        });
        
        // Individual checkbox
        $(document).on('change', '.fs-invoice-checkbox', function() {
            updateSelectedInvoices();
            
            // Update "select all" checkbox
            var total = $('.fs-invoice-checkbox').length;
            var checked = $('.fs-invoice-checkbox:checked').length;
            $('#fs-select-all-invoices').prop('checked', total === checked);
        });
        
        function updateSelectedInvoices() {
            selectedInvoices = [];
            $('.fs-invoice-checkbox:checked').each(function() {
                selectedInvoices.push($(this).val());
            });
            
            $('#fs-selected-count').text(selectedInvoices.length);
            
            if (selectedInvoices.length > 0) {
                $('#fs-batch-actions').slideDown(200);
            } else {
                $('#fs-batch-actions').slideUp(200);
            }
        }
        
        // Clear selection
        $('#fs-batch-clear').on('click', function() {
            $('.fs-invoice-checkbox, #fs-select-all-invoices').prop('checked', false);
            updateSelectedInvoices();
        });
        
        // Open batch mark paid modal
        $('#fs-batch-mark-paid').on('click', function() {
            if (selectedInvoices.length === 0) return;
            $('#fs-batch-paid-modal').fadeIn(200);
        });
        
        // Close modal
        $('#fs-batch-paid-modal .fs-modal-close').on('click', function() {
            $('#fs-batch-paid-modal').fadeOut(200);
        });
        
        // Confirm batch mark paid
        $('#fs-confirm-batch-paid').on('click', function() {
            var $btn = $(this);
            var paidDate = $('#fs-batch-paid-date').val();
            
            $btn.prop('disabled', true).text('Processing...');
            
            $.ajax({
                url: fs_ajax.ajax_url,
                type: 'POST',
                data: {
                    action: 'fs_batch_mark_invoices_paid',
                    nonce: fs_ajax.nonce,
                    invoice_ids: selectedInvoices,
                    paid_date: paidDate
                },
                success: function(response) {
                    if (response.success) {
                        location.reload();
                    } else {
                        alert('Error: ' + (response.data.message || 'Failed to update invoices'));
                        $btn.prop('disabled', false).text('Mark as Paid');
                    }
                },
                error: function() {
                    alert('An error occurred. Please try again.');
                    $btn.prop('disabled', false).text('Mark as Paid');
                }
            });
        });
    });

    // ============================================
    // v3.8.0 - Duplicate Project
    // ============================================
    $(document).on('click', '.fs-duplicate-project', function(e) {
        e.preventDefault();
        
        var projectId = $(this).data('project-id');
        
        if (!confirm('This will create a copy of this project including all tasks and milestones. Continue?')) {
            return;
        }
        
        var $link = $(this);
        $link.text('Duplicating...');
        
        $.ajax({
            url: fs_ajax.ajax_url,
            type: 'POST',
            data: {
                action: 'fs_duplicate_project',
                nonce: fs_ajax.nonce,
                project_id: projectId
            },
            success: function(response) {
                if (response.success) {
                    window.location.href = response.data.redirect_url;
                } else {
                    alert('Error: ' + (response.data.message || 'Failed to duplicate project'));
                    $link.text('Duplicate Project');
                }
            },
            error: function() {
                alert('An error occurred. Please try again.');
                $link.text('Duplicate Project');
            }
        });
    });

    // ============================================
    // v3.8.0 - CSV Export Handlers
    // ============================================
    $(document).on('click', '.fs-export-btn', function() {
        var $btn = $(this);
        var type = $btn.data('type');
        var year = $btn.data('year');
        var originalText = $btn.html();
        
        $btn.prop('disabled', true).html('<span class="dashicons dashicons-update spin"></span> Exporting...');
        
        $.ajax({
            url: fs_ajax.ajax_url,
            type: 'POST',
            data: {
                action: 'fs_export_csv',
                nonce: fs_ajax.nonce,
                type: type,
                year: year
            },
            success: function(response) {
                if (response.success) {
                    // Create download link
                    var blob = new Blob([response.data.csv], { type: 'text/csv;charset=utf-8;' });
                    var link = document.createElement('a');
                    link.href = URL.createObjectURL(blob);
                    link.download = response.data.filename;
                    document.body.appendChild(link);
                    link.click();
                    document.body.removeChild(link);
                } else {
                    alert('Error: ' + (response.data.message || 'Export failed'));
                }
                $btn.prop('disabled', false).html(originalText);
            },
            error: function() {
                alert('An error occurred. Please try again.');
                $btn.prop('disabled', false).html(originalText);
            }
        });
    });

    // Export invoices from invoices page
    $('#fs-export-invoices-btn').on('click', function() {
        var $btn = $(this);
        var originalText = $btn.html();
        var year = new Date().getFullYear();
        
        $btn.prop('disabled', true).html('<span class="dashicons dashicons-update spin"></span> Exporting...');
        
        $.ajax({
            url: fs_ajax.ajax_url,
            type: 'POST',
            data: {
                action: 'fs_export_csv',
                nonce: fs_ajax.nonce,
                type: 'invoices',
                year: year
            },
            success: function(response) {
                if (response.success) {
                    var blob = new Blob([response.data.csv], { type: 'text/csv;charset=utf-8;' });
                    var link = document.createElement('a');
                    link.href = URL.createObjectURL(blob);
                    link.download = response.data.filename;
                    document.body.appendChild(link);
                    link.click();
                    document.body.removeChild(link);
                } else {
                    alert('Error: ' + (response.data.message || 'Export failed'));
                }
                $btn.prop('disabled', false).html(originalText);
            },
            error: function() {
                alert('An error occurred. Please try again.');
                $btn.prop('disabled', false).html(originalText);
            }
        });
    });

    // ============================================
    // v3.8.0 - 1099 PDF Generation
    // ============================================
    $('#fs-generate-1099-pdf').on('click', function() {
        var $btn = $(this);
        var year = $btn.data('year');
        var originalText = $btn.html();
        
        $btn.prop('disabled', true).html('<span class="dashicons dashicons-update spin"></span> Generating...');
        
        $.ajax({
            url: fs_ajax.ajax_url,
            type: 'POST',
            data: {
                action: 'fs_generate_1099_pdf',
                nonce: fs_ajax.nonce,
                year: year
            },
            success: function(response) {
                if (response.success) {
                    // Open PDF in new tab
                    window.open(response.data.pdf_url, '_blank');
                } else {
                    alert(response.data.message || 'No clients with payments over $600 found.');
                }
                $btn.prop('disabled', false).html(originalText);
            },
            error: function() {
                alert('An error occurred. Please try again.');
                $btn.prop('disabled', false).html(originalText);
            }
        });
    });

    // ============================================
    // v4.0.13 - Proposal Custom Sections
    // ============================================
    
    // Track custom section index
    var customSectionIndex = $('.fs-custom-section-item').length;
    
    // Helper function to initialize TinyMCE on a textarea
    function initSectionEditor(textareaId) {
        if (typeof tinymce !== 'undefined' && typeof wp !== 'undefined' && wp.editor) {
            // Use wp.editor.initialize for proper WordPress integration
            wp.editor.initialize(textareaId, {
                tinymce: {
                    wpautop: true,
                    plugins: 'lists link paste',
                    toolbar1: 'bold,italic,bullist,numlist,link,unlink',
                    height: 200
                },
                quicktags: {
                    buttons: 'strong,em,link,ul,ol,li'
                },
                mediaButtons: false
            });
        }
    }
    
    // Add custom section
    $(document).on('click', '.fs-add-custom-section-btn', function() {
        var position = $(this).data('position');
        var template = $('#fs-custom-section-template').html();
        var id = 'section_' + Date.now();
        var textareaId = 'section_editor_' + customSectionIndex;
        
        template = template.replace(/\{\{index\}\}/g, customSectionIndex);
        template = template.replace(/\{\{id\}\}/g, id);
        template = template.replace(/\{\{position\}\}/g, position);
        template = template.replace(/\{\{title\}\}/g, '');
        template = template.replace(/\{\{content\}\}/g, '');
        template = template.replace(/\{\{editor_id\}\}/g, textareaId);
        
        var $container = $('.fs-custom-sections-list[data-position="' + position + '"]');
        $container.append(template);
        
        // Initialize TinyMCE on the new textarea
        setTimeout(function() {
            initSectionEditor(textareaId);
        }, 100);
        
        // Focus on the title field
        $container.find('.fs-custom-section-item:last .fs-custom-section-title').focus();
        
        customSectionIndex++;
    });
    
    // Remove custom section
    $(document).on('click', '.fs-remove-custom-section', function() {
        if (confirm('Are you sure you want to remove this section?')) {
            var $section = $(this).closest('.fs-custom-section-item');
            var $textarea = $section.find('textarea');
            var textareaId = $textarea.attr('id');
            
            // Remove TinyMCE instance if exists
            if (typeof tinymce !== 'undefined' && tinymce.get(textareaId)) {
                wp.editor.remove(textareaId);
            }
            
            $section.remove();
        }
    });
    
    // Insert section from template
    $(document).on('click', '.fs-insert-section-template', function(e) {
        e.preventDefault();
        
        var title = $(this).data('title');
        var content = $(this).data('content');
        var position = $(this).data('position');
        var template = $('#fs-custom-section-template').html();
        var id = 'section_' + Date.now();
        var textareaId = 'section_editor_' + customSectionIndex;
        
        template = template.replace(/\{\{index\}\}/g, customSectionIndex);
        template = template.replace(/\{\{id\}\}/g, id);
        template = template.replace(/\{\{position\}\}/g, position);
        template = template.replace(/\{\{title\}\}/g, title);
        template = template.replace(/\{\{content\}\}/g, content);
        template = template.replace(/\{\{editor_id\}\}/g, textareaId);
        
        var $container = $('.fs-custom-sections-list[data-position="' + position + '"]');
        $container.append(template);
        
        // Initialize TinyMCE on the new textarea
        setTimeout(function() {
            initSectionEditor(textareaId);
        }, 100);
        
        customSectionIndex++;
        
        // Hide dropdown
        $(this).closest('.fs-dropdown-menu').hide();
    });
    
    // Toggle template dropdown
    $(document).on('click', '.fs-custom-sections-container .fs-dropdown-trigger', function(e) {
        e.preventDefault();
        e.stopPropagation();
        var $menu = $(this).siblings('.fs-dropdown-menu');
        $('.fs-dropdown-menu').not($menu).hide();
        $menu.toggle();
    });
    
    // Close dropdown when clicking elsewhere
    $(document).on('click', function(e) {
        if (!$(e.target).closest('.fs-dropdown').length) {
            $('.fs-custom-sections-container .fs-dropdown-menu').hide();
        }
    });
    
    // Save section as template - open modal
    $(document).on('click', '.fs-save-section-template', function() {
        var $section = $(this).closest('.fs-custom-section-item');
        var title = $section.find('.fs-custom-section-title').val();
        var position = $section.find('input[name*="[position]"]').val();
        
        // Get content - check for both TinyMCE and textarea
        var $textarea = $section.find('.fs-custom-section-content, textarea[name*="[content]"]');
        var textareaId = $textarea.attr('id');
        var content = '';
        
        // Try TinyMCE first
        if (typeof tinymce !== 'undefined' && tinymce.get(textareaId)) {
            content = tinymce.get(textareaId).getContent();
        } else {
            content = $textarea.val();
        }
        
        $('#template_title').val(title);
        $('#template_position').val(position);
        $('#template_content').val(content);
        
        $('#fs-save-section-modal').show();
    });
    
    // Close modal
    $(document).on('click', '.fs-modal-close, .fs-modal', function(e) {
        if (e.target === this || $(this).hasClass('fs-modal-close')) {
            $('#fs-save-section-modal').hide();
        }
    });
    
    // Prevent modal content click from closing modal
    $(document).on('click', '.fs-modal-content', function(e) {
        e.stopPropagation();
    });
    
    // Save template via AJAX
    $(document).on('click', '#fs-save-template-btn', function() {
        var $btn = $(this);
        var title = $('#template_title').val();
        var content = $('#template_content').val();
        var position = $('#template_position').val();
        
        if (!title) {
            alert('Please enter a template name.');
            return;
        }
        
        $btn.prop('disabled', true).text('Saving...');
        
        $.ajax({
            url: fs_ajax.ajax_url,
            type: 'POST',
            data: {
                action: 'fs_save_proposal_section_template',
                nonce: fs_ajax.nonce,
                title: title,
                content: content,
                position: position
            },
            success: function(response) {
                if (response.success) {
                    alert('Template saved successfully! It will be available when you create your next proposal.');
                    $('#fs-save-section-modal').hide();
                } else {
                    alert(response.data.message || 'Failed to save template.');
                }
                $btn.prop('disabled', false).text('Save Template');
            },
            error: function() {
                alert('An error occurred. Please try again.');
                $btn.prop('disabled', false).text('Save Template');
            }
        });
    });
    
    // Delete section template
    $(document).on('click', '.fs-delete-section-template', function(e) {
        e.preventDefault();
        e.stopPropagation();
        
        var $btn = $(this);
        var templateId = $btn.data('id');
        
        if (!confirm(fs_ajax.strings.confirm_delete || 'Are you sure you want to delete this template?')) {
            return;
        }
        
        $.ajax({
            url: fs_ajax.ajax_url,
            type: 'POST',
            data: {
                action: 'fs_delete_proposal_section_template',
                nonce: fs_ajax.nonce,
                id: templateId
            },
            success: function(response) {
                if (response.success) {
                    $btn.closest('.fs-template-menu-item').slideUp(200, function() {
                        $(this).remove();
                        // If no templates left, hide the dropdown
                        var $menu = $btn.closest('.fs-dropdown-menu');
                        if (!$menu.find('.fs-template-menu-item').length) {
                            $btn.closest('.fs-dropdown').remove();
                        }
                    });
                } else {
                    alert(response.data.message || 'Failed to delete template.');
                }
            },
            error: function() {
                alert('An error occurred. Please try again.');
            }
        });
    });

    // Make custom sections sortable within their container
    if (typeof $.fn.sortable !== 'undefined') {
        $('.fs-custom-sections-list').sortable({
            handle: '.fs-section-drag',
            placeholder: 'fs-section-placeholder',
            update: function() {
                // Renumber sections after sort
                $(this).find('.fs-custom-section-item').each(function(index) {
                    // We don't need to renumber since PHP will re-index on save
                });
            }
        });
    }

    // Retainer "New Period" manual reset button
    $(document).on('click', '.fs-retainer-reset-btn', function(e) {
        e.preventDefault();
        var $btn       = $(this);
        var projectId  = $btn.data('project-id');

        if (!confirm(fsAdminStrings && fsAdminStrings.retainer_reset_confirm
                ? fsAdminStrings.retainer_reset_confirm
                : 'Start a new retainer period now? The current period will be closed today and a new one will begin tomorrow.')) {
            return;
        }

        $btn.prop('disabled', true).text('...');

        $.ajax({
            url: typeof ajaxurl !== 'undefined' ? ajaxurl : fs_ajax.ajax_url,
            type: 'POST',
            data: {
                action: 'fs_reset_retainer_period',
                project_id: projectId,
                nonce: typeof fs_ajax !== 'undefined' ? fs_ajax.nonce : fsAdminNonce
            },
            success: function(response) {
                if (response.success) {
                    // Show success and reload to reflect new period
                    $btn.closest('.fs-retainer-widget').find('.fs-retainer-period-label')
                        .text(response.data.message)
                        .removeClass('fs-text-warning');
                    setTimeout(function() { location.reload(); }, 1200);
                } else {
                    alert(response.data.message || 'Failed to reset period.');
                    $btn.prop('disabled', false).html('<span class="dashicons dashicons-update"></span> New Period');
                }
            },
            error: function() {
                alert('An error occurred. Please try again.');
                $btn.prop('disabled', false).html('<span class="dashicons dashicons-update"></span> New Period');
            }
        });
    });

})(jQuery);
