# easy-invoice/2.4.0/includes/Controllers/SettingsController.php

Easy Invoice – Invoice Generator, PDF Quotes &amp; Payments, version 2.4.0. 2,124 lines.

- Page: https://pluginprobe.com/plugins/easy-invoice/2.4.0/code/includes/Controllers/SettingsController.php
- Raw: https://pluginprobe.com/plugins/easy-invoice/2.4.0/raw/includes/Controllers/SettingsController.php
- Modified: 2026-09-15T12:31:20+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/easy-invoice/2.4.0/code/includes/Controllers/SettingsController.php#L10-L20`.

```php
<?php
/**
 * Settings Controller Class
 *
 * Handles configuration, display and storage of all plugin settings
 *
 * @package Easy_Invoice
 * @subpackage Controllers
 * @since 1.0.0
 */

namespace EasyInvoice\Controllers;

use EasyInvoice\Constants\PagesSlugs;
use EasyInvoice\Helpers\CurrencyHelper;

/**
 * SettingsController handles all settings-related functionality
 */
class SettingsController extends BaseController {
    /**
     * Settings cache
     *
     * @var array
     */
    private $settings_cache = null;

    /**
     * Settings resolved once per request. Every money formatter used to
     * construct its own controller and rebuild the whole settings array (the
     * config tree with every translated label) — around a millisecond per
     * formatted amount, ten seconds on a 4,500-line statement.
     *
     * @var array|null
     */
    private static $shared_settings_cache = null;

    /**
     * Drop the per-request settings cache (any easy_invoice_* option changed).
     */
    public static function flushSharedCache(): void {
        self::$shared_settings_cache = null;
    }

    /**
     * Settings configuration cache
     *
     * @var array
     */
    private $config_cache = null;

    /**
     * Clear settings cache
     *
     * @return void
     */
    public function clearCache(): void {
        $this->config_cache = null;
        // Also clear any WordPress object cache
        wp_cache_delete('easy_invoice_settings_config', 'easy_invoice');
        // Force clear any other caches
        if (function_exists('wp_cache_flush')) {
            wp_cache_flush();
        }
    }

    /**
     * Initialize the controller
     *
     * @return void
     */
    public function init() {
        // Add AJAX handlers
        add_action('wp_ajax_easy_invoice_save_settings', [$this, 'saveSettings']);
        add_action('wp_ajax_easy_invoice_test_email', [$this, 'testEmail']);
        add_action('wp_ajax_easy_invoice_test_template_email', [$this, 'testTemplateEmail']);
        add_action('wp_ajax_easy_invoice_test_payment_reminder_email', [$this, 'testPaymentReminderEmail']);
        add_action('wp_ajax_regenerate_invoice_numbers', [$this, 'ajaxRegenerateInvoiceNumbers']);
        add_action('wp_ajax_regenerate_quote_numbers', [$this, 'ajaxRegenerateQuoteNumbers']);

        // Clear cache on admin init to ensure new fields are recognized
        add_action('admin_init', [$this, 'clearCache']);

        // Enqueue admin scripts
        add_action('admin_enqueue_scripts', [$this, 'enqueueAdminScripts']);

        // Initialize settings
        $this->initializeSettings();
    }



    /**
     * Enqueue admin scripts and styles for the settings page.
     *
     * @param string $hook_suffix The current admin page.
     * @return void
     */
    public function enqueueAdminScripts($hook_suffix) {
        // Check if we are on the Easy Invoice settings page.
        if (empty($hook_suffix) || strpos($hook_suffix, PagesSlugs::SETTINGS) === false) {
            return;
        }

            wp_enqueue_script('jquery-ui-sortable');
            wp_enqueue_media(); // For the logo uploader

        // Enqueue Select2 for enhancing multiselect fields
        wp_enqueue_style(
            'select2',
			EASY_INVOICE_URL . 'assets/lib/select2/select2.min.css',
            [],
            '4.0.13'
        );


		wp_enqueue_script(
			'select2',
			EASY_INVOICE_URL . 'assets/lib/select2/select2.min.js',
			['jquery'],
			'4.0.13',
			true
		);

        // Enqueue confirmation modal for premium features
        wp_enqueue_script(
            'easy-invoice-confirmation-modal',
            EASY_INVOICE_URL . 'assets/js/confirmation-modal.js',
            ['jquery'],
            EASY_INVOICE_VERSION,
            true
        );

        // Enqueue toast notification system
        wp_enqueue_script(
            'easy-invoice-toast',
            EASY_INVOICE_URL . 'assets/js/easy-invoice-toast.js',
            ['jquery'],
            EASY_INVOICE_VERSION,
            true
        );

        $script_handle = 'easy-invoice-settings';
            wp_enqueue_script(
            $script_handle,
                EASY_INVOICE_URL . 'assets/js/settings.js',
            ['jquery', 'jquery-ui-sortable', 'wp-util', 'select2', 'easy-invoice-confirmation-modal', 'media-views'],
                EASY_INVOICE_VERSION,
                true
            );

        // Enqueue the new settings.css only on the settings page
        wp_enqueue_style(
            'easy-invoice-settings',
            EASY_INVOICE_URL . 'assets/css/settings.css',
            [],
            EASY_INVOICE_VERSION
        );

        $localize_data = apply_filters('easy_invoice_settings_js_data', [
                    'ajaxurl' => admin_url('admin-ajax.php'),
            'nonce' => wp_create_nonce('easy_invoice_settings'),
            'saveSuccess' => __('Settings saved successfully', 'easy-invoice'),
            'saveError' => __('Error saving settings', 'easy-invoice'),
        ]);

        wp_localize_script($script_handle, 'easyInvoiceSettings', $localize_data);

        do_action('easy_invoice_after_admin_scripts', $hook_suffix);
    }

    /**
     * Check if Easy Invoice Pro is active
     *
     * @return bool
     */
    private function isProActive(): bool {
        return easy_invoice_has_pro();
    }

    /**
     * Get settings fields configuration
     *
     * @return array
     */
    public function get_settings_fields_config() {
        // Use cached config if available
        if (is_array($this->config_cache)) {
            return $this->config_cache;
        }

        $config = [
            'company' => [
                'title' => __('Company Information', 'easy-invoice'),
                'description' => __('Set up your company details', 'easy-invoice'),
                'icon' => 'fas fa-building',
                'fields' => [
                    'easy_invoice_company_name' => ['label' => __('Company Name', 'easy-invoice'), 'type' => 'text', 'default' => '', 'col_span' => 'sm:col-span-6' ],
                    'easy_invoice_company_email' => ['label' => __('Email', 'easy-invoice'), 'type' => 'email', 'default' => '', 'col_span' => 'sm:col-span-6' ],
                    'easy_invoice_company_phone' => ['label' => __('Phone Number', 'easy-invoice'), 'type' => 'tel', 'default' => '', 'col_span' => 'sm:col-span-6' ],
                    'easy_invoice_company_website' => ['label' => __('Website', 'easy-invoice'), 'type' => 'url', 'default' => '', 'col_span' => 'sm:col-span-6' ],
                    // Both are required by EN 16931 and are what makes an intra-EU
                    // reverse charge determinable. See Services\TaxTreatment.
                    'easy_invoice_company_vat_number' => ['label' => __('VAT number', 'easy-invoice'), 'type' => 'text', 'default' => '', 'col_span' => 'sm:col-span-3', 'description' => __('Your VAT identifier, including the country prefix — for example DE123456789.', 'easy-invoice') ],
                    'easy_invoice_company_country' => ['label' => __('Country', 'easy-invoice'), 'type' => 'text', 'default' => '', 'col_span' => 'sm:col-span-3', 'description' => __('Two-letter country code, for example DE, FR or GB.', 'easy-invoice') ],
                    'easy_invoice_company_address' => ['label' => __('Address', 'easy-invoice'), 'type' => 'textarea', 'default' => '', 'col_span' => 'sm:col-span-6' ],
                    'easy_invoice_tax_number' => ['label' => __('Tax ID / VAT Number', 'easy-invoice'), 'type' => 'text', 'default' => '', 'col_span' => 'sm:col-span-6' ],
                    'easy_invoice_company_logo' => ['label' => __('Company Logo', 'easy-invoice'), 'type' => 'image', 'default' => '', 'col_span' => 'sm:col-span-6' ],
                ]
            ],
            'invoice' => [
                'title' => __('Invoice Settings', 'easy-invoice'),
                'description' => __('Configure invoice-specific settings and preferences', 'easy-invoice'),
                'icon' => 'fas fa-file-invoice',
                'fields' => [
                    'easy_invoice_invoice_prefix' => [
                        'label' => __('Invoice Prefix', 'easy-invoice'),
                        'type' => 'text',
                        'default' => 'EIIN_',
                        'description' => __('Prefix for invoice numbers (e.g., EIIN_, INV-)', 'easy-invoice'),
                        'col_span' => 'sm:col-span-2'
                    ],
                    'easy_invoice_next_invoice_number' => [
                        'label' => __('Next Invoice Number', 'easy-invoice'),
                        'type' => 'number',
                        'default' => '1',
                        'description' => __('Set the next invoice number to be generated. This will be the number of the next invoice created.', 'easy-invoice'),
                        'col_span' => 'sm:col-span-2',
                    ],
                    'easy_invoice_regenerate_invoice_numbers' => [
                        'label' => __('Regenerate Invoice Numbers', 'easy-invoice'),
                        'type' => 'button',
                        'button_text' => __('Regenerate All Invoice Numbers', 'easy-invoice'),
                        'button_class' => 'button button-secondary',
                        'description' => __('Click to regenerate all invoice numbers starting from the Last Invoice Number. This will update all existing invoices with new sequential numbers.', 'easy-invoice'),
                        'col_span' => 'sm:col-span-2',
                        'ajax_action' => 'regenerate_invoice_numbers'
                    ],
                    'easy_invoice_invoice_show_adjust_field' => [
                        'label' => __('Show/Hide Adjust Field', 'easy-invoice'),
                        'type' => 'checkbox',
                        'default' => 'yes',
                        'description' => __('Enable/Disable Adjust field. Tick this to show adjust field', 'easy-invoice'),
                        'col_span' => 'sm:col-span-6'
                    ],
                    'easy_invoice_invoice_terms_conditions' => [
                        'label' => __('Terms & Conditions', 'easy-invoice'),
                        'type' => 'wp_editor',
                        'default' => __('Payment is due within 30 days from date of invoice', 'easy-invoice'),
                        'description' => __('Terms and conditions that will be displayed on your invoice', 'easy-invoice'),
                        'col_span' => 'sm:col-span-6'
                    ],
                    'easy_invoice_invoice_footer_text' => [
                        'label' => __('Footer Text', 'easy-invoice'),
                        'type' => 'wp_editor',
                        'default' => '',
                        'description' => __('You can modify your invoice footer text from here. HTML tags supports: a, br, em, strong, hr, p, h1 to h4', 'easy-invoice'),
                        'col_span' => 'sm:col-span-6'
                    ],
                ],
            ],
            'quote' => [
                'title' => __('Quote Settings', 'easy-invoice'),
                'description' => __('Configure quote-specific settings and preferences', 'easy-invoice'),
                'icon' => 'fas fa-file-contract',
                'fields' => [
                    'easy_invoice_quote_prefix' => [
                        'label' => __('Quote Prefix', 'easy-invoice'),
                        'type' => 'text',
                        'default' => 'EIQN_',
                        'description' => __('Prefix for quote numbers (e.g., EIQN_, QT-)', 'easy-invoice'),
                        'col_span' => 'sm:col-span-2'
                    ],
                    'easy_invoice_next_quote_number' => [
                        'label' => __('Next Quote Number', 'easy-invoice'),
                        'type' => 'number',
                        'default' => '1',
                        'description' => __('Set the next quote number to be generated. This will be the number of the next quote created.', 'easy-invoice'),
                        'col_span' => 'sm:col-span-2',
                    ],
                    'easy_invoice_regenerate_quote_numbers' => [
                        'label' => __('Regenerate Quote Numbers', 'easy-invoice'),
                        'type' => 'button',
                        'button_text' => __('Regenerate All Quote Numbers', 'easy-invoice'),
                        'button_class' => 'button button-secondary',
                        'description' => __('Click to regenerate all quote numbers starting from the Next Quote Number. This will update all existing quotes with new sequential numbers.', 'easy-invoice'),
                        'col_span' => 'sm:col-span-2',
                        'ajax_action' => 'regenerate_quote_numbers'
                    ],
                    'easy_invoice_quote_show_adjust_field' => [
                        'label' => __('Show/Hide Adjust Field', 'easy-invoice'),
                        'type' => 'checkbox',
                        'default' => 'yes',
                        'description' => __('Enable/Disable Adjust field. Tick this to show adjust field', 'easy-invoice'),
                        'col_span' => 'sm:col-span-6'
                    ],
                    'easy_invoice_quote_terms_conditions' => [
                        'label' => __('Terms & Conditions', 'easy-invoice'),
                        'type' => 'wp_editor',
                        'default' => __('This quote has a fixed price. Upon acceptance, we kindly ask for a 25% deposit prior to initiating the work.', 'easy-invoice'),
                        'description' => __('Terms and conditions that will be displayed on your quote!', 'easy-invoice'),
                        'col_span' => 'sm:col-span-6'
                    ],
                    'easy_invoice_quote_footer_text' => [
                        'label' => __('Footer Text', 'easy-invoice'),
                        'type' => 'wp_editor',
                        'default' => __('Thank you for your business.', 'easy-invoice'),
                        'description' => __('You can modify your quote footer text from here. HTML tags supports: a, br, em, strong, hr, p, h1 to h4', 'easy-invoice'),
                        'col_span' => 'sm:col-span-6'
                    ],
                    'easy_invoice_quote_accept_button' => [
                        'label' => __('Accept quote button', 'easy-invoice'),
                        'type' => 'checkbox',
                        'default' => 'yes',
                        'description' => __('Show/hide accept quote button on quotes.', 'easy-invoice'),
                        'col_span' => 'sm:col-span-6'
                    ],
                    'easy_invoice_quote_accept_action' => [
                        'label' => __('Accept quote button action', 'easy-invoice'),
                        'type' => 'select',
                        'default' => 'convert',
                        'options' => [
                            'convert' => __('Convert quote to invoice - Draft', 'easy-invoice'),
                            'convert_available' => __('Convert quote to invoice - Available', 'easy-invoice'),
                            'convert_send' => __('Convert quote to invoice and send to client - Available', 'easy-invoice'),
                            'duplicate' => __('Create new invoice, keep quote as-is - Draft', 'easy-invoice'),
                            'duplicate_send' => __('Create new invoice and send to client, keep quote as-is - Available', 'easy-invoice'),
                            'do_nothing' => __('Do nothing', 'easy-invoice'),
                        ],
                        'description' => __('Upon the client clicking the "accept quote" button, the subsequent action will be activated.', 'easy-invoice'),
                        'col_span' => 'sm:col-span-6'
                    ],
                    'easy_invoice_quote_accept_text' => [
                        'label' => __('Accept Quote Text', 'easy-invoice'),
                        'type' => 'wp_editor',
                        'default' => __('Important: When you accept this Quote, an Invoice will be created automatically. This will form a legally binding contract.', 'easy-invoice'),
                        'description' => __('This information tells your client what happens once they accept the Quote', 'easy-invoice'),
                        'col_span' => 'sm:col-span-6'
                    ],
                    'easy_invoice_quote_accepted_message' => [
                        'label' => __('Accepted Quote Message', 'easy-invoice'),
                        'type' => 'wp_editor',
                        'default' => __('You\'ve confirmed the Quote.<br>We\'ll get in touch with you shortly.', 'easy-invoice'),
                        'description' => __('If the client accepts the Quote, display this message.', 'easy-invoice'),
                        'col_span' => 'sm:col-span-6'
                    ],
                    'easy_invoice_quote_decline_reason_required' => [
                        'label' => __('Decline Reason Required', 'easy-invoice'),
                        'type' => 'checkbox',
                        'default' => 'no',
                        'description' => __('Make the \'Reason for declining\' field mandatory when rejecting.', 'easy-invoice'),
                        'col_span' => 'sm:col-span-6'
                    ],
                    'easy_invoice_quote_declined_message' => [
                        'label' => __('Declined Quote Message', 'easy-invoice'),
                        'type' => 'wp_editor',
                        'default' => '',
                        'description' => __('Message to display if client declines the Quote', 'easy-invoice'),
                        'col_span' => 'sm:col-span-6'
                    ],
                ],
            ],
            'currency' => [
                'title' => __('Currency Settings', 'easy-invoice'),
                'description' => __('Configure currency display preferences', 'easy-invoice'),
                'icon' => 'fas fa-dollar-sign',
                'fields' => [
                    'easy_invoice_currency_code' => [
                        'label' => __('Currency & Symbol', 'easy-invoice'),
                        'type' => 'select',
                        'default' => 'USD',
                        'options' => CurrencyHelper::getCurrencyOptions(),
                        'col_span' => 'sm:col-span-6',
                        'description' => __('Select your preferred currency. The symbol will be automatically set.', 'easy-invoice')
                    ],
                    'easy_invoice_currency_position' => [
                        'label' => __('Symbol Position', 'easy-invoice'),
                        'type' => 'select',
                        'default' => 'left',
                        'options' => [
                            'left' => __('Left', 'easy-invoice'),
                            'right' => __('Right', 'easy-invoice'),
                            'left_space' => __('Left with space', 'easy-invoice'),
                            'right_space' => __('Right with space', 'easy-invoice'),
                        ],
                        'col_span' => 'sm:col-span-3'
                    ],
                    'easy_invoice_currency_symbol_type' => [
                        'label' => __('Currency Symbol Type', 'easy-invoice'),
                        'type' => 'select',
                        'default' => 'symbol',
                        'options' => [
                            'code' => __('Currency Code', 'easy-invoice'),
                            'symbol' => __('Currency Symbol', 'easy-invoice'),
                        ],
                        'description' => __('Choose whether to display currency code (USD) or currency symbol ($)', 'easy-invoice'),
                        'col_span' => 'sm:col-span-3'
                    ],
                    'easy_invoice_thousands_separator' => ['label' => __('Thousands Separator', 'easy-invoice'), 'type' => 'text', 'default' => ',', 'col_span' => 'sm:col-span-3' ],
                    'easy_invoice_decimal_separator' => ['label' => __('Decimal Separator', 'easy-invoice'), 'type' => 'text', 'default' => '.', 'col_span' => 'sm:col-span-3' ],
                    'easy_invoice_decimal_precision' => ['label' => __('Decimal Places', 'easy-invoice'), 'type' => 'number', 'default' => '2', 'min' => 0, 'max' => 4, 'col_span' => 'sm:col-span-3' ],
                ]
            ],
            'tax' => [
                'title' => __('Tax Settings', 'easy-invoice'),
                'description' => __('Configure tax rates and preferences', 'easy-invoice'),
                'icon' => 'fas fa-percent',
                'fields' => [
                    'easy_invoice_tax_enabled' => ['label' => __('Enable Tax', 'easy-invoice'), 'type' => 'checkbox', 'default' => 'no', 'col_span' => 'sm:col-span-6' ],
                    'easy_invoice_tax_entry_method' => [
                        'label' => __('How do you enter tax?', 'easy-invoice'),
                        'type' => 'select',
                        'default' => 'exclusive',
                        'options' => [
                            'inclusive' => __('I will enter price inclusive of tax', 'easy-invoice'),
                            'exclusive' => __('I will enter price exclusive of tax', 'easy-invoice'),
                        ],
                        'description' => __('Choose how you want to enter prices - with or without tax included', 'easy-invoice'),
                        'col_span' => 'sm:col-span-6'
                    ],
                    'easy_invoice_tax_rate' => ['label' => __('Default Tax Rate (%)', 'easy-invoice'), 'type' => 'number', 'default' => '0', 'step' => '0.01', 'min' => '0', 'max' => '100', 'col_span' => 'sm:col-span-3' ],
                    'easy_invoice_tax_name' => ['label' => __('Tax Name', 'easy-invoice'), 'type' => 'text', 'default' => __('Tax', 'easy-invoice'), 'col_span' => 'sm:col-span-3' ],
                ]
            ],
            'payment' => [
                'title' => __('Payment Methods', 'easy-invoice'),
                'description' => __('Configure and order available payment methods.', 'easy-invoice'),
                'icon' => 'fas fa-credit-card',
                'is_special_section' => true,
                'gateways' => $this->getGatewaySettingsConfigs(),
            ],
            'email' => [
                'title' => __('Email Settings', 'easy-invoice'),
                'description' => __('Configure email sending options and templates', 'easy-invoice'),
                'icon' => 'fas fa-envelope',
                'subsections' => [
                    'general' => [
                        'title' => __('General Email Settings', 'easy-invoice'),
                        'description' => __('Configure general email sending options', 'easy-invoice'),
                        'fields' => [
                            'easy_invoice_email_from_name' => ['label' => __('From Name', 'easy-invoice'), 'type' => 'text', 'default' => get_bloginfo('name') ?: 'Easy Invoice', 'col_span' => 'sm:col-span-3'],
                            'easy_invoice_email_from_address' => ['label' => __('From Email Address', 'easy-invoice'), 'type' => 'email', 'default' => get_bloginfo('admin_email') ?: get_option('admin_email', ''), 'col_span' => 'sm:col-span-3'],
                            'easy_invoice_email_reply_to' => ['label' => __('Reply-To Email', 'easy-invoice'), 'type' => 'email', 'default' => '', 'col_span' => 'sm:col-span-3'],
                            'easy_invoice_email_reply_to_name' => ['label' => __('Reply-To Name', 'easy-invoice'), 'type' => 'text', 'default' => '', 'col_span' => 'sm:col-span-3'],
                            'easy_invoice_enable_email_styling' => ['label' => __('Enable HTML Emails', 'easy-invoice'), 'type' => 'checkbox', 'default' => 'yes', 'col_span' => 'sm:col-span-6'],
                            'easy_invoice_bcc_admin' => ['label' => __('BCC Admin on All Emails', 'easy-invoice'), 'type' => 'checkbox', 'default' => 'no', 'col_span' => 'sm:col-span-6'],
                            // Off by default: attaching a document changes what every
                            // customer receives and makes messages much larger, which
                            // some SMTP relays reject. See EmailManager::shouldAttachInvoicePdf().
                            'easy_invoice_attach_pdf_to_email' => ['label' => __('Attach a PDF copy to invoice and quote emails', 'easy-invoice'), 'type' => 'checkbox', 'default' => 'no', 'col_span' => 'sm:col-span-6', 'description' => __('Generates the document on the server and attaches it. The PDF contains real, selectable text rather than an image.', 'easy-invoice')],
                            'easy_invoice_pdf_paper_size' => ['label' => __('PDF paper size', 'easy-invoice'), 'type' => 'select', 'default' => 'a4', 'options' => ['a4' => 'A4', 'letter' => 'Letter', 'legal' => 'Legal'], 'col_span' => 'sm:col-span-3'],
                            'easy_invoice_pdf_download_method' => ['label' => __('"Download as PDF" button', 'easy-invoice'), 'type' => 'select', 'default' => 'browser', 'options' => ['browser' => __('Exactly as shown on screen (rendered in the browser)', 'easy-invoice'), 'server' => __('Generated on the server (real text, smaller file)', 'easy-invoice')], 'col_span' => 'sm:col-span-3', 'description' => __('The browser capture reproduces the page pixel for pixel — every design, custom template and watermark. The server file is selectable text and a fraction of the size. Emails, the API and e-invoicing always use the server.', 'easy-invoice')],
                            'easy_invoice_pdf_layout' => ['label' => __('Server-generated PDF layout', 'easy-invoice'), 'type' => 'select', 'default' => 'design', 'options' => ['design' => __('Match the selected invoice / quote design', 'easy-invoice'), 'plain' => __('Plain print layout', 'easy-invoice')], 'col_span' => 'sm:col-span-3', 'description' => __('Applies to PDFs the server produces: email attachments, the API, e-invoicing, and the download button when set to "server". The design is the template chosen on the document; the plain layout is a neutral table-based page.', 'easy-invoice')],
                            'easy_invoice_admin_email' => ['label' => __('Admin Email for BCC', 'easy-invoice'), 'type' => 'email', 'default' => get_option('admin_email', ''), 'col_span' => 'sm:col-span-6'],
                            'easy_invoice_email_logo' => ['label' => __('Email Logo URL', 'easy-invoice'), 'type' => 'url', 'default' => '', 'col_span' => 'sm:col-span-6'],
                            'easy_invoice_email_footer_text' => ['label' => __('Email Footer Text', 'easy-invoice'), 'type' => 'textarea', 'default' => '', 'col_span' => 'sm:col-span-6'],
                            'easy_invoice_test_email' => [
                                'label' => __('Test Email', 'easy-invoice'),
                                'type' => 'test_email',
                                'default' => '',
                                'col_span' => 'sm:col-span-6',
                                'description' => __('Send a test email to verify your email configuration is working correctly.', 'easy-invoice')
                            ],
                        ]
                    ],
                    'invoice_available' => [
                        'title' => __('Invoice Available Email', 'easy-invoice'),
                        'description' => __('Configure email sent when an invoice is available for the client', 'easy-invoice'),
                        'fields' => [
                            'easy_invoice_invoice_email_enabled' => ['label' => __('Enable Invoice Available Email', 'easy-invoice'), 'type' => 'checkbox', 'default' => 'yes', 'col_span' => 'sm:col-span-6'],
                            'easy_invoice_invoice_email_subject' => ['label' => __('Subject', 'easy-invoice'), 'type' => 'text', 'default' => __('Your Invoice #{{invoice_number}} from {{company_name}}', 'easy-invoice'), 'col_span' => 'sm:col-span-6'],
                            'easy_invoice_invoice_email_body' => ['label' => __('Email Body', 'easy-invoice'), 'type' => 'wp_editor', 'default' => \EasyInvoice\Services\EmailManager::defaultTemplate('invoice'), 'col_span' => 'sm:col-span-6'],
                            'easy_invoice_invoice_test_template' => [
                                'label' => __('Test Invoice Email', 'easy-invoice'),
                                'type' => 'test_template_email',
                                'default' => '',
                                'col_span' => 'sm:col-span-6',
                                'description' => __('Send a test invoice email to verify the template and configuration.', 'easy-invoice'),
                                'template_type' => 'invoice_available'
                            ],
                        ]
                    ],
                    'quote_available' => [
                        'title' => __('Quote Available Email', 'easy-invoice'),
                        'description' => __('Configure email sent when a quote is available for the client', 'easy-invoice'),
                        'fields' => [
                            'easy_invoice_quote_email_enabled' => ['label' => __('Enable Quote Available Email', 'easy-invoice'), 'type' => 'checkbox', 'default' => 'yes', 'col_span' => 'sm:col-span-6'],
                            'easy_invoice_quote_email_subject' => ['label' => __('Subject', 'easy-invoice'), 'type' => 'text', 'default' => __('Your Quote #{{quote_number}} from {{company_name}}', 'easy-invoice'), 'col_span' => 'sm:col-span-6'],
                            'easy_invoice_quote_email_body' => ['label' => __('Email Body', 'easy-invoice'), 'type' => 'wp_editor', 'default' => \EasyInvoice\Services\EmailManager::defaultTemplate('quote'), 'col_span' => 'sm:col-span-6'],
                            'easy_invoice_quote_test_template' => [
                                'label' => __('Test Quote Email', 'easy-invoice'),
                                'type' => 'test_template_email',
                                'default' => '',
                                'col_span' => 'sm:col-span-6',
                                'description' => __('Send a test quote email to verify the template and configuration.', 'easy-invoice'),
                                'template_type' => 'quote_available'
                            ],
                        ]
                    ],
                    'payment_received' => [
                        'title' => __('Payment Received Email', 'easy-invoice'),
                        'description' => __('Configure email sent when a payment is received', 'easy-invoice'),
                        'fields' => [
                            'easy_invoice_payment_email_enabled' => ['label' => __('Enable Payment Received Email', 'easy-invoice'), 'type' => 'checkbox', 'default' => 'yes', 'col_span' => 'sm:col-span-6'],
                            'easy_invoice_payment_email_subject' => ['label' => __('Subject', 'easy-invoice'), 'type' => 'text', 'default' => __('Payment Received - Invoice #{{invoice_number}}', 'easy-invoice'), 'col_span' => 'sm:col-span-6'],
                            'easy_invoice_payment_email_body' => ['label' => __('Email Body', 'easy-invoice'), 'type' => 'wp_editor', 'default' => \EasyInvoice\Services\EmailManager::defaultTemplate('payment'), 'col_span' => 'sm:col-span-6'],
                            'easy_invoice_payment_test_template' => [
                                'label' => __('Test Payment Email', 'easy-invoice'),
                                'type' => 'test_template_email',
                                'default' => '',
                                'col_span' => 'sm:col-span-6',
                                'description' => __('Send a test payment email to verify the template and configuration.', 'easy-invoice'),
                                'template_type' => 'payment_received'
                            ],
                        ]
                    ],
                ]
            ],
            'text_settings' => [
                'title' => __('Text Settings', 'easy-invoice'),
                'description' => __('Customize text labels for invoices and quotes. Perfect for multi-language support.', 'easy-invoice'),
                'icon' => '<svg class="text-indigo-600 text-xl h-6 w-6" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
                    <path d="M14 2H6C4.9 2 4 2.9 4 4V20C4 21.1 4.9 22 6 22H18C19.1 22 20 21.1 20 20V8L14 2Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
                    <path d="M14 2V8H20" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
                    <path d="M8 11H16" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
                    <path d="M8 15H12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
                    <path d="M8 19H14" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
                </svg>',
                'fields' => [
                    // Invoice Text Settings
                    'easy_invoice_text_invoice' => ['label' => __('Invoice', 'easy-invoice'), 'type' => 'text', 'default' => __('Invoice', 'easy-invoice'), 'col_span' => '', 'description' => __('Label for single invoice', 'easy-invoice')],
                    'easy_invoice_text_invoices' => ['label' => __('Invoices', 'easy-invoice'), 'type' => 'text', 'default' => __('Invoices', 'easy-invoice'), 'col_span' => '', 'description' => __('Label for multiple invoices', 'easy-invoice')],
                    'easy_invoice_text_to' => ['label' => __('To', 'easy-invoice'), 'type' => 'text', 'default' => __('To', 'easy-invoice'), 'col_span' => '', 'description' => __('To label on invoice/quote', 'easy-invoice')],
                    'easy_invoice_text_invoice_number' => ['label' => __('Invoice Number', 'easy-invoice'), 'type' => 'text', 'default' => __('Invoice Number', 'easy-invoice'), 'col_span' => '', 'description' => __('Invoice number label', 'easy-invoice')],
                    'easy_invoice_text_invoice_date' => ['label' => __('Invoice Date', 'easy-invoice'), 'type' => 'text', 'default' => __('Invoice Date', 'easy-invoice'), 'col_span' => '', 'description' => __('Invoice date label', 'easy-invoice')],
                    'easy_invoice_text_due_date' => ['label' => __('Due Date', 'easy-invoice'), 'type' => 'text', 'default' => __('Due Date', 'easy-invoice'), 'col_span' => '', 'description' => __('Due date label', 'easy-invoice')],
                    'easy_invoice_text_total_due' => ['label' => __('Total Due', 'easy-invoice'), 'type' => 'text', 'default' => __('Total Due', 'easy-invoice'), 'col_span' => '', 'description' => __('Total due label', 'easy-invoice')],
                    'easy_invoice_text_qty' => ['label' => __('Qty', 'easy-invoice'), 'type' => 'text', 'default' => __('Qty', 'easy-invoice'), 'col_span' => '', 'description' => __('Quantity label', 'easy-invoice')],
                    'easy_invoice_text_service' => ['label' => __('Service', 'easy-invoice'), 'type' => 'text', 'default' => __('Service', 'easy-invoice'), 'col_span' => '', 'description' => __('Service label', 'easy-invoice')],
                    'easy_invoice_text_rate_price' => ['label' => __('Rate', 'easy-invoice'), 'type' => 'text', 'default' => __('Rate', 'easy-invoice'), 'col_span' => '', 'description' => __('Rate or price label', 'easy-invoice')],
                    'easy_invoice_text_adjust' => ['label' => __('Adjust', 'easy-invoice'), 'type' => 'text', 'default' => __('Adjust', 'easy-invoice'), 'col_span' => '', 'description' => __('Adjust label', 'easy-invoice')],
                    'easy_invoice_text_sub_total' => ['label' => __('Sub Total', 'easy-invoice'), 'type' => 'text', 'default' => __('Sub Total', 'easy-invoice'), 'col_span' => '', 'description' => __('Sub total label', 'easy-invoice')],
                    'easy_invoice_text_total' => ['label' => __('Total', 'easy-invoice'), 'type' => 'text', 'default' => __('Total', 'easy-invoice'), 'col_span' => '', 'description' => __('Total label', 'easy-invoice')],
                    'easy_invoice_text_line_total' => ['label' => __('Line total', 'easy-invoice'), 'type' => 'text', 'default' => __('Total', 'easy-invoice'), 'col_span' => '', 'description' => __('Heading of the line-item amount column', 'easy-invoice')],
                    'easy_invoice_text_tax' => ['label' => __('Tax', 'easy-invoice'), 'type' => 'text', 'default' => __('Tax', 'easy-invoice'), 'col_span' => '', 'description' => __('Tax label', 'easy-invoice')],
                    'easy_invoice_text_discount' => ['label' => __('Discount', 'easy-invoice'), 'type' => 'text', 'default' => __('Discount', 'easy-invoice'), 'col_span' => '', 'description' => __('Discount label', 'easy-invoice')],
                    'easy_invoice_text_print' => ['label' => __('Print', 'easy-invoice'), 'type' => 'text', 'default' => __('Print', 'easy-invoice'), 'col_span' => '', 'description' => __('Print button text', 'easy-invoice')],
                    'easy_invoice_text_download_pdf' => ['label' => __('Download as PDF', 'easy-invoice'), 'type' => 'text', 'default' => __('Download as PDF', 'easy-invoice'), 'col_span' => '', 'description' => __('Download PDF button text', 'easy-invoice')],
                    'easy_invoice_text_send_email' => ['label' => __('Send Email', 'easy-invoice'), 'type' => 'text', 'default' => __('Send Email', 'easy-invoice'), 'col_span' => '', 'description' => __('Send email button text', 'easy-invoice')],
                    'easy_invoice_text_pay_now' => ['label' => __('Pay Now', 'easy-invoice'), 'type' => 'text', 'default' => __('Pay Now', 'easy-invoice'), 'col_span' => '', 'description' => __('Pay now button text', 'easy-invoice')],

                    // Quote Text Settings
                    'easy_invoice_text_quote' => ['label' => __('Quote', 'easy-invoice'), 'type' => 'text', 'default' => __('Quote', 'easy-invoice'), 'col_span' => '', 'description' => __('Label for single quote', 'easy-invoice')],
                    'easy_invoice_text_quote_number' => ['label' => __('Quote Number', 'easy-invoice'), 'type' => 'text', 'default' => __('Quote Number', 'easy-invoice'), 'col_span' => '', 'description' => __('Quote number label', 'easy-invoice')],
                    'easy_invoice_text_accept_quote' => ['label' => __('Accept Quote', 'easy-invoice'), 'type' => 'text', 'default' => __('Accept Quote', 'easy-invoice'), 'col_span' => '', 'description' => __('Accept quote button text', 'easy-invoice')],
                    'easy_invoice_text_decline_quote' => ['label' => __('Decline Quote', 'easy-invoice'), 'type' => 'text', 'default' => __('Decline Quote', 'easy-invoice'), 'col_span' => '', 'description' => __('Decline quote button text', 'easy-invoice')],
                    'easy_invoice_text_decline_reason' => ['label' => __('Reason for declining', 'easy-invoice'), 'type' => 'text', 'default' => __('Reason for declining', 'easy-invoice'), 'col_span' => '', 'description' => __('Decline reason label', 'easy-invoice')],
                    'easy_invoice_text_valid_until' => ['label' => __('Valid Until Date', 'easy-invoice'), 'type' => 'text', 'default' => __('Valid Until Date', 'easy-invoice'), 'col_span' => '', 'description' => __('Valid until date label', 'easy-invoice')],
                    'easy_invoice_text_quote_date' => ['label' => __('Quote Date', 'easy-invoice'), 'type' => 'text', 'default' => __('Quote Date', 'easy-invoice'), 'col_span' => '', 'description' => __('Quote date label', 'easy-invoice')],
                ]
            ],
            'advanced' => [
                'title' => __('Advanced Settings', 'easy-invoice'),
                'description' => __('Configure system preferences', 'easy-invoice'),
                'icon' => 'fas fa-cog',
                'fields' => [
                    'easy_invoice_date_format' => [
                        'label' => __('Date Format', 'easy-invoice'),
                        'type' => 'select',
                        'default' => 'us',
                        'options' => [
                            'us' => __('MM/DD/YYYY (US) - 01/15/2024', 'easy-invoice'),
                            'uk' => __('DD/MM/YYYY (UK) - 15/01/2024', 'easy-invoice'),
                            'iso' => __('YYYY-MM-DD (ISO) - 2024-01-15', 'easy-invoice'),
                        ],
                        'col_span' => 'sm:col-span-3'
                    ],

                    'easy_invoice_invoice_numbering' => ['label' => __('Auto-increment invoice numbers', 'easy-invoice'), 'type' => 'checkbox', 'description' => __('Automatically increment invoice numbers for new invoices', 'easy-invoice'), 'default' => 'yes', 'col_span' => 'sm:col-span-6' ],
                    'easy_invoice_payment_reminder_days' => ['label' => __('Payment Reminder Days', 'easy-invoice'), 'type' => 'number', 'default' => 3, 'col_span' => 'sm:col-span-3'],
                    'easy_invoice_remove_data_on_uninstall' => ['label' => __('Remove all data when the plugin is deleted', 'easy-invoice'), 'type' => 'checkbox', 'default' => 'no', 'col_span' => 'sm:col-span-6', 'description' => __('Off, deleting the plugin from the Plugins screen keeps every invoice, quote, payment, client record and setting, so reinstalling picks up where you left off. On, all of it is removed — invoices are tax records, so leave this off unless you are certain.', 'easy-invoice')],
                ]
            ],
        ];

        // Cache and filter the config
        $this->config_cache = apply_filters('easy_invoice_settings_fields_config', $config);

        return $this->config_cache;
    }

    /**
     * Get all gateway settings configurations
     *
     * @return array Gateway settings configs
     */
    private function getGatewaySettingsConfigs(): array {
        $gateway_configs = [];

        try {
            // Get gateway manager from the main plugin instance
            $gateway_manager = \EasyInvoice\EasyInvoice::getInstance()->getGatewayManager();
            $gateways = $gateway_manager->getGateways();

            // Collect settings from each gateway
            foreach ($gateways as $gateway_id => $gateway) {
                try {
                    $gateway_configs[$gateway_id] = $gateway->getSettingsConfig();
                } catch (\Exception $e) {
                    // Log the error but continue with other gateways
                    $gateway_configs[$gateway_id] = [];
                }
            }
        } catch (\Exception $e) {
        }

        return $gateway_configs;
    }

    /**
     * Prepare settings for display with proper filtering
     *
     * @param array $settings The settings array
     * @return array The filtered settings
     */
    public function prepareSettingsForDisplay($settings) {
        return apply_filters('easy_invoice_settings_for_display', $settings);
    }

    /**
     * Display method implementation
     *
     * @param array $args Display arguments
     * @return void
     */
    public function display(array $args = []) {
        $page = isset($args['page']) ? $args['page'] : '';
        $subsection = isset($args['subsection']) ? $args['subsection'] : '';

        // Clear cache to ensure new fields are recognized
        $this->clearCache();

        switch ($page) {
            case PagesSlugs::SETTINGS:
                if (!empty($subsection)) {
                    // Handle email settings subsections
                    $this->displayEmailSettingsPage($subsection);
                } else {
                    $this->displaySettingsPage();
                }
                break;

            default:
                $this->displaySettingsPage();
                break;
        }
    }

    /**
     * Display email settings page for specific subsection
     *
     * @param string $subsection The subsection to display
     * @return void
     */
    protected function displayEmailSettingsPage($subsection) {
        // Get settings configuration
        $settings_config = $this->get_settings_fields_config();
        $settings = $this->getSettings();

        // Filter to show only email settings
        $email_config = $settings_config['email'] ?? [];

        // Map subsection to the appropriate subsection in email config
        $subsection_mapping = [
            PagesSlugs::EMAIL_SETTINGS_GENERAL => 'general',
            PagesSlugs::EMAIL_SETTINGS_INVOICE => 'invoice_available',
            PagesSlugs::EMAIL_SETTINGS_QUOTE => 'quote_available',
            PagesSlugs::EMAIL_SETTINGS_PAYMENT => 'payment_received',
            PagesSlugs::EMAIL_SETTINGS_PAYMENT_REMINDER => 'payment_reminder',
        ];

        $target_subsection = $subsection_mapping[$subsection] ?? 'general';

        // Create a modified config with only the target subsection
        $filtered_config = [
            'email' => [
                'title' => __('Email Settings', 'easy-invoice'),
                'description' => __('Configure email sending options and templates', 'easy-invoice'),
                'icon' => 'fas fa-envelope',
                'subsections' => [
                    $target_subsection => $email_config['subsections'][$target_subsection] ?? []
                ]
            ]
        ];

        // Include the settings page template with filtered config
        include EASY_INVOICE_PLUGIN_DIR . 'templates/settings-page.php';
    }

    /**
     * Display settings page
     *
     * @return void
     */
    protected function displaySettingsPage() {
        $settings = $this->getSettings();

        // Allow developers to modify settings before display
        $settings = apply_filters('easy_invoice_before_display_settings', $settings);

        // Ensure payment methods are set (this is critical for gateway checkboxes)
        if (!isset($settings['easy_invoice_payment_methods']) || !is_array($settings['easy_invoice_payment_methods'])) {
            $settings['easy_invoice_payment_methods'] = get_option('easy_invoice_payment_methods', []);
        }

        // Ensure gateway order is set
        if (!isset($settings['easy_invoice_payment_gateway_order']) || !is_array($settings['easy_invoice_payment_gateway_order'])) {
            $settings['easy_invoice_payment_gateway_order'] = get_option('easy_invoice_payment_gateway_order', []);
        }

        $gateway_manager = \EasyInvoice\EasyInvoice::getInstance()->getGatewayManager();
        $all_gateways_unsorted = $gateway_manager->getGateways();
        $gateway_order_option = $settings['easy_invoice_payment_gateway_order'];
        $payment_methods_enabled = $settings['easy_invoice_payment_methods'];

        $all_gateways_sorted = [];
        if (!empty($gateway_order_option)) {
            foreach ($gateway_order_option as $gateway_id) {
                if (isset($all_gateways_unsorted[$gateway_id])) {
                    $all_gateways_sorted[$gateway_id] = $all_gateways_unsorted[$gateway_id];
                    unset($all_gateways_unsorted[$gateway_id]);
                }
            }
        }
        $all_gateways_sorted = array_merge($all_gateways_sorted, $all_gateways_unsorted);

        $settings_config = $this->get_settings_fields_config();

        // Prepare template variables
        $template_args = apply_filters('easy_invoice_settings_template_args', [
            'settings' => $settings,
            'settings_config' => $settings_config,
            'all_gateways_sorted' => $all_gateways_sorted,
            'payment_methods_enabled' => $payment_methods_enabled
        ]);

        // Display the template with template args
        $this->displayTemplate(
            apply_filters('easy_invoice_settings_template_path', EASY_INVOICE_PLUGIN_DIR . 'templates/settings-page.php'),
            $template_args
        );

        // Action for plugins to add their own content after the settings page
        do_action('easy_invoice_after_settings_page');
    }

    /**
     * Save payment settings
     *
     * @param array $posted_settings Posted settings
     * @param array $response Response array
     * @return void
     */
    private function savePaymentSettings($posted_settings, &$response) {
        // Handle payment methods
        if (isset($posted_settings['easy_invoice_payment_methods']) && is_array($posted_settings['easy_invoice_payment_methods'])) {
            $sanitized_methods = array_map('sanitize_key', $posted_settings['easy_invoice_payment_methods']);
            update_option('easy_invoice_payment_methods', $sanitized_methods);
            $response['saved_options']['easy_invoice_payment_methods'] = $sanitized_methods;
        } else {
            update_option('easy_invoice_payment_methods', []);
            $response['saved_options']['easy_invoice_payment_methods'] = [];
        }

        // Handle gateway order
        if (isset($posted_settings['easy_invoice_gateway_order']) && is_array($posted_settings['easy_invoice_gateway_order'])) {
            $gateway_order = array_map('sanitize_key', $posted_settings['easy_invoice_gateway_order']);
            update_option('easy_invoice_payment_gateway_order', $gateway_order);
            $response['saved_options']['easy_invoice_gateway_order'] = $gateway_order;
        } else {
            update_option('easy_invoice_payment_gateway_order', []);
            $response['saved_options']['easy_invoice_gateway_order'] = [];
        }

        // Handle gateway display names
        $gateway_manager = \EasyInvoice\EasyInvoice::getInstance()->getGatewayManager();
        $all_gateways = $gateway_manager->getGateways();

        foreach ($all_gateways as $gateway_id => $gateway) {
            $display_name_key = 'easy_invoice_gateway_display_name_' . $gateway_id;
            if (isset($posted_settings[$display_name_key])) {
                $display_name = wp_strip_all_tags(wp_unslash($posted_settings[$display_name_key]));
                update_option($display_name_key, $display_name);
                $response['saved_options'][$display_name_key] = $display_name;
            }
        }

        // Handle gateway-specific settings
        try {
            $gateway_configs = $this->getGatewaySettingsConfigs();
            foreach ($gateway_configs as $gateway_id => $gateway_config) {
                if (isset($gateway_config['fields']) && is_array($gateway_config['fields'])) {
                    foreach ($gateway_config['fields'] as $option_key => $field_config) {
                        if (isset($posted_settings[$option_key])) {
                            $this->sanitizeAndSaveSetting($option_key, $field_config, $posted_settings[$option_key], $response);
                        } elseif ($field_config['type'] === 'checkbox') {
                            // Checkboxes not in POST are considered unchecked
                            update_option($option_key, 'no');
                            $response['saved_options'][$option_key] = 'no';
                        }
                    }
                }
            }
        } catch (\Exception $e) {
            throw $e; // Re-throw to be caught by the main try-catch
        }
    }

    /**
     * Save settings
     *
     * @return void
     */
    public function saveSettings() {
        // Verify nonce. $_POST['easy_invoice_settings_nonce'] was read unguarded, so a
        // request without it raised "Undefined array key" before the check could run.
        $nonce = isset($_POST['easy_invoice_settings_nonce'])
            ? sanitize_text_field(wp_unslash($_POST['easy_invoice_settings_nonce']))
            : '';
        if (!wp_verify_nonce($nonce, 'easy_invoice_settings')) {
            wp_die(esc_html__('Security check failed', 'easy-invoice'));
        }


        // Check permissions
        if (!current_user_can('manage_options')) {
            wp_die(esc_html__('You do not have permission to perform this action', 'easy-invoice'));
        }

        $posted_settings = $_POST['settings'] ?? [];

        $settings_config = $this->get_settings_fields_config();
        $response = [
            'success' => true,
            'message' => __('Settings saved successfully', 'easy-invoice'),
            'saved_options' => []
        ];

        try {
            // Process each section
            foreach ($settings_config as $section_id => $section_data) {
                if (isset($section_data['fields']) && is_array($section_data['fields'])) {
                    // Handle regular fields
                    foreach ($section_data['fields'] as $option_key => $field_config) {
                        if (isset($posted_settings[$option_key])) {
                            $this->sanitizeAndSaveSetting($option_key, $field_config, $posted_settings[$option_key], $response);
                        } elseif ($field_config['type'] === 'checkbox') {
                            // Checkboxes not in POST are considered unchecked
                            update_option($option_key, 'no');
                            $response['saved_options'][$option_key] = 'no';
                        }
                    }
                } else if (isset($section_data['subsections']) && is_array($section_data['subsections'])) {
                    // Handle sections with subsections (like invoice, quote, email, etc.)
                    foreach ($section_data['subsections'] as $subsection_id => $subsection_data) {
                        if (isset($subsection_data['fields']) && is_array($subsection_data['fields'])) {
                            foreach ($subsection_data['fields'] as $option_key => $field_config) {
                                if (isset($posted_settings[$option_key])) {
                                    $this->sanitizeAndSaveSetting($option_key, $field_config, $posted_settings[$option_key], $response);
                                } elseif ($field_config['type'] === 'checkbox') {
                                    // Checkboxes not in POST are considered unchecked
                                    update_option($option_key, 'no');
                                    $response['saved_options'][$option_key] = 'no';
                                }
                            }
                        }
                    }
                }
            }

            // Fields other code adds to the Tax section for display only
            // (`easy_invoice_tax_section_additional_fields`) are saved with the
            // same rules — the reverse-charge switch lived only there and could
            // never be turned on from the screen.
            $extra_tax_fields = apply_filters('easy_invoice_tax_section_additional_fields', [], []);
            $known = [];
            foreach ($settings_config as $section_data) {
                foreach ((array) ($section_data['fields'] ?? []) as $k => $unused) { $known[$k] = true; }
                foreach ((array) ($section_data['subsections'] ?? []) as $sub) { foreach ((array) ($sub['fields'] ?? []) as $k => $unused) { $known[$k] = true; } }
            }
            foreach ((array) $extra_tax_fields as $option_key => $field_config) {
                if (!is_string($option_key) || isset($known[$option_key]) || !is_array($field_config)) {
                    continue;
                }
                if (isset($posted_settings[$option_key])) {
                    $this->sanitizeAndSaveSetting($option_key, $field_config, $posted_settings[$option_key], $response);
                } elseif (($field_config['type'] ?? '') === 'checkbox') {
                    update_option($option_key, 'no');
                    $response['saved_options'][$option_key] = 'no';
                }
            }

            // Handle special sections like payment methods
            if (isset($settings_config['payment']) && isset($settings_config['payment']['is_special_section'])) {
                $this->savePaymentSettings($posted_settings, $response);
            }

            // Clear any caches
            wp_cache_flush();

            // Clear settings cache
            $this->settings_cache = null;
            self::$shared_settings_cache = null;

            // Log the save action
            $this->log('Settings saved successfully by user: ' . get_current_user_id());

        } catch (\Exception $e) {
            $response['success'] = false;
            $response['message'] = __('Error saving settings: ', 'easy-invoice') . $e->getMessage();
            $this->log('Error saving settings: ' . $e->getMessage());

        }

        // Clear settings cache to ensure new fields are recognized
        $this->clearCache();

        // Send JSON response
        wp_send_json($response);
    }

    /**
     * Sanitize and save a single setting
     *
     * @param string $option_key The option key
     * @param array $field_config The field configuration
     * @param mixed $value The value to sanitize and save
     * @param array &$response The response array to update
     * @return void
     */
    private function sanitizeAndSaveSetting($option_key, $field_config, $value, &$response) {
        if (in_array($option_key, [
            'easy_invoice_quote_accept_text',
            'easy_invoice_quote_accepted_message',
            'easy_invoice_quote_declined_message'
        ])) {
        }

        // Unslash the value to prevent double-escaping
        $value = wp_unslash($value);

        // Allow pre-sanitization filters
        $value = apply_filters('easy_invoice_pre_sanitize_option', $value, $option_key, $field_config);

        // Sanitize based on field type
        switch ($field_config['type']) {
            case 'email':
                $value = sanitize_email($value);
                break;
            case 'url':
                $value = esc_url_raw($value);
                break;
            case 'textarea':
                // Use wp_kses_post for textarea to allow safe HTML while preventing double-escaping
                $value = wp_kses_post($value);
                break;
            case 'wp_editor':
                // For wp_editor content, use WordPress's built-in sanitization
                if (is_string($value)) {
                    // Use wp_kses_post which expects unslashed data
                    $value = wp_kses_post($value);
                }
                break;
            case 'number':
                if (isset($field_config['step']) && strpos((string)$field_config['step'], '.') !== false) {
                    $value = floatval(wp_strip_all_tags(easy_invoice_str_replace(',', '.', $value)));
                } elseif (isset($field_config['min']) && intval($field_config['min']) < 0) {
                    $value = intval($value);
                } else {
                    $value = absint($value);
                }
                break;
            case 'checkbox':
                // For checkboxes, we expect 'yes' or 'no' values
                $value = ($value === 'yes' || $value === '1' || $value === true) ? 'yes' : 'no';
                break;
            case 'select':
                $value = sanitize_key($value);
                // Ensure currency codes are always uppercase
                if ($option_key === 'easy_invoice_currency_code') {
                    $value = strtoupper($value);
                }
                break;
            case 'multiselect':
                // For multiselect, ensure it's an array and sanitize each value
                if (!is_array($value)) {
                    $value = [];
                } else {
                    $value = array_map('sanitize_key', $value);
                }
                break;
            default:
                // For regular text fields, use wp_strip_all_tags to prevent double-escaping
                $value = wp_strip_all_tags($value);
                break;
        }

        // Allow post-sanitization value modification
        $value = apply_filters("easy_invoice_sanitize_option_{$option_key}", $value, $field_config);
        $value = apply_filters('easy_invoice_sanitize_option', $value, $option_key, $field_config);

        // Save the setting normally
        update_option($option_key, $value);
        $response['saved_options'][$option_key] = $value;
    }

    /**
     * Get all settings for the plugin
     *
     * @param bool $use_cache Whether to use cached settings
     * @return array The settings array
     */
    public function getSettings($use_cache = true): array {
        // Return cached settings if available
        if ($use_cache && is_array($this->settings_cache)) {
            return $this->settings_cache;
        }
        if ($use_cache && is_array(self::$shared_settings_cache)) {
            return $this->settings_cache = self::$shared_settings_cache;
        }

        $settings = [];
        $config = $this->get_settings_fields_config();

        foreach ($config as $section_id => $section_data) {
            if ($section_id === 'payment' && isset($section_data['gateways'])) {
                // Handle gateway fields specifically
                foreach ($section_data['gateways'] as $gateway_id => $gateway_config) {
                    // The per-gateway display name is saved by savePaymentSettings()
                    // but was never loaded back, so the field always showed the default.
                    $display_key = 'easy_invoice_gateway_display_name_' . $gateway_id;
                    $display_val = get_option($display_key, '');
                    if ('' !== $display_val) {
                        $settings[$display_key] = $display_val;
                    }
                    if (isset($gateway_config['fields']) && is_array($gateway_config['fields'])) {
                        foreach ($gateway_config['fields'] as $option_key => $field_data) {
                            $default_value = $field_data['default'] ?? '';
                            $settings[$option_key] = get_option($option_key, $default_value);
                        }
                    }
                }
            }
            else if (isset($section_data['subsections']) && is_array($section_data['subsections'])) {
                // Handle sections with subsections (like email, invoice, quote)
                foreach ($section_data['subsections'] as $subsection_id => $subsection_data) {
                    if (isset($subsection_data['fields']) && is_array($subsection_data['fields'])) {
                        foreach ($subsection_data['fields'] as $option_key => $field_data) {
                            $default_value = $field_data['default'] ?? '';

                            // Handle special defaults
                            if ($option_key === 'easy_invoice_email_from_name' && $default_value === get_bloginfo('name')) {
                                $default_value = get_bloginfo('name');
                            }
                            if ($option_key === 'easy_invoice_email_from_address' && $default_value === get_bloginfo('admin_email')) {
                                $default_value = get_bloginfo('admin_email');
                            }

                            // Check if field has a value_callback function
                            if (isset($field_data['value_callback']) && is_callable($field_data['value_callback'])) {
                                $settings[$option_key] = $field_data['value_callback']();
                            } else {
                                // Get option value or default
                                $settings[$option_key] = get_option($option_key, $default_value);
                            }
                        }
                    }
                }
            }
            else if (isset($section_data['fields']) && is_array($section_data['fields'])) {
                foreach ($section_data['fields'] as $option_key => $field_data) {
                    $default_value = $field_data['default'] ?? '';

                    // Handle special defaults
                    if ($option_key === 'easy_invoice_email_from_name' && $default_value === get_bloginfo('name')) {
                        $default_value = get_bloginfo('name');
                    }
                    if ($option_key === 'easy_invoice_email_from_address' && $default_value === get_bloginfo('admin_email')) {
                        $default_value = get_bloginfo('admin_email');
                    }

                    // Check if field has a value_callback function
                    if (isset($field_data['value_callback']) && is_callable($field_data['value_callback'])) {
                        $settings[$option_key] = $field_data['value_callback']();
                    } else {
                        // Get option value or default
                        $settings[$option_key] = get_option($option_key, $default_value);
                    }
                }
            }
        }

        // Add special array settings
        $settings['easy_invoice_payment_methods'] = get_option('easy_invoice_payment_methods', []);
        $settings['easy_invoice_payment_gateway_order'] = get_option('easy_invoice_payment_gateway_order', []);

        // Cache settings
        $this->settings_cache = $settings;
        self::$shared_settings_cache = $settings;

        // Allow third party to add/modify settings
        return apply_filters('easy_invoice_settings', $settings);
    }

    /**
     * Log debug information
     *
     * @param string $message The message to log
     * @return void
     */
    protected function log(string $message): void {

    }

    /**
     * Initialize default settings
     */
    public function initializeSettings() {
        // One-time migration (2.1.22): strip the leftover
        //   "<strong>Shortcode:</strong> <code>[easy_(invoice|quote)_url …]</code>"
        // reference line from saved email bodies. Originally intended as admin-side
        // documentation, but it lived inside the email template and got emailed to
        // clients verbatim. Guarded by a flag option so each install runs at most once.
        //
        // The regex tolerates the optional surrounding <p>...</p> because WP's
        // wp_kses_post may normalise the wrapping when the option is saved.
        if (!get_option('easy_invoice_email_shortcode_hint_removed_v2')) {
            foreach (['easy_invoice_invoice_email_body', 'easy_invoice_quote_email_body'] as $opt_key) {
                $body = get_option($opt_key, '');
                if (!is_string($body) || $body === '') {
                    continue;
                }
                $cleaned = preg_replace(
                    '#\s*(?:<p>\s*)?<strong>\s*Shortcode\s*:\s*</strong>\s*<code>\s*\[easy_(?:invoice|quote)_url[^\]]*\]\s*</code>(?:\s*</p>)?\s*#i',
                    '',
                    $body
                );
                if ($cleaned !== null && $cleaned !== $body) {
                    update_option($opt_key, $cleaned);
                }
            }
            update_option('easy_invoice_email_shortcode_hint_removed_v2', 1);
        }

        // Initialize invoice number settings if not already set
        if (!get_option('easy_invoice_invoice_prefix')) {
            update_option('easy_invoice_invoice_prefix', 'INV-');
        }

        if (!get_option('easy_invoice_next_invoice_number')) {
            update_option('easy_invoice_next_invoice_number', 1);
        }

        // Initialize other default settings
        if (!get_option('easy_invoice_invoice_due_days')) {
            update_option('easy_invoice_invoice_due_days', 30);
        }

        if (!get_option('easy_invoice_currency_code')) {
            update_option('easy_invoice_currency_code', 'USD');
        }

        if (!get_option('easy_invoice_currency_symbol')) {
            update_option('easy_invoice_currency_symbol', '$');
        }

        if (!get_option('easy_invoice_currency_position')) {
            update_option('easy_invoice_currency_position', 'left');
        }

        if (!get_option('easy_invoice_currency_symbol_type')) {
            update_option('easy_invoice_currency_symbol_type', 'symbol');
        }

        if (!get_option('easy_invoice_decimal_separator')) {
            update_option('easy_invoice_decimal_separator', '.');
        }

        if (!get_option('easy_invoice_thousands_separator')) {
            update_option('easy_invoice_thousands_separator', ',');
        }

        if (!get_option('easy_invoice_decimal_precision')) {
            update_option('easy_invoice_decimal_precision', 2);
        }

        // Initialize date format setting
        if (!get_option('easy_invoice_date_format')) {
            update_option('easy_invoice_date_format', 'us');
        }

        // Fix legacy date format values
        $current_date_format = get_option('easy_invoice_date_format');
        if ($current_date_format === 'mdy' || $current_date_format === 'm/d/Y') {
            update_option('easy_invoice_date_format', 'us');
        } elseif ($current_date_format === 'd/m/Y') {
            update_option('easy_invoice_date_format', 'uk');
        } elseif ($current_date_format === 'Y-m-d') {
            update_option('easy_invoice_date_format', 'iso');
        }

        // Initialize email settings
        if (!get_option('easy_invoice_email_from_name')) {
            update_option('easy_invoice_email_from_name', get_bloginfo('name'));
        }

        if (!get_option('easy_invoice_email_from_address')) {
            update_option('easy_invoice_email_from_address', get_bloginfo('admin_email'));
        }

        if (!get_option('easy_invoice_enable_email_styling')) {
            update_option('easy_invoice_enable_email_styling', 'yes');
        }

        if (!get_option('easy_invoice_bcc_admin')) {
            update_option('easy_invoice_bcc_admin', 'no');
        }

        if (!get_option('easy_invoice_admin_email')) {
            update_option('easy_invoice_admin_email', get_option('admin_email'));
        }

        // Initialize Text Settings
        $text_settings = [
            'easy_invoice_text_invoice' => __('Invoice', 'easy-invoice'),
            'easy_invoice_text_invoices' => __('Invoices', 'easy-invoice'),
            'easy_invoice_text_from' => __('From', 'easy-invoice'),
            'easy_invoice_text_to' => __('To', 'easy-invoice'),
            'easy_invoice_text_invoice_number' => __('Invoice Number', 'easy-invoice'),
            'easy_invoice_text_order_number' => __('Order Number', 'easy-invoice'),
            'easy_invoice_text_invoice_date' => __('Invoice Date', 'easy-invoice'),
            'easy_invoice_text_due_date' => __('Due Date', 'easy-invoice'),
            'easy_invoice_text_total_due' => __('Total Due', 'easy-invoice'),
            'easy_invoice_text_qty' => __('Qty', 'easy-invoice'),
            'easy_invoice_text_service' => __('Service', 'easy-invoice'),
            'easy_invoice_text_rate_price' => __('Rate', 'easy-invoice'),
            'easy_invoice_text_adjust' => __('Adjust', 'easy-invoice'),
            'easy_invoice_text_sub_total' => __('Sub Total', 'easy-invoice'),
            'easy_invoice_text_total' => __('Total', 'easy-invoice'),
            'easy_invoice_text_line_total' => __('Total', 'easy-invoice'),
            'easy_invoice_text_tax' => __('Tax', 'easy-invoice'),
            'easy_invoice_text_discount' => __('Discount', 'easy-invoice'),
            'easy_invoice_text_page' => __('Page', 'easy-invoice'),
            'easy_invoice_text_print' => __('Print', 'easy-invoice'),
            'easy_invoice_text_download_pdf' => __('Download as PDF', 'easy-invoice'),
            'easy_invoice_text_send_email' => __('Send Email', 'easy-invoice'),
            'easy_invoice_text_pay_now' => __('Pay Now', 'easy-invoice'),
            'easy_invoice_text_proceed_payment' => __('Proceed to payment', 'easy-invoice'),
            'easy_invoice_text_payment_gateway' => __('Invoice Payment Gateway', 'easy-invoice'),
            'easy_invoice_text_quote' => __('Quote', 'easy-invoice'),
            'easy_invoice_text_quotes' => __('Quotes', 'easy-invoice'),
            'easy_invoice_text_quote_number' => __('Quote Number', 'easy-invoice'),
            'easy_invoice_text_accept_quote' => __('Accept Quote', 'easy-invoice'),
            'easy_invoice_text_decline_quote' => __('Decline Quote', 'easy-invoice'),
            'easy_invoice_text_decline_reason' => __('Reason for declining', 'easy-invoice'),
            'easy_invoice_text_quote_amount' => __('Quote Amount', 'easy-invoice'),
            'easy_invoice_text_valid_until' => __('Valid Until Date', 'easy-invoice'),
            'easy_invoice_text_quote_date' => __('Quote Date', 'easy-invoice'),
            'easy_invoice_text_available' => __('Available', 'easy-invoice'),
            'easy_invoice_text_draft' => __('Draft', 'easy-invoice'),
            'easy_invoice_text_overdue' => __('Overdue', 'easy-invoice'),
            'easy_invoice_text_paid' => __('Paid', 'easy-invoice'),
            'easy_invoice_text_unpaid' => __('Unpaid', 'easy-invoice'),
            'easy_invoice_text_cancelled' => __('Cancelled', 'easy-invoice'),
        ];

        foreach ($text_settings as $option_key => $default_value) {
            if (!get_option($option_key)) {
                update_option($option_key, $default_value);
            }
        }

        if (!get_option('easy_invoice_invoice_email_enabled')) {
            update_option('easy_invoice_invoice_email_enabled', 'yes');
        }

        if (!get_option('easy_invoice_invoice_email_subject')) {
            update_option('easy_invoice_invoice_email_subject', __('Your Invoice #{{invoice_number}} from {{company_name}}', 'easy-invoice'));
        }

        if (!get_option('easy_invoice_invoice_email_body')) {
            update_option('easy_invoice_invoice_email_body', \EasyInvoice\Services\EmailManager::defaultTemplate('invoice'));
        }

        if (!get_option('easy_invoice_quote_email_enabled')) {
            update_option('easy_invoice_quote_email_enabled', 'yes');
        }

        if (!get_option('easy_invoice_quote_email_subject')) {
            update_option('easy_invoice_quote_email_subject', __('Your Quote #{{quote_number}} from {{company_name}}', 'easy-invoice'));
        }

        if (!get_option('easy_invoice_quote_email_body')) {
            update_option('easy_invoice_quote_email_body', \EasyInvoice\Services\EmailManager::defaultTemplate('quote'));
        }

        if (!get_option('easy_invoice_payment_email_enabled')) {
            update_option('easy_invoice_payment_email_enabled', 'yes');
        }

        if (!get_option('easy_invoice_payment_email_subject')) {
            update_option('easy_invoice_payment_email_subject', __('Payment Received - Invoice #{{invoice_number}}', 'easy-invoice'));
        }

        if (!get_option('easy_invoice_payment_email_body')) {
            update_option('easy_invoice_payment_email_body', \EasyInvoice\Services\EmailManager::defaultTemplate('payment'));
        }
    }

    /**
     * Test email functionality
     */
    public function testEmail(): void {
        // Verify nonce
        if (!wp_verify_nonce($_POST['nonce'] ?? '', 'easy_invoice_settings')) {
            wp_send_json_error(['message' => __('Security check failed', 'easy-invoice')]);
        }

        // Check permissions
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('You do not have permission to perform this action', 'easy-invoice')]);
        }

        // Get test email address
        $test_email = sanitize_email($_POST['test_email'] ?? '');
        if (empty($test_email)) {
            wp_send_json_error(['message' => __('Please provide a valid email address', 'easy-invoice')]);
        }

        // Test email sending
        $email_manager = \EasyInvoice\Services\EmailManager::getInstance();
        $result = $email_manager->testEmail($test_email);

        if ($result['success']) {
            wp_send_json_success($result);
        } else {
            wp_send_json_error($result);
        }
    }

    /**
     * Test payment reminder email functionality
     */
    public function testPaymentReminderEmail(): void {
        // Verify nonce
        if (!wp_verify_nonce($_POST['nonce'] ?? '', 'easy_invoice_settings')) {
            wp_send_json_error(['message' => __('Security check failed', 'easy-invoice')]);
        }

        // Check permissions
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('You do not have permission to perform this action', 'easy-invoice')]);
        }

        // Get test email address
        $test_email = sanitize_email($_POST['test_email'] ?? '');
        if (empty($test_email)) {
            wp_send_json_error(['message' => __('Please provide a valid email address', 'easy-invoice')]);
        }

        // Get payment reminder settings
        $subject = get_option('easy_invoice_pro_payment_reminder_subject', __('A friendly reminder - Invoice #{{invoice_number}}', 'easy-invoice'));
        $body = get_option('easy_invoice_pro_payment_reminder_body', '');

        if (empty($body)) {
            wp_send_json_error(['message' => __('Payment reminder email template is empty. Please configure the email message first.', 'easy-invoice')]);
        }

        // Create sample data for testing
        $sample_data = [
            'invoice_number' => 'TEST-001',
            'client_name' => 'Test Client',
            'company_name' => get_option('easy_invoice_company_name', get_bloginfo('name') ?: 'Easy Invoice'),
            'company_email' => get_option('easy_invoice_company_email', get_option('admin_email', '')),
            'total_amount' => '$1,000.00',
            'amount_due' => '$1,000.00',
            'due_date' => wp_date('Y-m-d', strtotime('+7 days')),
        ];

        // Replace placeholders
        foreach ($sample_data as $placeholder => $value) {
            $subject = easy_invoice_str_replace('{{' . $placeholder . '}}', $value, $subject);
            $body = easy_invoice_str_replace('{{' . $placeholder . '}}', $value, $body);
        }

        // Test email sending
        $email_manager = \EasyInvoice\Services\EmailManager::getInstance();
        $result = $email_manager->sendTestTemplateEmail($test_email, $subject, $body);

        if ($result['success']) {
            wp_send_json_success($result);
        } else {
            wp_send_json_error($result);
        }
    }

    /**
     * Test template email functionality
     */
    public function testTemplateEmail(): void {
        // Verify nonce
        if (!wp_verify_nonce($_POST['nonce'] ?? '', 'easy_invoice_settings')) {
            wp_send_json_error(['message' => __('Security check failed', 'easy-invoice')]);
        }

        // Check permissions
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('You do not have permission to perform this action', 'easy-invoice')]);
        }

        // Get test email address and template type
        $test_email = sanitize_email($_POST['test_email'] ?? '');
        $template_type = sanitize_text_field($_POST['template_type'] ?? '');

        if (empty($test_email)) {
            wp_send_json_error(['message' => __('Please provide a valid email address', 'easy-invoice')]);
        }

        if (empty($template_type)) {
            wp_send_json_error(['message' => __('Template type is required', 'easy-invoice')]);
        }

        // Get the actual configured email settings
        $subject = '';
        $body = '';

        switch ($template_type) {
            case 'invoice_available':
                $subject = get_option('easy_invoice_invoice_email_subject', __('Your Invoice #{{invoice_number}} from {{company_name}}', 'easy-invoice'));
                $body = get_option('easy_invoice_invoice_email_body', self::getInvoiceEmailBody());
                break;
            case 'quote_available':
                $subject = get_option('easy_invoice_quote_email_subject', __('Your Quote #{{quote_number}} from {{company_name}}', 'easy-invoice'));
                $body = get_option('easy_invoice_quote_email_body', self::getQuoteEmailBody());
                break;
            case 'payment_received':
                $subject = get_option('easy_invoice_payment_email_subject', __('Payment Received - Invoice #{{invoice_number}}', 'easy-invoice'));
                $body = get_option('easy_invoice_payment_email_body', self::getPaymentEmailBody());
                break;
            default:
                wp_send_json_error(['message' => __('Invalid template type', 'easy-invoice')]);
        }

        // Ensure we have valid strings
        $subject = is_string($subject) ? $subject : '';
        $body = is_string($body) ? $body : '';

        if (empty($subject) || empty($body)) {
            wp_send_json_error(['message' => __('Email template is empty or invalid', 'easy-invoice')]);
        }

        // Create sample data for testing
        $sample_data = [
            'invoice_number' => 'TEST-001',
            'quote_number' => 'TEST-Q-001',
            'client_name' => 'Test Client',
            'company_name' => get_option('easy_invoice_company_name', get_bloginfo('name') ?: 'Easy Invoice'),
            'company_email' => get_option('easy_invoice_company_email', get_option('admin_email', '')),
            'total_amount' => function_exists('easy_invoice_format_money') ? easy_invoice_format_money(1000) : '$1,000.00',
            'payment_amount' => function_exists('easy_invoice_format_money') ? easy_invoice_format_money(1000) : '$1,000.00',
            'due_date' => wp_date('Y-m-d', strtotime('+30 days')),
            'expiry_date' => wp_date('Y-m-d', strtotime('+30 days')),
            'payment_date' => current_time('Y-m-d'),
            'payment_method' => 'Credit Card',
            'transaction_id' => 'TXN-' . uniqid(),
            'payment_terms' => get_option('easy_invoice_payment_terms', __('Due on receipt', 'easy-invoice')),
            // Everything the stock templates can mention, so a test never shows a raw tag.
            'amount_due' => function_exists('easy_invoice_format_money') ? easy_invoice_format_money(1000) : '$1,000.00',
            'subtotal' => function_exists('easy_invoice_format_money') ? easy_invoice_format_money(1000) : '$1,000.00',
            'tax_amount' => function_exists('easy_invoice_format_money') ? easy_invoice_format_money(0) : '$0.00',
            'discount_amount' => function_exists('easy_invoice_format_money') ? easy_invoice_format_money(0) : '$0.00',
            'issue_date' => current_time('Y-m-d'),
            'invoice_title' => __('Sample invoice', 'easy-invoice'),
            'quote_title' => __('Sample quote', 'easy-invoice'),
            'invoice_url' => home_url('/'),
            'quote_url' => home_url('/'),
            'payment_url' => home_url('/'),
            'site_url' => home_url('/'),
            'admin_url' => admin_url(),
            'client_email' => 'client@example.com',
            'client_first_name' => 'Test',
            'client_last_name' => 'Client',
            'client_address' => '1 Example Street',
            'company_phone' => get_option('easy_invoice_company_phone', ''),
            'company_address' => get_option('easy_invoice_company_address', ''),
            'company_website' => get_option('easy_invoice_company_website', ''),
        ];

        // Ensure all sample data values are strings
        foreach ($sample_data as $key => $value) {
            $sample_data[$key] = is_string($value) ? $value : (string) $value;
        }

        // Replace placeholders in subject and body
        foreach ($sample_data as $placeholder => $value) {
            $subject = easy_invoice_str_replace('{{' . $placeholder . '}}', $value, $subject);
            $body = easy_invoice_str_replace('{{' . $placeholder . '}}', $value, $body);
        }

        // Test email sending
        $email_manager = \EasyInvoice\Services\EmailManager::getInstance();
        $result = $email_manager->sendTestTemplateEmail($test_email, $subject, $body);

        if ($result['success']) {
            wp_send_json_success($result);
        } else {
            wp_send_json_error($result);
        }
    }

    /**
     * Check if quote adjust field should be shown
     *
     * @return bool True if adjust field should be shown
     */
    public static function shouldShowQuoteAdjustField(): bool {
        return get_option('easy_invoice_quote_show_adjust_field', 'yes') === 'yes';
    }

    /**
     * Check if invoice adjust field should be shown
     *
     * @return bool True if adjust field should be shown
     */
    public static function shouldShowInvoiceAdjustField(): bool {
        return get_option('easy_invoice_invoice_show_adjust_field', 'yes') === 'yes';
    }

    /**
     * Get invoice prefix
     *
     * @return string Invoice prefix
     */
    public static function getInvoicePrefix(): string {
        return get_option('easy_invoice_invoice_prefix', 'EIN_');
    }

    /**
     * Get invoice starting number
     *
     * @return int Invoice starting number
     */
    public static function getInvoiceStartingNumber(): int {
        return (int) get_option('easy_invoice_invoice_starting_number', 0);
    }

    /**
     * Get invoice terms and conditions
     *
     * @return string Invoice terms and conditions
     */
    public static function getInvoiceTermsConditions(): string {
        return get_option('easy_invoice_invoice_terms_conditions', __('Payment is due within 30 days from date of invoice', 'easy-invoice'));
    }

    /**
     * Get invoice footer text
     *
     * @return string Invoice footer text
     */
    public static function getInvoiceFooterText(): string {
        return get_option('easy_invoice_invoice_footer_text', '');
    }

    /**
     * Get quote prefix
     *
     * @return string Quote prefix
     */
    public static function getQuotePrefix(): string {
        return get_option('easy_invoice_quote_prefix', 'EIQN_');
    }

    /**
     * Get quote starting number
     *
     * @return int Quote starting number
     */
    public static function getQuoteStartingNumber(): int {
        return (int) get_option('easy_invoice_quote_starting_number', 1);
    }

    /**
     * Get quote terms and conditions
     *
     * @return string Quote terms and conditions
     */
    public static function getQuoteTermsConditions(): string {
        return get_option('easy_invoice_quote_terms_conditions', __('This quote has a fixed price. Upon acceptance, we kindly ask for a 25% deposit prior to initiating the work.', 'easy-invoice'));
    }

    /**
     * Get quote footer text
     *
     * @return string Quote footer text
     */
    public static function getQuoteFooterText(): string {
        return get_option('easy_invoice_quote_footer_text', __('Thank you for your business.', 'easy-invoice'));
    }

    /**
     * Check if quote accept button should be shown
     *
     * @return bool True if accept button should be shown
     */
    public static function shouldShowQuoteAcceptButton(): bool {
        return get_option('easy_invoice_quote_accept_button', 'yes') === 'yes';
    }

    /**
     * Get quote accept action
     *
     * @return string Quote accept action
     */
    public static function getQuoteAcceptAction(): string {
        return get_option('easy_invoice_quote_accept_action', 'convert');
    }

    /**
     * Get quote accept text
     *
     * @return string Quote accept text
     */
    public static function getQuoteAcceptText(): string {
        return get_option('easy_invoice_quote_accept_text', __('Important: When you accept this Quote, an Invoice will be created automatically. This will form a legally binding contract.', 'easy-invoice'));
    }

    /**
     * Get accepted quote message
     *
     * @return string Accepted quote message
     */
    public static function getAcceptedQuoteMessage(): string {
        return get_option('easy_invoice_quote_accepted_message', __('You\'ve confirmed the Quote.<br>We\'ll get in touch with you shortly.', 'easy-invoice'));
    }

    /**
     * Check if decline reason is required
     *
     * @return bool True if decline reason is required
     */
    public static function isDeclineReasonRequired(): bool {
        return get_option('easy_invoice_quote_decline_reason_required', 'no') === 'yes';
    }

    /**
     * Get declined quote message
     *
     * @return string Declined quote message
     */
    public static function getDeclinedQuoteMessage(): string {
        return get_option('easy_invoice_quote_declined_message', '');
    }

    // Email Settings Helper Functions

    /**
     * Check if invoice available email is enabled
     *
     * @return bool True if invoice email is enabled
     */
    public static function isInvoiceEmailEnabled(): bool {
        return get_option('easy_invoice_invoice_email_enabled', 'yes') === 'yes';
    }

    /**
     * Get invoice email subject
     *
     * @return string Invoice email subject
     */
    public static function getInvoiceEmailSubject(): string {
        return get_option('easy_invoice_invoice_email_subject', __('Your Invoice #{{invoice_number}} from {{company_name}}', 'easy-invoice'));
    }

    /**
     * Get invoice email body
     *
     * @return string Invoice email body
     */
    public static function getInvoiceEmailBody(): string {
        return get_option('easy_invoice_invoice_email_body', \EasyInvoice\Services\EmailManager::defaultTemplate('invoice'));
    }

    /**
     * Check if quote available email is enabled
     *
     * @return bool True if quote email is enabled
     */
    public static function isQuoteEmailEnabled(): bool {
        return get_option('easy_invoice_quote_email_enabled', 'yes') === 'yes';
    }

    /**
     * Get quote email subject
     *
     * @return string Quote email subject
     */
    public static function getQuoteEmailSubject(): string {
        return get_option('easy_invoice_quote_email_subject', __('Your Quote #{{quote_number}} from {{company_name}}', 'easy-invoice'));
    }

    /**
     * Get quote email body
     *
     * @return string Quote email body
     */
    public static function getQuoteEmailBody(): string {
        return get_option('easy_invoice_quote_email_body', \EasyInvoice\Services\EmailManager::defaultTemplate('quote'));
    }

    /**
     * Check if payment received email is enabled
     *
     * @return bool True if payment email is enabled
     */
    public static function isPaymentEmailEnabled(): bool {
        return get_option('easy_invoice_payment_email_enabled', 'yes') === 'yes';
    }

    /**
     * Get payment email subject
     *
     * @return string Payment email subject
     */
    public static function getPaymentEmailSubject(): string {
        return get_option('easy_invoice_payment_email_subject', __('Payment Received - Invoice #{{invoice_number}}', 'easy-invoice'));
    }

    /**
     * Get payment email body
     *
     * @return string Payment email body
     */
    public static function getPaymentEmailBody(): string {
        return get_option('easy_invoice_payment_email_body', \EasyInvoice\Services\EmailManager::defaultTemplate('payment'));
    }

    // ========================================
    // TEXT SETTINGS HELPER FUNCTIONS
    // ========================================
    //
    // USAGE IN TEMPLATES:
    // Instead of: echo __('Invoice', 'easy-invoice');
    // Use: echo \EasyInvoice\Controllers\SettingsController::getTextInvoice();
    //
    // Example:
    // <h1>echo \EasyInvoice\Controllers\SettingsController::getTextInvoice();</h1>
    // <p>echo \EasyInvoice\Controllers\SettingsController::getTextFrom(); : echo $company_name;</p>
    // <p>echo \EasyInvoice\Controllers\SettingsController::getTextTo(); : echo $client_name;</p>
    //

    /**
     * Get custom text setting with fallback to default
     *
     * @param string $key Text setting key
     * @param string $default Default text
     * @return string Custom text or default
     */
    /**
     * Every document label the site can override, with its saved value.
     *
     * The getText*() methods below are what templates call; this list exists
     * for anything that needs the whole set at once -- the Pro Translations
     * page offers each saved label for translation.
     *
     * @return array<string,array{label:string,default:string,value:string}> keyed by setting key.
     */
    public static function getTextLabels(): array {
        $labels = [
            'invoice'        => __('Invoice', 'easy-invoice'),
            'invoices'       => __('Invoices', 'easy-invoice'),
            'to'             => __('To', 'easy-invoice'),
            'from'           => __('From', 'easy-invoice'),
            'invoice_number' => __('Invoice Number', 'easy-invoice'),
            'invoice_date'   => __('Invoice Date', 'easy-invoice'),
            'due_date'       => __('Due Date', 'easy-invoice'),
            'total_due'      => __('Total Due', 'easy-invoice'),
            'qty'            => __('Qty', 'easy-invoice'),
            'service'        => __('Service', 'easy-invoice'),
            'rate_price'     => __('Rate', 'easy-invoice'),
            'adjust'         => __('Adjust', 'easy-invoice'),
            'sub_total'      => __('Sub Total', 'easy-invoice'),
            'total'          => __('Total', 'easy-invoice'),
            'tax'            => __('Tax', 'easy-invoice'),
            'discount'       => __('Discount', 'easy-invoice'),
            'print'          => __('Print', 'easy-invoice'),
            'download_pdf'   => __('Download as PDF', 'easy-invoice'),
            'send_email'     => __('Send Email', 'easy-invoice'),
            'pay_now'        => __('Pay Now', 'easy-invoice'),
            'quote'          => __('Quote', 'easy-invoice'),
            'quote_number'   => __('Quote Number', 'easy-invoice'),
            'quote_date'     => __('Quote Date', 'easy-invoice'),
            'valid_until'    => __('Valid Until Date', 'easy-invoice'),
            'accept_quote'   => __('Accept Quote', 'easy-invoice'),
            'decline_quote'  => __('Decline Quote', 'easy-invoice'),
            'decline_reason' => __('Reason for declining', 'easy-invoice'),
        ];
        $out = [];
        foreach ($labels as $key => $default) {
            $out[$key] = [
                'label'   => ucfirst(str_replace('_', ' ', $key)),
                'default' => $default,
                'value'   => (string) get_option('easy_invoice_text_' . $key, $default),
            ];
        }
        return $out;
    }

    public static function getTextSetting(string $key, string $default): string {
        $option_key = 'easy_invoice_text_' . $key;
        $value      = (string) get_option($option_key, $default);

        /**
         * Filter a document label after the saved override is applied.
         *
         * Saved labels are literal text and skip gettext, so anything that
         * renders a document in another language (Pro's Client Language) uses
         * this to run them through the active locale.
         *
         * @param string $value   Label to show.
         * @param string $key     Setting key, e.g. 'total'.
         * @param string $default Translated default.
         */
        return (string) apply_filters('easy_invoice_text_setting', $value, $key, $default);
    }

    // Invoice Text Settings
    public static function getTextInvoice(): string {
        return self::getTextSetting('invoice', __('Invoice', 'easy-invoice'));
    }

    public static function getTextInvoices(): string {
        return self::getTextSetting('invoices', __('Invoices', 'easy-invoice'));
    }

    public static function getTextTo(): string {
        return self::getTextSetting('to', __('To', 'easy-invoice'));
    }

    public static function getTextInvoiceNumber(): string {
        return self::getTextSetting('invoice_number', __('Invoice Number', 'easy-invoice'));
    }

    public static function getTextInvoiceDate(): string {
        return self::getTextSetting('invoice_date', __('Invoice Date', 'easy-invoice'));
    }

    public static function getTextDueDate(): string {
        return self::getTextSetting('due_date', __('Due Date', 'easy-invoice'));
    }

    public static function getTextTotalDue(): string {
        return self::getTextSetting('total_due', __('Total Due', 'easy-invoice'));
    }

    public static function getTextQty(): string {
        return self::getTextSetting('qty', __('Qty', 'easy-invoice'));
    }

    public static function getTextService(): string {
        return self::getTextSetting('service', __('Service', 'easy-invoice'));
    }

    public static function getTextRatePrice(): string {
        return self::getTextSetting('rate_price', __('Rate', 'easy-invoice'));
    }

    public static function getTextAdjust(): string {
        return self::getTextSetting('adjust', __('Adjust', 'easy-invoice'));
    }

    public static function getTextSubTotal(): string {
        return self::getTextSetting('sub_total', __('Sub Total', 'easy-invoice'));
    }

    public static function getTextTotal(): string {
        return self::getTextSetting('total', __('Total', 'easy-invoice'));
    }

    /** Heading of the line-item amount column, separate from the grand total. */
    public static function getTextLineTotal(): string {
        return self::getTextSetting('line_total', __('Total', 'easy-invoice'));
    }

    public static function getTextTax(): string {
        return self::getTextSetting('tax', __('Tax', 'easy-invoice'));
    }

    public static function getTextDiscount(): string {
        return self::getTextSetting('discount', __('Discount', 'easy-invoice'));
    }

    public static function getTextPrint(): string {
        return self::getTextSetting('print', __('Print', 'easy-invoice'));
    }

    public static function getTextDownloadPdf(): string {
        return self::getTextSetting('download_pdf', __('Download as PDF', 'easy-invoice'));
    }

    public static function getTextSendEmail(): string {
        return self::getTextSetting('send_email', __('Send Email', 'easy-invoice'));
    }

    public static function getTextPayNow(): string {
        return self::getTextSetting('pay_now', __('Pay Now', 'easy-invoice'));
    }

    // Quote Text Settings
    public static function getTextQuote(): string {
        return self::getTextSetting('quote', __('Quote', 'easy-invoice'));
    }

    public static function getTextQuoteNumber(): string {
        return self::getTextSetting('quote_number', __('Quote Number', 'easy-invoice'));
    }

    public static function getTextAcceptQuote(): string {
        return self::getTextSetting('accept_quote', __('Accept Quote', 'easy-invoice'));
    }

    public static function getTextDeclineQuote(): string {
        return self::getTextSetting('decline_quote', __('Decline Quote', 'easy-invoice'));
    }

    public static function getTextDeclineReason(): string {
        return self::getTextSetting('decline_reason', __('Reason for declining', 'easy-invoice'));
    }

    public static function getTextValidUntil(): string {
        return self::getTextSetting('valid_until', __('Valid Until Date', 'easy-invoice'));
    }

    public static function getTextQuoteDate(): string {
        return self::getTextSetting('quote_date', __('Quote Date', 'easy-invoice'));
    }

    public static function getTextFrom(): string {
        return self::getTextSetting('from', __('From', 'easy-invoice'));
    }

    /**
     * Get the actual date format string from the stored format identifier
     *
     * @param string $format_identifier The format identifier (us, uk, iso)
     * @return string The actual date format string
     */
    public static function getDateFormatString($format_identifier = null): string {
        if ($format_identifier === null) {
            $format_identifier = get_option('easy_invoice_date_format', 'us');
        }

        switch ($format_identifier) {
            case 'uk':
                return 'd/m/Y';
            case 'iso':
                return 'Y-m-d';
            case 'us':
            default:
                return 'm/d/Y';
        }
    }

    /**
     * Get the current date format string
     *
     * @return string The current date format string
     */
    public static function getCurrentDateFormat(): string {
        return self::getDateFormatString();
    }

    /**
     * Format a date using the Easy Invoice date format setting
     *
     * @param string|int $date The date to format (timestamp or date string)
     * @return string The formatted date
     */
    public static function formatDate($date): string {
        $format = self::getCurrentDateFormat();
        $timestamp = is_numeric($date) ? $date : strtotime($date);
        return date_i18n($format, $timestamp);
    }

    /**
     * AJAX handler for regenerating invoice numbers
     */
    public function ajaxRegenerateInvoiceNumbers() {

        // Verify nonce
        $nonce = $_POST['nonce'] ?? $_POST['easy_invoice_settings_nonce'] ?? '';
        if (!wp_verify_nonce($nonce, 'easy_invoice_settings')) {
            wp_send_json_error(['message' => __('Security check failed', 'easy-invoice')]);
        }

        // Check permissions
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('You do not have permission to perform this action', 'easy-invoice')]);
        }

        try {
            // Get the current next invoice number
            $current_next_number = intval(get_option('easy_invoice_next_invoice_number', 1));
            $prefix = get_option('easy_invoice_invoice_prefix', 'EIIN_');

            // Start regeneration from the current next number
            $starting_number = $current_next_number;

            // Get all invoices ordered by creation date
            $invoices = get_posts([
                'post_type' => 'easy_invoice',
                'post_status' => 'publish',
                'numberposts' => -1,
                'orderby' => 'date',
                'order' => 'ASC'
            ]);

            if (empty($invoices)) {
                wp_send_json_success([
                    'message' => __('No invoices found to regenerate', 'easy-invoice'),
                    'regenerated_count' => 0
                ]);
            }

            $regenerated_count = 0;
            $current_number = $starting_number;

            foreach ($invoices as $invoice) {
                // Generate new invoice number
                $new_invoice_number = $prefix . str_pad($current_number, 6, '0', STR_PAD_LEFT);

                // Update the invoice number
                update_post_meta($invoice->ID, '_easy_invoice_number', $new_invoice_number);

                $regenerated_count++;
                $current_number++;
            }

            // Update the next invoice number counter to continue from the last regenerated number + 1
            update_option('easy_invoice_next_invoice_number', $current_number);

            wp_send_json_success([
                /* translators: %d: number regenerated. */
                'message' => sprintf(__('Successfully regenerated %d invoice numbers', 'easy-invoice'), $regenerated_count),
                'regenerated_count' => $regenerated_count,
                'next_number' => $current_number
            ]);

        } catch (Exception $e) {
            wp_send_json_error(['message' => __('Failed to regenerate invoice numbers', 'easy-invoice')]);
        }
    }

    /**
     * AJAX handler for regenerating quote numbers
     */
    public function ajaxRegenerateQuoteNumbers() {

        // Verify nonce
        $nonce = $_POST['nonce'] ?? $_POST['easy_invoice_settings_nonce'] ?? '';
        if (!wp_verify_nonce($nonce, 'easy_invoice_settings')) {
            wp_send_json_error(['message' => __('Security check failed', 'easy-invoice')]);
        }

        // Check permissions
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('You do not have permission to perform this action', 'easy-invoice')]);
        }

        try {
            $current_next_number = intval(get_option('easy_invoice_next_quote_number', 1));
            $prefix = get_option('easy_invoice_quote_prefix', 'QT-');

            $starting_number = $current_next_number;

            $quotes = get_posts([
                'post_type' => 'easy_invoice_quote',
                'post_status' => 'publish',
                'numberposts' => -1,
                'orderby' => 'date',
                'order' => 'ASC',
                'meta_query' => [
                    [
                        'key' => '_easy_invoice_quote_number',
                        'compare' => 'EXISTS'
                    ]
                ]
            ]);

            $regenerated_count = 0;
            $current_number = $starting_number;

            foreach ($quotes as $quote_post) {
                $new_quote_number = $prefix . str_pad($current_number, 6, '0', STR_PAD_LEFT);
                update_post_meta($quote_post->ID, '_easy_invoice_quote_number', $new_quote_number);
                $regenerated_count++;
                $current_number++;
            }

            update_option('easy_invoice_next_quote_number', $current_number);

            wp_send_json_success([
                /* translators: %d: number regenerated. */
                'message' => sprintf(__('Successfully regenerated %d quote numbers', 'easy-invoice'), $regenerated_count),
                'regenerated_count' => $regenerated_count,
                'next_number' => $current_number
            ]);

        } catch (Exception $e) {
            wp_send_json_error(['message' => __('Failed to regenerate quote numbers', 'easy-invoice')]);
        }
    }
}

```
