<?php
/**
 * Quote Controller
 *
 * @package     EasyInvoice
 * @author      Your Name
 * @copyright   Copyright (c) 2023, Your Company
 * @license     http://opensource.org/licenses/gpl-2.0.php GNU Public License
 * @since       1.0.0
 */

namespace EasyInvoice\Controllers;

use EasyInvoice\Repositories\QuoteRepository;
use EasyInvoice\Repositories\ClientRepository;
use EasyInvoice\Forms\FormProcessor;
use EasyInvoice\Constants\PagesSlugs;
use EasyInvoice\Constants\PostTypes;
use EasyInvoice\Services\QuoteLogService;

/**
 * Quote Controller
 *
 * Handles quote-related operations and displays.
 *
 * @since 1.0.0
 */
class QuoteController {

    /**
     * Quote repository
     *
     * @var QuoteRepository
     */
    private $quote_repository;

    /**
     * Client repository
     *
     * @var ClientRepository
     */
    private $client_repository;

    /**
     * Form processor
     *
     * @var FormProcessor
     */
    private $form_processor;

    /**
     * Quote log service
     *
     * @var QuoteLogService
     */
    private $quote_log_service;

    /**
     * Constructor
     *
     * @since 1.0.0
     */
    public function __construct() {
        $this->quote_repository = new QuoteRepository();
        $this->client_repository = new ClientRepository();
        $this->form_processor = new FormProcessor();
        $this->quote_log_service = new QuoteLogService();
    }

    /**
     * Initialize the controller
     *
     * @since 1.0.0
     */
    public function init(): void {
        // Allow plugins to extend the controller initialization
        do_action('easy_invoice_quote_controller_before_init', $this);

        // Add AJAX handlers
        add_action('wp_ajax_easy_invoice_delete_quote', [$this, 'handleDeleteQuote']);
        add_action('wp_ajax_easy_invoice_get_quote', [$this, 'handleGetQuote']);
        add_action('wp_ajax_easy_invoice_load_quote_template', [$this, 'handleLoadQuoteTemplate']);
        add_action('wp_ajax_easy_invoice_create_new_quote', [$this, 'handleCreateNewQuote']);
        add_action('wp_ajax_easy_invoice_search_clients', [$this, 'handleSearchClients']);
        add_action('wp_ajax_easy_invoice_load_quote_form', [$this, 'handleLoadQuoteForm']);
        add_action('wp_ajax_easy_invoice_accept_quote', [$this, 'handleAcceptQuote']);
        add_action('wp_ajax_easy_invoice_decline_quote', [$this, 'handleDeclineQuote']);
        add_action('wp_ajax_nopriv_easy_invoice_accept_quote', [$this, 'handleAcceptQuote']);
        add_action('wp_ajax_nopriv_easy_invoice_decline_quote', [$this, 'handleDeclineQuote']);
        add_action('wp_ajax_easy_invoice_update_existing_quotes', [$this, 'handleUpdateExistingQuotes']);

        // Add missing AJAX handlers for quote listing actions
        add_action('wp_ajax_easy_invoice_bulk_quote_action', [$this, 'handleBulkQuoteAction']);
        add_action('wp_ajax_easy_invoice_trash_quote', [$this, 'handleTrashQuote']);
        add_action('wp_ajax_easy_invoice_draft_quote', [$this, 'handleDraftQuote']);

        // Add regular POST form handlers for quote actions
        add_action('init', [$this, 'handleQuoteFormActions']);

        // Add new AJAX handler for restoring a trashed quote
        add_action('wp_ajax_easy_invoice_restore_quote', [ $this, 'handleRestoreQuote' ]);

        // Add new AJAX handler for emptying trash
        add_action('wp_ajax_easy_invoice_empty_trash', [ $this, 'handleEmptyTrash' ]);

        // Add new AJAX handler for getting quote logs
        add_action('wp_ajax_easy_invoice_get_quote_logs', [ $this, 'handleGetQuoteLogs' ]);

        // Allow plugins to extend the controller initialization
        do_action('easy_invoice_quote_controller_after_init', $this);
    }

    /**
     * Display quote pages
     *
     * @since 1.0.0
     * @param array $args Display arguments
     */
    public function display(array $args = []): void {
        // Allow plugins to modify display arguments
        $args = apply_filters('easy_invoice_quote_controller_display_args', $args);

        $page = $args['page'] ?? '';

        // Allow plugins to modify the page before processing
        $page = apply_filters('easy_invoice_quote_controller_display_page', $page, $args);

        switch ($page) {
            case PagesSlugs::ALL_QUOTES:
                $this->displayListing();
                break;

            case PagesSlugs::QUOTE_NEW:
                $this->displayBuilder();
                break;

            case PagesSlugs::QUOTE_PREVIEW:
                $this->displayPreview($args);
                break;

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

        // Allow plugins to perform actions after display
        do_action('easy_invoice_quote_controller_after_display', $page, $args);
    }

    /**
     * Display quote listing page
     *
     * @since 1.0.0
     */
    private function displayListing(): void {
        // First, get all counts independently of any filtering
        global $wpdb;

        // Get trash count first (based on post_status)
        $trash_count = (int)$wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$wpdb->posts}
            WHERE post_type = %s AND post_status = 'trash'",
            PostTypes::EASY_INVOICE_QUOTE_POST_TYPE
        ));

        // Get counts for each meta status (excluding trashed posts)
        $status_counts = $wpdb->get_results($wpdb->prepare(
            "SELECT COALESCE(pm.meta_value, 'draft') as status, COUNT(*) as count
            FROM {$wpdb->posts} p
            LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_easy_invoice_quote_status'
            WHERE p.post_type = %s
            AND p.post_status != 'trash'
            GROUP BY COALESCE(pm.meta_value, 'draft')",
            PostTypes::EASY_INVOICE_QUOTE_POST_TYPE
        ));

        // Initialize counts
        $draft_count = 0;
        $available_count = 0;
        $sent_count = 0;
        $accepted_count = 0;
        $declined_count = 0;
        $expired_count = 0;
        $cancelled_count = 0;
        $all_count = 0;

        // Process status counts
        foreach ($status_counts as $status) {
            $count = (int)$status->count;
            $all_count += $count;  // Add to total (excluding trash)

            switch ($status->status) {
                case 'draft':
                    $draft_count = $count;
                    break;
                case 'available':
                    $available_count = $count;
                    break;
                case 'sent':
                    $sent_count = $count;
                    break;
                case 'accepted':
                    $accepted_count = $count;
                    break;
                case 'declined':
                    $declined_count = $count;
                    break;
                case 'expired':
                    $expired_count = $count;
                    break;
                case 'cancelled':
                    $cancelled_count = $count;
                    break;
            }
        }

        // Now handle the display filtering
        // Allow plugins to perform actions before displaying listing
        do_action('easy_invoice_quote_controller_before_display_listing');

        // Get filter parameters
        $status_filter = isset($_GET['status']) ? sanitize_text_field($_GET['status']) : '';
        $search_query = isset($_GET['search']) ? sanitize_text_field(wp_unslash($_GET['search'])) : '';
        $current_view = isset($_GET['view']) ? sanitize_text_field($_GET['view']) : 'all';
        $current_page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
        $per_page = 20;

        // Build query args for display
        $query_args = [
            'post_type' => PostTypes::EASY_INVOICE_QUOTE_POST_TYPE,
            'posts_per_page' => $per_page,
            'paged' => $current_page,
            'orderby' => 'date',
            'order' => 'DESC',
            'no_found_rows' => false,
            'update_post_term_cache' => false,
            'update_post_meta_cache' => false
        ];

        // Handle view filtering
        if ($current_view === 'trash' || $current_view === 'cancelled') {
            // For trash and cancelled views, look at post_status = 'trash'
            $query_args['post_status'] = 'trash';

            // For cancelled view, also filter by meta status
            if ($current_view === 'cancelled') {
                $query_args['meta_query'] = [
                    [
                        'key' => '_easy_invoice_quote_status',
                        'value' => 'cancelled',
                        'compare' => '='
                    ]
                ];
            }
        } else {
            // For all other views, exclude trashed posts
            $query_args['post_status'] = ['publish', 'draft', 'private', 'pending'];

            if ($current_view !== 'all') {
                // For specific status views, add meta query
                $query_args['meta_query'] = [
                    [
                        'key' => '_easy_invoice_quote_status',
                        'value' => $current_view,
                        'compare' => '='
                    ]
                ];
            }
        }

        // Add search if provided
        if (!empty($search_query)) {
            $search_ids = [];

            // Build base query args for search
            $search_query_args = [
                'post_type' => PostTypes::EASY_INVOICE_QUOTE_POST_TYPE,
                'post_status' => $query_args['post_status'],
                'posts_per_page' => -1,
                'fields' => 'ids' // Only get IDs for better performance
            ];

            // Search in title and content
            $title_search_args = array_merge($search_query_args, [
                's' => $search_query
            ]);
            $title_search = new \WP_Query($title_search_args);
            $search_ids = $title_search->posts;

            // Search in meta
            $meta_search_args = array_merge($search_query_args, [
                'meta_query' => [
                    'relation' => 'OR',
                    [
                        'key' => '_easy_invoice_quote_number',
                        'value' => $search_query,
                        'compare' => 'LIKE'
                    ],
                    [
                        'key' => '_easy_invoice_quote_client_name',
                        'value' => $search_query,
                        'compare' => 'LIKE'
                    ],
                    [
                        'key' => '_easy_invoice_quote_client_email',
                        'value' => $search_query,
                        'compare' => 'LIKE'
                    ]
                ]
            ]);
            $meta_search = new \WP_Query($meta_search_args);

            if ($meta_search->have_posts()) {
                $search_ids = array_merge($search_ids, wp_list_pluck($meta_search->posts, 'ID'));
            }

            $search_ids = array_unique($search_ids);

            if (!empty($search_ids)) {
                $query_args['post__in'] = $search_ids;
            } else {
                $query_args['post__in'] = [0];
            }
        }

        // Allow plugins to modify query args
        $query_args = apply_filters('easy_invoice_quote_controller_final_query_args', $query_args);
        // Get filtered quotes for display
        $wp_query = new \WP_Query($query_args);
        $quotes = [];

        if ($wp_query->have_posts()) {
            foreach ($wp_query->posts as $post) {
                $quote = $this->quote_repository->find($post->ID);
                if ($quote) {
                    $quotes[] = $quote;
                }
            }
        }

        // Allow plugins to modify the quotes array
        $quotes = apply_filters('easy_invoice_quote_controller_quotes_list', $quotes, $wp_query);

        // Get pagination info from WordPress query
        $total_quotes = $wp_query->found_posts;
        $total_pages = $wp_query->max_num_pages;

        // Initialize counts
        $draft_count = 0;
        $available_count = 0;
        $sent_count = 0;
        $accepted_count = 0;
        $declined_count = 0;
        $expired_count = 0;
        $cancelled_count = 0;

        // Process status counts
        foreach ($status_counts as $status) {
            switch ($status->status) {
                case 'draft':
                    $draft_count = $status->count;
                    break;
                case 'available':
                    $available_count = $status->count;
                    break;
                case 'sent':
                    $sent_count = $status->count;
                    break;
                case 'accepted':
                    $accepted_count = $status->count;
                    break;
                case 'declined':
                    $declined_count = $status->count;
                    break;
                case 'expired':
                    $expired_count = $status->count;
                    break;
                case 'cancelled':
                    $cancelled_count = $status->count;
                    break;
            }
        }

        // Prepare template data
        $template_data = [
            'quotes' => $quotes,
            'current_view' => $current_view,
            'status_filter' => $status_filter,
            'search_query' => $search_query,
            'all_count' => (int)$all_count,
            'trash_count' => (int)$trash_count,
            'draft_count' => (int)$draft_count,
            'available_count' => (int)$available_count,
            'sent_count' => (int)$sent_count,
            'accepted_count' => (int)$accepted_count,
            'declined_count' => (int)$declined_count,
            'expired_count' => (int)$expired_count,
            'cancelled_count' => (int)$cancelled_count,
            'repository' => $this->quote_repository,
            'current_page' => $current_page,
            'per_page' => $per_page,
            'total_quotes' => $total_quotes,
            'total_pages' => $total_pages,
            'wp_query' => $wp_query
        ];

        // Allow plugins to modify template data
        $template_data = apply_filters('easy_invoice_quote_controller_template_data', $template_data);

        // Display the template
        include EASY_INVOICE_PLUGIN_DIR . 'templates/quotes/listing.php';

        // Allow plugins to perform actions after displaying listing
        do_action('easy_invoice_quote_controller_after_display_listing', $template_data);
    }

    /**
     * Display quote builder page
     *
     * @since 1.0.0
     */
    private function displayBuilder(): void {
        // Allow plugins to perform actions before displaying builder
        do_action('easy_invoice_quote_controller_before_display_builder');

        $quote_id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
        $quote = null;

        if ($quote_id > 0) {
            $quote = $this->quote_repository->find($quote_id);
        }

        $clients = $this->client_repository->all();

        // Allow plugins to modify the data
        $quote = apply_filters('easy_invoice_quote_controller_builder_quote', $quote, $quote_id);
        $clients = apply_filters('easy_invoice_quote_controller_builder_clients', $clients);

        // Include the builder template
        include EASY_INVOICE_PLUGIN_DIR . 'templates/quotes/builder.php';

        // Allow plugins to perform actions after displaying builder
        do_action('easy_invoice_quote_controller_after_display_builder', $quote, $clients);
    }

    /**
     * Display quote preview page
     *
     * @since 1.0.0
     * @param array $args Display arguments
     */
    private function displayPreview(array $args): void {
        // Allow plugins to perform actions before displaying preview
        do_action('easy_invoice_quote_controller_before_display_preview', $args);

        $quote_id = isset($_GET['id']) ? (int) $_GET['id'] : 0;

        if ($quote_id <= 0) {
            wp_die(__('Quote not found.', 'easy-invoice'));
        }

        $quote = $this->quote_repository->find($quote_id);
        if (!$quote) {
            wp_die(__('Quote not found.', 'easy-invoice'));
        }

        // Allow plugins to modify the quote
        $quote = apply_filters('easy_invoice_quote_controller_preview_quote', $quote, $quote_id);

        // Include the preview template
        include EASY_INVOICE_PLUGIN_DIR . 'templates/quotes/preview.php';

        // Allow plugins to perform actions after displaying preview
        do_action('easy_invoice_quote_controller_after_display_preview', $quote, $args);
    }

    /**
     * Handle delete quote AJAX request
     *
     * @since 1.0.0
     */
    public function handleDeleteQuote(): void {
        // Verify nonce
        if (!wp_verify_nonce($_POST['nonce'] ?? '', 'easy_invoice_admin_nonce')) {
            wp_send_json_error(['message' => __('Security check failed.', 'easy-invoice')]);
        }

        // Check permissions
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('Insufficient permissions.', 'easy-invoice')]);
        }

        $quote_id = isset($_POST['quote_id']) ? (int) $_POST['quote_id'] : 0;

        if ($quote_id <= 0) {
            wp_send_json_error(['message' => __('Invalid quote ID.', 'easy-invoice')]);
        }

        if ($this->quote_repository->delete($quote_id)) {
            // Log the quote deletion
            $this->quote_log_service->logDeletion($quote_id);

            wp_send_json_success([
                'message' => __('Quote deleted successfully.', 'easy-invoice'),
                'toast' => [
                    'type' => 'success',
                    'message' => __('Quote deleted successfully.', 'easy-invoice')
                ]
            ]);
        } else {
            wp_send_json_error(['message' => __('Failed to delete quote.', 'easy-invoice')]);
        }
    }

    /**
     * Handle get quote AJAX request
     *
     * @since 1.0.0
     */
    public function handleGetQuote(): void {
        // Verify nonce
        if (!wp_verify_nonce($_POST['nonce'] ?? '', 'easy_invoice_get_quote')) {
            wp_send_json_error(['message' => __('Security check failed.', 'easy-invoice')]);
        }

        // Check permissions
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('Insufficient permissions.', 'easy-invoice')]);
        }

        $quote_id = isset($_POST['quote_id']) ? (int) $_POST['quote_id'] : 0;

        if ($quote_id <= 0) {
            wp_send_json_error(['message' => __('Invalid quote ID.', 'easy-invoice')]);
        }

        $quote = $this->quote_repository->find($quote_id);

        if (!$quote) {
            wp_send_json_error(['message' => __('Quote not found.', 'easy-invoice')]);
        }

        wp_send_json_success(['quote' => $quote->toArray()]);
    }

    /**
     * Handle AJAX request to load quote template
     *
     * @since 1.0.0
     */
    public function handleLoadQuoteTemplate(): void {
        // Verify nonce
        if (!wp_verify_nonce($_POST['nonce'] ?? '', 'easy_invoice_nonce')) {
            wp_send_json_error(['message' => __('Security check failed.', 'easy-invoice')]);
        }

        // Check permissions
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('Insufficient permissions.', 'easy-invoice')]);
        }

        $template_id = sanitize_text_field($_POST['template'] ?? '');
        $quote_id = isset($_POST['quote_id']) ? (int) $_POST['quote_id'] : 0;

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

        // Validate template name securely
        $template_id = $this->validateTemplateName($template_id, 'quote');

        // Get secure template file path
        $template_file = $this->getSecureTemplatePath($template_id, 'quote');

        if (!$template_file) {
            wp_send_json_error(['message' => __('Template not found.', 'easy-invoice')]);
        }

        // Load quote if provided
        $quote = null;
        if ($quote_id > 0) {
            $quote = $this->quote_repository->find($quote_id);
        }

        // Start output buffering to capture template HTML
        ob_start();

        // Include the template file
        include $template_file;

        // Get the captured HTML
        $html = ob_get_clean();

        wp_send_json_success(['html' => $html]);
    }

    /**
     * Validate and sanitize template name to prevent directory traversal attacks
     *
     * @param string $template The template name to validate
     * @param string $type Either 'invoice' or 'quote'
     * @return string Validated template name or 'standard' as fallback
     */
    private function validateTemplateName($template, $type = 'quote') {
        // Whitelist of allowed template names
        $allowed_templates = array(
            'invoice' => array('classic', 'corporate', 'creative', 'elegant', 'legacy', 'minimal', 'modern', 'professional', 'standard'),
            'quote' => array('legacy', 'minimal', 'minimalist', 'modern', 'standard')
        );

        // Strip any directory components using basename
        $template = basename($template);

        // Remove any file extension
        $template = preg_replace('/\.(php|html|htm)$/i', '', $template);

        // Remove any non-alphanumeric characters except hyphens and underscores
        $template = preg_replace('/[^a-z0-9_-]/i', '', $template);

        // Check if template is in whitelist
        if (isset($allowed_templates[$type]) && in_array($template, $allowed_templates[$type], true)) {
            return $template;
        }

        // Return default template if not in whitelist
        return 'standard';
    }

    /**
     * Get secure template file path with directory traversal protection
     *
     * @param string $template The validated template name
     * @param string $type Either 'invoice' or 'quote'
     * @return string|false The secure template file path or false if invalid
     */
    private function getSecureTemplatePath($template, $type = 'quote') {
        // Define template directories
        $template_dirs = array(
            'invoice' => EASY_INVOICE_PLUGIN_DIR . 'templates/invoice-templates/',
            'quote' => EASY_INVOICE_PLUGIN_DIR . 'templates/quote-templates/'
        );

        if (!isset($template_dirs[$type])) {
            return false;
        }

        $template_dir = $template_dirs[$type];

        // Ensure template directory exists and is a directory
        if (!is_dir($template_dir)) {
            return false;
        }

        // Get the real path of the template directory (resolves any symlinks)
        $real_template_dir = realpath($template_dir);
        if ($real_template_dir === false) {
            return false;
        }

        // Construct the template file path
        $template_file = $real_template_dir . DIRECTORY_SEPARATOR . $template . '.php';

        // Get the real path of the template file (resolves any .. or . components)
        $real_template_file = realpath($template_file);

        // Verify that the resolved path is within the template directory
        // This prevents directory traversal attacks
        if ($real_template_file === false || strpos($real_template_file, $real_template_dir) !== 0) {
            // If template doesn't exist or is outside the directory, use default
            $default_file = $real_template_dir . DIRECTORY_SEPARATOR . 'standard.php';
            $real_default_file = realpath($default_file);

            if ($real_default_file !== false && strpos($real_default_file, $real_template_dir) === 0) {
                return $real_default_file;
            }

            return false;
        }

        // Verify the file exists and is readable
        if (!is_file($real_template_file) || !is_readable($real_template_file)) {
            // Fallback to standard template
            $default_file = $real_template_dir . DIRECTORY_SEPARATOR . 'standard.php';
            $real_default_file = realpath($default_file);

            if ($real_default_file !== false && strpos($real_default_file, $real_template_dir) === 0 && is_file($real_default_file) && is_readable($real_default_file)) {
                return $real_default_file;
            }

            return false;
        }

        return $real_template_file;
    }

    /**
     * Handle AJAX request to create a new quote with just the title
     *
     * @since 1.0.0
     */
    public function handleCreateNewQuote(): void {
        // Verify nonce
        if (!wp_verify_nonce($_POST['nonce'] ?? '', 'easy_invoice_admin_nonce')) {
            wp_send_json_error(['message' => __('Security check failed.', 'easy-invoice')]);
        }
        // Check permissions
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('Insufficient permissions.', 'easy-invoice')]);
        }
        $title = isset($_POST['title']) ? sanitize_text_field($_POST['title']) : '';
        if (empty($title)) {
            wp_send_json_error(['message' => __('Quote title is required.', 'easy-invoice')]);
        }

        // Generate a unique quote number
        $quote_number = '';
        if (class_exists('\\EasyInvoice\\Services\\QuoteNumberService')) {
            $quote_number_service = new \EasyInvoice\Services\QuoteNumberService();
            $quote_number = $quote_number_service->generateUniqueNumber();
        } else {
            // Fallback if service doesn't exist
            $quote_number = 'QT-' . str_pad(time(), 6, '0', STR_PAD_LEFT);
        }

        // Get global quote settings
        $settings_controller = new \EasyInvoice\Controllers\SettingsController();
        $quote_terms = $settings_controller::getQuoteTermsConditions();
        $quote_footer = $settings_controller::getQuoteFooterText();
        $quote_accept_button = get_option('easy_invoice_quote_accept_button', 'yes');
        $quote_accept_action = get_option('easy_invoice_quote_accept_action', 'email');
        $quote_accept_text = get_option('easy_invoice_quote_accept_text', __('Accept Quote', 'easy-invoice'));
        $quote_accepted_message = get_option('easy_invoice_quote_accepted_message', __('Thank you for accepting our quote!', 'easy-invoice'));
        $quote_declined_message = get_option('easy_invoice_quote_declined_message', __('Thank you for your consideration.', 'easy-invoice'));

        // Create the quote with just the title and default values
        $data = [
            'title' => $title,
            'status' => 'draft',
            'number' => $quote_number, // Use the generated unique number
            'issue_date' => date('Y-m-d'),
            'expiry_date' => date('Y-m-d', strtotime('+30 days')),
            'items' => [],
            'notes' => '', // Ensure notes is never null
            'terms' => $quote_terms, // Use global terms setting
            'footer_text' => $quote_footer, // Use global footer setting
            'accept_button' => $quote_accept_button, // Use global accept button setting
            'accept_action' => $quote_accept_action, // Use global accept action setting
            'accept_text' => $quote_accept_text, // Use global accept text setting
            'accepted_message' => $quote_accepted_message, // Use global accepted message setting
            'declined_message' => $quote_declined_message, // Use global declined message setting
			'template' => get_option('easy_invoice_last_quote_template', 'standard')
        ];


        $quote = $this->quote_repository->create($data);
        if (!$quote) {
            wp_send_json_error(['message' => __('Failed to create quote.', 'easy-invoice')]);
        }
        wp_send_json_success(['quote_id' => $quote->getId()]);
    }

    /**
     * Handle AJAX request to load quote form for modal
     *
     * @since 1.0.0
     */
    public function handleLoadQuoteForm(): void {
        // Verify nonce
        if (!wp_verify_nonce($_POST['nonce'] ?? '', 'easy_invoice_admin_nonce')) {
            wp_send_json_error(['message' => __('Security check failed.', 'easy-invoice')]);
        }

        // Check permissions
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('Insufficient permissions.', 'easy-invoice')]);
        }

        // Get global quote settings
        $settings_controller = new \EasyInvoice\Controllers\SettingsController();
        $quote_terms = $settings_controller::getQuoteTermsConditions();
        $quote_footer = $settings_controller::getQuoteFooterText();
        $quote_accept_button = get_option('easy_invoice_quote_accept_button', 'yes');
        $quote_accept_action = get_option('easy_invoice_quote_accept_action', 'email');
        $quote_accept_text = get_option('easy_invoice_quote_accept_text', __('Accept Quote', 'easy-invoice'));
        $quote_accepted_message = get_option('easy_invoice_quote_accepted_message', __('Thank you for accepting our quote!', 'easy-invoice'));
        $quote_declined_message = get_option('easy_invoice_quote_declined_message', __('Thank you for your consideration.', 'easy-invoice'));

        // Create a new quote object for the form
        $quote_number_service = function_exists('easy_invoice_get_quote_number_service') ? easy_invoice_get_quote_number_service() : null;
        $quote_data = array(
            'number' => $quote_number_service ? $quote_number_service->getNextNumber() : 'QT-1',
            'date' => date('Y-m-d'),
            'expiry_date' => date('Y-m-d', strtotime('+30 days')),
            'client_id' => 0,
            'client_name' => '',
            'client_email' => '',
            'client_phone' => '',
            'client_address' => '',
            'items' => array(),
            'notes' => '',
            'internal_notes' => '',
            'discount' => 0,
            'discount_type' => 'percentage',
            'calculation_method' => 'before_tax',
            'tax_rate' => 10,
            'prices_include_tax' => 'no',
            'status' => 'draft',
            'currency' => 'USD',
            'currency_symbol' => '$',
            'title' => '',
            'description' => '',
            'terms' => $quote_terms, // Use global terms setting
            'footer_text' => $quote_footer, // Use global footer setting
            'accept_button' => $quote_accept_button, // Use global accept button setting
            'accept_action' => $quote_accept_action, // Use global accept action setting
            'accept_text' => $quote_accept_text, // Use global accept text setting
            'accepted_message' => $quote_accepted_message, // Use global accepted message setting
            'declined_message' => $quote_declined_message, // Use global declined message setting
        );

        // Create a temporary WP_Post object for new quote
        $empty_post = new \WP_Post((object) array(
            'ID' => 0,
            'post_author' => get_current_user_id(),
            'post_date' => current_time('mysql'),
            'post_date_gmt' => current_time('mysql', 1),
            'post_title' => $quote_data['number'],
            'post_status' => 'auto-draft',
            'comment_status' => 'closed',
            'ping_status' => 'closed',
            'post_name' => '',
            'post_modified' => current_time('mysql'),
            'post_modified_gmt' => current_time('mysql', 1),
            'post_parent' => 0,
            'guid' => '',
            'menu_order' => 0,
            'post_type' => \EasyInvoice\Constants\PostTypes::EASY_INVOICE_QUOTE_POST_TYPE,
            'post_mime_type' => '',
            'comment_count' => 0,
            'filter' => 'raw',
        ));

        $quote = new \EasyInvoice\Models\Quote($empty_post);

        // Set default values on the quote object
        foreach ($quote_data as $key => $value) {
            $setter = 'set' . easy_invoice_str_replace('_', '', ucwords($key, '_'));
            if (method_exists($quote, $setter)) {
                switch ($setter) {
                    case 'setClientId':
                        $quote->setClientId((int) $value);
                        break;
                    case 'setItems':
                        $quote->setItems((array) $value);
                        break;
                    case 'setSubtotal':
                    case 'setTaxAmount':
                    case 'setDiscountAmount':
                    case 'setTotal':
                    case 'setDiscountValue':
                    case 'setTaxRate':
                        $quote->$setter((float) $value);
                        break;
                    case 'setPricesIncludeTax':
                        $quote->$setter((bool) $value);
                        break;
                    default:
                        $quote->$setter((string) $value);
                        break;
                }
            }
        }

        // Initialize empty items array
        $quote->setItems([]);

        // Set variables needed by the form template
        $quote_id = 0;
        $clients = \EasyInvoice\Providers\ClientServiceProvider::getClientRepository()->all();
        $quote_form_manager = new \EasyInvoice\Forms\Quote\QuoteFormManager();
        $quote_items_json = json_encode([]);
        $admin_nonce = wp_create_nonce('easy_invoice_admin_nonce');
        $quote_field_config = $quote_form_manager->getFieldConfigForJavaScript();

        // Start output buffering to capture form HTML
        ob_start();

        // Include the quote form template
        include EASY_INVOICE_PLUGIN_DIR . 'templates/quotes/form.php';

        // Get the captured HTML
        $html = ob_get_clean();

        wp_send_json_success(['html' => $html]);
    }

    /**
     * Handle search clients AJAX request
     *
     * @since 1.0.0
     */
    public function handleSearchClients(): void {
        // Verify nonce
        if (!wp_verify_nonce($_POST['nonce'] ?? '', 'easy_invoice_nonce')) {
            wp_send_json_error(['message' => __('Security check failed.', 'easy-invoice')]);
        }

        // Check permissions
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('Insufficient permissions.', 'easy-invoice')]);
        }

        $query = sanitize_text_field($_POST['query'] ?? '');

        // If query is empty, get all clients
        if (empty($query)) {
            $clients = $this->client_repository->all();
        } else {
        // Search clients by name, email, or company
        $clients = $this->client_repository->search($query);
        }

        $results = [];
        foreach ($clients as $client) {
            $results[] = [
                'id' => $client->getId(),
                'name' => $client->getBusinessClientName() ?: ($client->getFirstName() . ' ' . $client->getLastName()),
                'email' => $client->getEmail(),
                'company' => $client->getBusinessClientName(),
                'phone' => $client->getExtraInfo(),
                'website' => $client->getWebsite(),
                'address' => $client->getAddress()
            ];
        }

        wp_send_json_success($results);
    }

    /**
     * Nonce action for quote accept/decline (includes quote ID to prevent cross-quote reuse).
     */
    private function quoteAcceptDeclineNonceAction(int $quote_id): string {
        return 'easy_invoice_quote_action_' . $quote_id;
    }

    /**
     * Handle AJAX request to accept a quote
     *
     * @since 1.0.0
     */
    public function handleAcceptQuote(): void {
        $quote_id = isset($_POST['quote_id']) ? (int) $_POST['quote_id'] : 0;

        if ($quote_id <= 0) {
            wp_send_json_error(['message' => __('Invalid quote ID.', 'easy-invoice')]);
        }

        // Quote-scoped nonce prevents cross-quote IDOR with a leaked global nonce.
        if (!wp_verify_nonce($_POST['nonce'] ?? '', $this->quoteAcceptDeclineNonceAction($quote_id))) {
            wp_send_json_error(['message' => __('Security check failed.', 'easy-invoice')]);
        }

        $is_admin = current_user_can('manage_options');
        if ($is_admin) {
            $quote = $this->quote_repository->find($quote_id);
        } else {
            $quote = $this->quote_repository->findPublished($quote_id);
        }

        if (!$quote) {
            wp_send_json_error(['message' => __('Quote not found.', 'easy-invoice')]);
        }

        // Check if user has permission to accept this quote
        $current_user = wp_get_current_user();

		$restrict = get_option('easy_invoice_pro_restrict_quote_to_client', 'no');

		if (!$is_admin && $restrict === 'yes') {
			// For non-admins, check if they are the client
            if ($quote->getClientId()) {
                $client_repository = \EasyInvoice\Providers\ClientServiceProvider::getClientRepository();
                $client = $client_repository->find($quote->getClientId());

                if (!$client || $client->getEmail() !== $current_user->user_email) {
                    wp_send_json_error(['message' => __('You do not have permission to accept this quote.', 'easy-invoice')]);
                }
            } else {
                wp_send_json_error(['message' => __('You do not have permission to accept this quote.', 'easy-invoice')]);
            }
        }

        // Get global accept action setting
        $settings_controller = new \EasyInvoice\Controllers\SettingsController();
        $accept_action = $settings_controller::getQuoteAcceptAction();

        // Update quote status to accepted
        $quote->setStatus('accepted');
        $quote->setAcceptedDate(date('Y-m-d H:i:s'));
        $quote->setAcceptedBy($current_user->ID);

        // Save the quote
        $saved = $quote->save();

        if (!$saved) {
            wp_send_json_error(['message' => __('Failed to accept quote.', 'easy-invoice')]);
        }

        // Log the quote acceptance
        $this->quote_log_service->logAcceptance($quote_id, [
            'accept_action' => $accept_action,
            'user_type' => $is_admin ? 'admin' : 'client'
        ]);

        // Perform the configured accept action
        $invoice_id = null;
        $action_message = '';

        switch ($accept_action) {
            case 'convert':
                // Convert quote to invoice (Draft status)
                $invoice_id = $this->convertQuoteToInvoice($quote, 'draft');
                if ($invoice_id) {
                    $this->quote_log_service->logConversionToInvoice($quote_id, $invoice_id);
                }
                $action_message = __('Quote converted to invoice successfully.', 'easy-invoice');
                break;

            case 'convert_available':
                // Convert quote to invoice (Available status)
                $invoice_id = $this->convertQuoteToInvoice($quote, 'available');
                if ($invoice_id) {
                    $this->quote_log_service->logConversionToInvoice($quote_id, $invoice_id);
                }
                $action_message = __('Quote converted to invoice successfully.', 'easy-invoice');
                break;

            case 'convert_send':
                // Convert quote to invoice and send to client (Available status)
                $invoice_id = $this->convertQuoteToInvoice($quote, 'available');
                if ($invoice_id) {
                    $this->sendInvoiceToClient($invoice_id);
                }
                $action_message = __('Quote converted to invoice and sent to client successfully.', 'easy-invoice');
                break;

            case 'duplicate':
                // Create new invoice, keep quote as-is (Draft status)
                $invoice_id = $this->createInvoiceFromQuote($quote, 'draft');
                if ($invoice_id) {
                    $this->quote_log_service->logDuplicationToInvoice($quote_id, $invoice_id);
                }
                $action_message = __('New invoice created from quote successfully.', 'easy-invoice');
                break;

            case 'duplicate_send':
                // Create new invoice and send to client, keep quote as-is (Available status)
                $invoice_id = $this->createInvoiceFromQuote($quote, 'available');
                if ($invoice_id) {
                    $this->sendInvoiceToClient($invoice_id);
                }
                $action_message = __('New invoice created and sent to client successfully.', 'easy-invoice');
                break;

            case 'do_nothing':
            default:
                // Do nothing additional
                $action_message = __('Quote accepted successfully.', 'easy-invoice');
                break;
        }

        // Send notification email to admin
        if (!$is_admin) {
            $this->sendQuoteAcceptanceNotification($quote);
        }

        // Get URLs for the new invoice
        $invoice_url = null;
        $secure_url = null;

        if ($invoice_id) {
            // Always use WordPress permalink
            $invoice_url = get_permalink($invoice_id);
            // If Pro and secure link available, use secure link
            if (class_exists('\EasyInvoicePro\Controllers\PermalinkController')) {
                $secure_url = \EasyInvoicePro\Controllers\PermalinkController::getInvoiceSecureLinkUrl($invoice_id);
                if ($secure_url) {
                    $invoice_url = $secure_url;
                }
            }
        }

        wp_send_json_success([
            'message' => $action_message,
            'invoice_id' => $invoice_id,
            'invoice_url' => $invoice_url,
            'secure_url' => $secure_url,
            'toast' => [
                'type' => 'success',
                'message' => $action_message
            ]
        ]);
    }

    /**
     * Convert quote to invoice
     *
     * @param \EasyInvoice\Models\Quote $quote The quote to convert
     * @param string $status The status for the new invoice ('draft' or 'available')
     * @return int|null The invoice ID if successful, null otherwise
     */
    private function convertQuoteToInvoice($quote, $status = 'draft'): ?int {
        try {
            // Get invoice repository
            $invoice_repository = \EasyInvoice\Providers\InvoiceServiceProvider::getInvoiceRepository();

            // Create invoice data from quote - convert ALL fields
            $invoice_data = [
                'title' => $quote->getTitle() ?: 'Invoice from Quote ' . $quote->getNumber(),
                'number' => $this->generateInvoiceNumber(),
                'status' => $status,
                'issue_date' => date('Y-m-d'),
                'due_date' => date('Y-m-d', strtotime('+30 days')),
                'client_id' => $quote->getClientId(),
                'customer_name' => $quote->getCustomerName(),
                'customer_email' => $quote->getCustomerEmail(),
                'customer_address' => $quote->getCustomerAddress(),
                'shipping_name' => $quote->getCustomerName(), // Use customer name as shipping name
                'shipping_address' => $quote->getCustomerAddress(), // Use customer address as shipping address
                'items' => $this->convertQuoteItemsToInvoiceItems($quote->getItems()),
                'notes' => $quote->getNotes(),
                'description' => $quote->getDescription(),
                'terms' => $quote->getTerms(),
                'internal_notes' => $quote->getInternalNotes(),
                'payment_instructions' => '', // Invoice-specific field, leave empty
                'payment_gateways' => [], // Invoice-specific field, leave empty
                'template' => $quote->getTemplate(),
                'subtotal' => $quote->getSubtotal(),
                'tax_rate' => $quote->getTaxRate(),
                'tax_amount' => $quote->getTaxAmount(),
                'discount_type' => $quote->getDiscountType(),
                'discount_value' => $quote->getDiscountValue(),
                'discount_amount' => $quote->getDiscountAmount(),
                'total' => $quote->getTotal(),
                'currency_code' => $quote->getCurrencyCode() ?: 'USD',
                'currency_position' => $quote->getCurrencyPosition() ?: 'left',
                'footer_text' => $quote->getFooterText(),
                'calculation_method' => 'standard', // Default calculation method for invoices
                'prices_include_tax' => $quote->getPricesIncludeTax(),
                'custom_fields' => $quote->getCustomFields(), // Transfer custom fields
            ];

            // Create the invoice
            $invoice = $invoice_repository->create($invoice_data);

            if ($invoice) {
                // Store the quote ID in the invoice's meta for tracking
                update_post_meta($invoice->getId(), '_converted_from_quote', $quote->getId());

                // Update quote to reference the created invoice
                $quote->setCustomField('converted_invoice_id', $invoice->getId());
                $quote->save();

                // Ensure secure link is generated for the new invoice (Pro version)
                if (class_exists('\EasyInvoicePro\Controllers\PermalinkController')) {
                    // Trigger the save_post hook to generate secure link
                    do_action('save_post_easy_invoice', $invoice->getId(), get_post($invoice->getId()));
                }

                return $invoice->getId();
            }

            return null;
        } catch (\Exception $e) {
            // Error converting quote to invoice
            return null;
        }
    }

    /**
     * Create new invoice from quote (duplicate)
     *
     * @param \EasyInvoice\Models\Quote $quote The quote to duplicate
     * @param string $status The status for the new invoice ('draft' or 'available')
     * @return int|null The invoice ID if successful, null otherwise
     */
    private function createInvoiceFromQuote($quote, $status = 'draft'): ?int {
        try {
            // Get invoice repository
            $invoice_repository = \EasyInvoice\Providers\InvoiceServiceProvider::getInvoiceRepository();

            // Create invoice data from quote - convert ALL fields
            $invoice_data = [
                'title' => 'Invoice from Quote ' . $quote->getNumber(),
                'number' => $this->generateInvoiceNumber(),
                'status' => $status,
                'issue_date' => date('Y-m-d'),
                'due_date' => date('Y-m-d', strtotime('+30 days')),
                'client_id' => $quote->getClientId(),
                'customer_name' => $quote->getCustomerName(),
                'customer_email' => $quote->getCustomerEmail(),
                'customer_address' => $quote->getCustomerAddress(),
                'shipping_name' => $quote->getCustomerName(), // Use customer name as shipping name
                'shipping_address' => $quote->getCustomerAddress(), // Use customer address as shipping address
                'items' => $this->convertQuoteItemsToInvoiceItems($quote->getItems()),
                'notes' => $quote->getNotes(),
                'description' => $quote->getDescription(),
                'terms' => $quote->getTerms(),
                'internal_notes' => $quote->getInternalNotes(),
                'payment_instructions' => '', // Invoice-specific field, leave empty
                'payment_gateways' => [], // Invoice-specific field, leave empty
                'template' => $quote->getTemplate(),
                'subtotal' => $quote->getSubtotal(),
                'tax_rate' => $quote->getTaxRate(),
                'tax_amount' => $quote->getTaxAmount(),
                'discount_type' => $quote->getDiscountType(),
                'discount_value' => $quote->getDiscountValue(),
                'discount_amount' => $quote->getDiscountAmount(),
                'total' => $quote->getTotal(),
                'currency_code' => $quote->getCurrencyCode() ?: 'USD',
                'currency_position' => $quote->getCurrencyPosition() ?: 'left',
                'footer_text' => $quote->getFooterText(),
                'calculation_method' => 'standard', // Default calculation method for invoices
                'prices_include_tax' => $quote->getPricesIncludeTax(),
                'custom_fields' => $quote->getCustomFields(), // Transfer custom fields
            ];

            // Create the invoice
            $invoice = $invoice_repository->create($invoice_data);

            if ($invoice) {
                // Link the invoice to the quote
                $quote->setCustomField('related_invoice_id', $invoice->getId());
                $quote->save();

                // Ensure secure link is generated for the new invoice (Pro version)
                if (class_exists('\EasyInvoicePro\Controllers\PermalinkController')) {
                    // Trigger the save_post hook to generate secure link
                    do_action('save_post_easy_invoice', $invoice->getId(), get_post($invoice->getId()));
                }

                return $invoice->getId();
            }

            return null;
        } catch (\Exception $e) {
            // Error creating invoice from quote
            return null;
        }
    }

    /**
     * Send invoice to client
     *
     * @param int $invoice_id The invoice ID
     * @return bool True if sent successfully
     */
    private function sendInvoiceToClient(int $invoice_id): bool {
        try {
            // Get invoice
            $invoice_repository = \EasyInvoice\Providers\InvoiceServiceProvider::getInvoiceRepository();
            $invoice = $invoice_repository->find($invoice_id);

            if (!$invoice) {
                return false;
            }

            // Get email manager
            $email_manager = \EasyInvoice\Services\EmailManager::getInstance();

            // Send invoice email
            $result = $email_manager->sendInvoiceEmail($invoice, 'new');

            return $result['success'];
        } catch (\Exception $e) {
            // Error sending invoice to client
            return false;
        }
    }

    /**
     * Convert quote items to invoice items
     *
     * @param array $quote_items Array of quote items
     * @return array Array of invoice items
     */
    private function convertQuoteItemsToInvoiceItems(array $quote_items): array {
        $invoice_items = [];

        foreach ($quote_items as $quote_item) {
            if (is_object($quote_item) && method_exists($quote_item, 'toArray')) {
                // Convert QuoteItem object to InvoiceItem array
                $item_data = $quote_item->toArray();
                $invoice_items[] = [
                    'name' => $item_data['name'] ?? '',
                    'description' => $item_data['description'] ?? '',
                    'quantity' => $item_data['quantity'] ?? 0,
                    'price' => $item_data['price'] ?? 0,
                    'amount' => $item_data['amount'] ?? 0,
                    'taxable' => $item_data['taxable'] ?? true,
                    // Map adjust_percentage to a similar field if needed
                    'adjust_percentage' => $item_data['adjust_percentage'] ?? 0,
                ];
            } elseif (is_array($quote_item)) {
                // Convert array item directly
                $invoice_items[] = [
                    'name' => $quote_item['name'] ?? $quote_item['title'] ?? '',
                    'description' => $quote_item['description'] ?? '',
                    'quantity' => $quote_item['quantity'] ?? 0,
                    'price' => $quote_item['price'] ?? 0,
                    'amount' => $quote_item['amount'] ?? $quote_item['total'] ?? 0,
                    'taxable' => $quote_item['taxable'] ?? true,
                    'adjust_percentage' => $quote_item['adjust_percentage'] ?? 0,
                ];
            }
        }

        return $invoice_items;
    }

    /**
     * Generate unique invoice number
     *
     * @return string The invoice number
     */
    private function generateInvoiceNumber(): string {
        // Try to use invoice number service if available
        if (class_exists('\\EasyInvoice\\Services\\InvoiceNumberService')) {
            $invoice_number_service = new \EasyInvoice\Services\InvoiceNumberService();
            return $invoice_number_service->generateUniqueNumber();
        }

        // Fallback to timestamp-based number
        return 'INV-' . str_pad(time(), 6, '0', STR_PAD_LEFT);
    }

    /**
     * Get changes between two quote versions
     *
     * @param \EasyInvoice\Models\Quote $old_quote Old quote
     * @param \EasyInvoice\Models\Quote $new_quote New quote
     * @return array Array of changes
     */
    private function getQuoteChanges($old_quote, $new_quote): array {
        $changes = [];

        // Compare key fields
        $fields_to_compare = [
            'title' => 'Title',
            'status' => 'Status',
            'customer_name' => 'Customer Name',
            'customer_email' => 'Customer Email',
            'customer_address' => 'Customer Address',
            'issue_date' => 'Issue Date',
            'expiry_date' => 'Expiry Date',
            'total' => 'Total Amount',
            'notes' => 'Notes',
            'terms' => 'Terms',
        ];

        foreach ($fields_to_compare as $field => $label) {
            $method_name = 'get' . easy_invoice_str_replace('_', '', ucwords($field, '_'));

            if (method_exists($old_quote, $method_name) && method_exists($new_quote, $method_name)) {
                $old_value = $old_quote->$method_name();
                $new_value = $new_quote->$method_name();

                if ($old_value !== $new_value) {
                    $changes[$field] = $new_value;
                }
            }
        }

        return $changes;
    }

    /**
     * Handle AJAX request to decline a quote
     *
     * @since 1.0.0
     */
    public function handleDeclineQuote(): void {
        $quote_id = isset($_POST['quote_id']) ? (int) $_POST['quote_id'] : 0;
        $decline_reason = isset($_POST['decline_reason']) ? sanitize_textarea_field($_POST['decline_reason']) : '';

        if ($quote_id <= 0) {
            wp_send_json_error(['message' => __('Invalid quote ID.', 'easy-invoice')]);
        }

        if (!wp_verify_nonce($_POST['nonce'] ?? '', $this->quoteAcceptDeclineNonceAction($quote_id))) {
            wp_send_json_error(['message' => __('Security check failed.', 'easy-invoice')]);
        }

        $is_admin = current_user_can('manage_options');
        if ($is_admin) {
            $quote = $this->quote_repository->find($quote_id);
        } else {
            $quote = $this->quote_repository->findPublished($quote_id);
        }

        if (!$quote) {
            wp_send_json_error(['message' => __('Quote not found.', 'easy-invoice')]);
        }

        // Check if decline reason is required by global settings
        $settings_controller = new \EasyInvoice\Controllers\SettingsController();
        if ($settings_controller::isDeclineReasonRequired() && empty(trim($decline_reason))) {
            wp_send_json_error(['message' => __('Reason for declining is required.', 'easy-invoice')]);
        }

        // Check if user has permission to decline this quote
        $current_user = wp_get_current_user();

        $restrict = get_option('easy_invoice_pro_restrict_quote_to_client', 'no');

        if (!$is_admin && $restrict === 'yes') {
            // For non-admins, check if they are the client
            if ($quote->getClientId()) {
                $client_repository = \EasyInvoice\Providers\ClientServiceProvider::getClientRepository();
                $client = $client_repository->find($quote->getClientId());

                if (!$client || $client->getEmail() !== $current_user->user_email) {
                    wp_send_json_error(['message' => __('You do not have permission to decline this quote.', 'easy-invoice')]);
                }
            } else {
                wp_send_json_error(['message' => __('You do not have permission to decline this quote.', 'easy-invoice')]);
            }
        }

        // Update quote status to declined
        $quote->setStatus('declined');
        $quote->setDeclinedDate(date('Y-m-d H:i:s'));
        $quote->setDeclinedBy($current_user->ID);

        // Save decline reason if provided
        if (!empty($decline_reason)) {
            $quote->setDeclineReason($decline_reason);
        }

        // Save the quote
        $saved = $quote->save();

        if (!$saved) {
            wp_send_json_error(['message' => __('Failed to decline quote.', 'easy-invoice')]);
        }

        // Log the quote decline
        $this->quote_log_service->logDecline($quote_id, $decline_reason, [
            'user_type' => $is_admin ? 'admin' : 'client'
        ]);

        // Send notification email to admin
        if (!$is_admin) {
            $this->sendQuoteDeclineNotification($quote);
        }

        wp_send_json_success([
            'message' => __('Quote declined successfully.', 'easy-invoice'),
            'toast' => [
                'type' => 'success',
                'message' => __('Quote declined successfully.', 'easy-invoice')
            ]
        ]);
    }

    /**
     * Send quote acceptance notification to admin
     *
     * @param \EasyInvoice\Models\Quote $quote The quote that was accepted
     */
    private function sendQuoteAcceptanceNotification($quote): void {
        // Use EmailManager to send admin notification
        $email_manager = \EasyInvoice\Services\EmailManager::getInstance();
        $email_manager->sendAdminQuoteNotification($quote, 'accepted');
    }

    /**
     * Send quote decline notification to admin
     *
     * @param \EasyInvoice\Models\Quote $quote The quote that was declined
     */
    private function sendQuoteDeclineNotification($quote): void {
        // Use EmailManager to send admin notification
        $email_manager = \EasyInvoice\Services\EmailManager::getInstance();
        $email_manager->sendAdminQuoteNotification($quote, 'declined');
    }

    /**
     * Handle AJAX request to update existing quotes with missing data
     *
     * @since 1.0.0
     */
    public function handleUpdateExistingQuotes(): void {
        // Verify nonce - match the nonce being sent from JavaScript
        if (!wp_verify_nonce($_POST['nonce'] ?? '', 'easy_invoice_admin_nonce')) {
            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')]);
        }

        $updated_count = 0;
        $quotes = $this->quote_repository->findAll();

        foreach ($quotes as $quote) {
            $post = get_post($quote->getId());
            if ($post && empty($post->post_name)) {
                // Generate a proper slug for this quote
                $post_title = $quote->getTitle() ?: $quote->getNumber() ?: 'Untitled Quote';
                $post_name = sanitize_title($post_title);

                // Ensure uniqueness
                $original_slug = $post_name;
                $counter = 1;
                while (get_page_by_path($post_name, OBJECT, \EasyInvoice\Constants\PostTypes::EASY_INVOICE_QUOTE_POST_TYPE)) {
                    $post_name = $original_slug . '-' . $counter;
                    $counter++;
                }

                // Update the post with the new slug
                wp_update_post([
                    'ID' => $quote->getId(),
                    'post_name' => $post_name
                ]);

                $updated_count++;
            }
        }

        wp_send_json_success([
            'message' => sprintf(__('Updated %d quotes with proper URLs.', 'easy-invoice'), $updated_count)
        ]);
    }

    /**
     * Handle AJAX request to duplicate a quote
     *
     * @since 1.0.0
     */
    public function handleDuplicateQuote(): void {
        // Verify nonce
        if (!wp_verify_nonce($_POST['nonce'] ?? '', 'easy_invoice_admin_nonce')) {
            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')]);
        }

        $quote_id = isset($_POST['quote_id']) ? (int) $_POST['quote_id'] : 0;

        if ($quote_id <= 0) {
            wp_send_json_error(['message' => __('Invalid quote ID.', 'easy-invoice')]);
        }

        $quote = $this->quote_repository->find($quote_id);

        if (!$quote) {
            wp_send_json_error(['message' => __('Quote not found.', 'easy-invoice')]);
        }

        // Get global quote settings
        $settings_controller = new \EasyInvoice\Controllers\SettingsController();
        $quote_terms = $settings_controller::getQuoteTermsConditions();
        $quote_footer = $settings_controller::getQuoteFooterText();
        $quote_accept_button = get_option('easy_invoice_quote_accept_button', 'yes');
        $quote_accept_action = get_option('easy_invoice_quote_accept_action', 'email');
        $quote_accept_text = get_option('easy_invoice_quote_accept_text', __('Accept Quote', 'easy-invoice'));
        $quote_accepted_message = get_option('easy_invoice_quote_accepted_message', __('Thank you for accepting our quote!', 'easy-invoice'));
        $quote_declined_message = get_option('easy_invoice_quote_declined_message', __('Thank you for your consideration.', 'easy-invoice'));

        // Create the duplicate quote
        $duplicate_data = [
            'title' => $quote->getTitle() . ' (Copy)',
            'status' => 'draft',
            'number' => $this->generateInvoiceNumber(), // Use invoice number service for consistency
            'issue_date' => date('Y-m-d'),
            'expiry_date' => date('Y-m-d', strtotime('+30 days')),
            'items' => $this->convertQuoteItemsToInvoiceItems($quote->getItems()), // Use invoice item conversion
            'notes' => $quote->getNotes(),
            'description' => $quote->getDescription(),
            'terms' => $quote_terms,
            'internal_notes' => $quote->getInternalNotes(),
            'accept_button' => $quote_accept_button,
            'accept_action' => $quote_accept_action,
            'accept_text' => $quote_accept_text,
            'accepted_message' => $quote_accepted_message,
            'declined_message' => $quote_declined_message,
        ];

        // Set client ID to 0 for a new quote
        $duplicate_data['client_id'] = 0;

        $duplicate_quote = $this->quote_repository->create($duplicate_data);

        if ($duplicate_quote) {
            $this->quote_log_service->logActivity($quote_id, 'duplicate', 'Quote duplicated', ['duplicate_id' => $duplicate_quote->getId()]);
            wp_send_json_success([
                'message' => __('Quote duplicated successfully.', 'easy-invoice'),
                'quote_id' => $duplicate_quote->getId(),
                'toast' => [
                    'type' => 'success',
                    'message' => __('Quote duplicated successfully.', 'easy-invoice')
                ]
            ]);
        } else {
            wp_send_json_error(['message' => __('Failed to duplicate quote.', 'easy-invoice')]);
        }
    }

    /**
     * Handle regular POST form actions for quote accept/decline
     *
     * @since 1.0.0
     */
    public function handleQuoteFormActions(): void {
        // Only process on POST requests
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            return;
        }

        // Handle accept quote
        if (isset($_POST['accept_quote']) && isset($_POST['quote_id'])) {
            $this->handleAcceptQuoteForm();
        }

        // Handle decline quote
        if (isset($_POST['decline_quote']) && isset($_POST['quote_id'])) {
            $this->handleDeclineQuoteForm();
        }
    }

    /**
     * Handle accept quote form submission
     *
     * @since 1.0.0
     */
    private function handleAcceptQuoteForm(): void {
        $quote_id = isset($_POST['quote_id']) ? (int) $_POST['quote_id'] : 0;

        if ($quote_id <= 0) {
            wp_die(__('Invalid quote ID.', 'easy-invoice'));
        }

        if (!wp_verify_nonce($_POST['quote_nonce'] ?? '', $this->quoteAcceptDeclineNonceAction($quote_id))) {
            wp_die(__('Security check failed.', 'easy-invoice'));
        }

        $current_user = wp_get_current_user();
        $is_admin = current_user_can('manage_options');

        if ($is_admin) {
            $quote = $this->quote_repository->find($quote_id);
        } else {
            $quote = $this->quote_repository->findPublished($quote_id);
        }

        if (!$quote) {
            wp_die(__('Quote not found.', 'easy-invoice'));
        }

        // Check if user has permission to accept this quote

        $restrict = get_option('easy_invoice_pro_restrict_quote_to_client', 'no');

        if (!$is_admin && $restrict === 'yes') {
            // For non-admins, check if they are the client
            if ($quote->getClientId()) {
                $client_repository = \EasyInvoice\Providers\ClientServiceProvider::getClientRepository();
                $client = $client_repository->find($quote->getClientId());

                if (!$client || $client->getEmail() !== $current_user->user_email) {
                    wp_die(__('You do not have permission to accept this quote.', 'easy-invoice'));
                }
            } else {
                wp_die(__('You do not have permission to accept this quote.', 'easy-invoice'));
            }
        }

        // Update quote status to accepted
        $quote->setStatus('accepted');
        $quote->setAcceptedDate(date('Y-m-d H:i:s'));
        $quote->setAcceptedBy($current_user->ID);

        // Save the quote
        $saved = $quote->save();

        if (!$saved) {
            wp_die(__('Failed to accept quote.', 'easy-invoice'));
        }

        // Send notification email to admin
        if (!$is_admin) {
            $this->sendQuoteAcceptanceNotification($quote);
        }

        // Redirect back to the quote page with success message
        $redirect_url = add_query_arg('action', 'accepted', get_permalink($quote_id));
        wp_redirect($redirect_url);
        exit;
    }

    /**
     * Handle decline quote form submission
     *
     * @since 1.0.0
     */
    private function handleDeclineQuoteForm(): void {
        $quote_id = isset($_POST['quote_id']) ? (int) $_POST['quote_id'] : 0;

        if ($quote_id <= 0) {
            wp_die(__('Invalid quote ID.', 'easy-invoice'));
        }

        if (!wp_verify_nonce($_POST['quote_nonce'] ?? '', $this->quoteAcceptDeclineNonceAction($quote_id))) {
            wp_die(__('Security check failed.', 'easy-invoice'));
        }

        $current_user = wp_get_current_user();
        $is_admin = current_user_can('manage_options');

        if ($is_admin) {
            $quote = $this->quote_repository->find($quote_id);
        } else {
            $quote = $this->quote_repository->findPublished($quote_id);
        }

        if (!$quote) {
            wp_die(__('Quote not found.', 'easy-invoice'));
        }

        // Check if user has permission to decline this quote

        if (!$is_admin) {
            // For non-admins, check if they are the client
            if ($quote->getClientId()) {
                $client_repository = \EasyInvoice\Providers\ClientServiceProvider::getClientRepository();
                $client = $client_repository->find($quote->getClientId());

                if (!$client || $client->getEmail() !== $current_user->user_email) {
                    wp_die(__('You do not have permission to decline this quote.', 'easy-invoice'));
                }
            } else {
                wp_die(__('You do not have permission to decline this quote.', 'easy-invoice'));
            }
        }

        // Update quote status to declined
        $quote->setStatus('declined');
        $quote->setDeclinedDate(date('Y-m-d H:i:s'));
        $quote->setDeclinedBy($current_user->ID);

        // Save the quote
        $saved = $quote->save();

        if (!$saved) {
            wp_die(__('Failed to decline quote.', 'easy-invoice'));
        }

        // Send notification email to admin
        if (!$is_admin) {
            $this->sendQuoteDeclineNotification($quote);
        }

        // Redirect back to the quote page with success message
        $redirect_url = add_query_arg('action', 'declined', get_permalink($quote_id));
        wp_redirect($redirect_url);
        exit;
    }

    /**
     * Handle AJAX request for bulk quote actions
     *
     * @since 1.0.0
     */
    public function handleBulkQuoteAction(): void {
        // Verify nonce
        if (!wp_verify_nonce($_POST['nonce'] ?? '', 'easy_invoice_admin_nonce')) {
            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')]);
        }

        $quote_ids = isset($_POST['quote_ids']) ? array_map('intval', $_POST['quote_ids']) : [];
        $bulk_action = sanitize_text_field($_POST['bulk_action'] ?? '');

        if (empty($quote_ids)) {
            wp_send_json_error(['message' => __('No quotes selected.', 'easy-invoice')]);
        }

        if (empty($bulk_action)) {
            wp_send_json_error(['message' => __('No action selected.', 'easy-invoice')]);
        }

        $success_count = 0;
        $error_count = 0;

        foreach ($quote_ids as $quote_id) {
            $quote = $this->quote_repository->find($quote_id);

            if (!$quote) {
                $error_count++;
                continue;
            }

            try {
                switch ($bulk_action) {
                    case 'delete':
                        if ($this->quote_repository->delete($quote_id)) {
                            $this->quote_log_service->logDeletion($quote_id);
                            $success_count++;
                        } else {
                            $error_count++;
                        }
                        break;

                    case 'trash':
                        $old_status = $quote->getStatus();
                        $quote->setStatus('cancelled'); // Using cancelled as trash status
                        if ($quote->save()) {
                            $this->quote_log_service->logStatusChange($quote_id, $old_status, 'cancelled');
                            $success_count++;
                        } else {
                            $error_count++;
                        }
                        break;

                    case 'draft':
                        $old_status = $quote->getStatus();
                        $quote->setStatus('draft');
                        if ($quote->save()) {
                            $this->quote_log_service->logStatusChange($quote_id, $old_status, 'draft');
                            $success_count++;
                        } else {
                            $error_count++;
                        }
                        break;

                    case 'restore':
                        $old_status = $quote->getStatus();
                        $quote->setStatus('draft');
                        if ($quote->save()) {
                            $this->quote_log_service->logRestoration($quote_id);
                            $success_count++;
                        } else {
                            $error_count++;
                        }
                        break;

                    default:
                        $error_count++;
                        break;
                }
            } catch (\Exception $e) {
                $error_count++;
                // Error in bulk action
            }
        }

        if ($error_count > 0) {
            wp_send_json_success([
                'message' => sprintf(__('Processed %d quotes successfully. %d failed.', 'easy-invoice'), $success_count, $error_count),
                'toast' => [
                    'type' => 'warning',
                    'message' => sprintf(__('Processed %d quotes successfully. %d failed.', 'easy-invoice'), $success_count, $error_count)
                ]
            ]);
        } else {
            wp_send_json_success([
                'message' => sprintf(__('Successfully processed %d quotes.', 'easy-invoice'), $success_count),
                'toast' => [
                    'type' => 'success',
                    'message' => sprintf(__('Successfully processed %d quotes.', 'easy-invoice'), $success_count)
                ]
            ]);
        }
    }

    /**
     * Handle AJAX request to trash a quote
     *
     * @since 1.0.0
     */
    public function handleTrashQuote(): void {
        // Verify nonce
        if (!wp_verify_nonce($_POST['nonce'] ?? '', 'easy_invoice_admin_nonce')) {
            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')]);
        }

        $quote_id = isset($_POST['quote_id']) ? (int) $_POST['quote_id'] : 0;

        if ($quote_id <= 0) {
            wp_send_json_error(['message' => __('Invalid quote ID.', 'easy-invoice')]);
        }

        $quote = $this->quote_repository->find($quote_id);

        if (!$quote) {
            wp_send_json_error(['message' => __('Quote not found.', 'easy-invoice')]);
        }

        // Set status to cancelled before moving to trash
        $old_status = $quote->getStatus();
        $quote->setStatus('cancelled');
        $quote->save();

        // Move the post to trash status
        $result = wp_trash_post($quote_id);

        if ($result) {
            $this->quote_log_service->logStatusChange($quote_id, $old_status, 'cancelled');
            wp_send_json_success([
                'message' => __('Quote moved to trash successfully.', 'easy-invoice'),
                'toast' => [
                    'type' => 'success',
                    'message' => __('Quote moved to trash successfully.', 'easy-invoice')
                ]
            ]);
        } else {
            wp_send_json_error(['message' => __('Failed to move quote to trash.', 'easy-invoice')]);
        }
    }

    /**
     * Handle AJAX request to move a quote to draft
     *
     * @since 1.0.0
     */
    public function handleDraftQuote(): void {
        // Verify nonce
        if (!wp_verify_nonce($_POST['nonce'] ?? '', 'easy_invoice_admin_nonce')) {
            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')]);
        }

        $quote_id = isset($_POST['quote_id']) ? (int) $_POST['quote_id'] : 0;

        if ($quote_id <= 0) {
            wp_send_json_error(['message' => __('Invalid quote ID.', 'easy-invoice')]);
        }

        $quote = $this->quote_repository->find($quote_id);

        if (!$quote) {
            wp_send_json_error(['message' => __('Quote not found.', 'easy-invoice')]);
        }

        // Set status to draft
        $old_status = $quote->getStatus();
        $quote->setStatus('draft');

        if ($quote->save()) {
            $this->quote_log_service->logStatusChange($quote_id, $old_status, 'draft');
            wp_send_json_success([
                'message' => __('Quote moved to draft successfully.', 'easy-invoice'),
                'toast' => [
                    'type' => 'success',
                    'message' => __('Quote moved to draft successfully.', 'easy-invoice')
                ]
            ]);
        } else {
            wp_send_json_error(['message' => __('Failed to move quote to draft.', 'easy-invoice')]);
        }
    }

    /**
     * Handle AJAX request to restore a trashed quote
     *
     * @since 1.0.0
     */
    public function handleRestoreQuote(): void {
        // Verify nonce
        if (!wp_verify_nonce($_POST['nonce'] ?? '', 'easy_invoice_admin_nonce')) {
            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')]);
        }

        $quote_id = isset($_POST['quote_id']) ? (int) $_POST['quote_id'] : 0;

        if ($quote_id <= 0) {
            wp_send_json_error(['message' => __('Invalid quote ID.', 'easy-invoice')]);
        }

        $quote = $this->quote_repository->find($quote_id);

        if (!$quote) {
            wp_send_json_error(['message' => __('Quote not found.', 'easy-invoice')]);
        }

        // Restore the post from trash
        $result = wp_untrash_post($quote_id);

        if ($result) {
            // After restoring from trash, set the meta status to available
            $quote->setStatus('available');
            $quote->save();

            $this->quote_log_service->logRestoration($quote_id);
            wp_send_json_success([
                'message' => __('Quote restored successfully.', 'easy-invoice'),
                'toast' => [
                    'type' => 'success',
                    'message' => __('Quote restored successfully.', 'easy-invoice')
                ]
            ]);
        } else {
            wp_send_json_error(['message' => __('Failed to restore quote.', 'easy-invoice')]);
        }
    }

    /**
     * Handle AJAX request to empty trash
     *
     * @since 1.0.0
     */
    public function handleEmptyTrash(): void {
        try {
            // Verify nonce
            if (!wp_verify_nonce($_POST['nonce'] ?? '', 'easy_invoice_nonce')) {
                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 all quotes in trash (post_status = 'trash')
            global $wpdb;
            $quote_ids = $wpdb->get_col($wpdb->prepare(
                "SELECT ID FROM {$wpdb->posts}
                WHERE post_type = %s
                AND post_status = 'trash'",
                PostTypes::EASY_INVOICE_QUOTE_POST_TYPE
            ));

            if (empty($quote_ids)) {
                wp_send_json_error(['message' => __('No quotes found in trash.', 'easy-invoice')]);
            }

            $success_count = 0;
            $error_count = 0;

            foreach ($quote_ids as $quote_id) {
                if (wp_delete_post($quote_id, true)) {
                    $this->quote_log_service->logDeletion($quote_id);
                    $success_count++;
                } else {
                    $error_count++;
                }
            }

            if ($error_count > 0) {
                wp_send_json_success([
                    'message' => sprintf(__('Emptied trash: %d quotes deleted successfully, %d failed.', 'easy-invoice'), $success_count, $error_count),
                    'success_count' => $success_count,
                    'error_count' => $error_count,
                    'toast' => [
                        'type' => 'warning',
                        'message' => sprintf(__('Emptied trash: %d quotes deleted successfully, %d failed.', 'easy-invoice'), $success_count, $error_count)
                    ]
                ]);
            } else {
                wp_send_json_success([
                    'message' => sprintf(__('Successfully emptied trash: %d quotes deleted.', 'easy-invoice'), $success_count),
                    'success_count' => $success_count,
                    'error_count' => 0,
                    'toast' => [
                        'type' => 'success',
                        'message' => sprintf(__('Successfully emptied trash: %d quotes deleted.', 'easy-invoice'), $success_count)
                    ]
                ]);
            }

        } catch (\Exception $e) {
            error_log('Error emptying quote trash: ' . $e->getMessage());
            wp_send_json_error([
                'message' => __('Failed to empty trash.', 'easy-invoice'),
                'debug' => $e->getMessage()
            ]);
        }
    }

    /**
     * Handle AJAX request to get quote logs
     *
     * @since 1.0.0
     */
    public function handleGetQuoteLogs(): void {
        // Verify nonce
        if (!wp_verify_nonce($_POST['nonce'] ?? '', 'easy_invoice_admin_nonce')) {
            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')]);
        }

        $quote_id = isset($_POST['quote_id']) ? (int) $_POST['quote_id'] : 0;

        if ($quote_id <= 0) {
            wp_send_json_error(['message' => __('Invalid quote ID.', 'easy-invoice')]);
        }

        try {
            $logs = $this->quote_log_service->getLogs($quote_id);

            // Convert QuoteLog objects to arrays for JSON response
            $logs_data = [];
            foreach ($logs as $log) {
                $logs_data[] = [
                    'action' => $log->getAction(),
                    'description' => $log->getDescription(),
                    'user_id' => $log->getUserId(),
                    'user_name' => $log->getUserName(),
                    'ip_address' => $log->getIpAddress(),
                    'user_agent' => $log->getUserAgent(),
                    'additional_data' => $log->getAdditionalData(),
                    'created_date' => $log->getCreatedDate(),
                ];
            }

            wp_send_json_success([
                'logs' => $logs_data,
                'count' => count($logs_data)
            ]);

        } catch (\Exception $e) {
            wp_send_json_error([
                'message' => __('Error retrieving quote logs.', 'easy-invoice'),
                'debug' => $e->getMessage()
            ]);
        }
    }

    /**
     * Format currency amount using QuoteFormatter
     *
     * @param float $amount The amount to format
     * @param \EasyInvoice\Models\Quote|null $quote The quote object for currency settings
     * @return string Formatted currency string
     */
    private function formatCurrency(float $amount, $quote = null): string {
        $formatter = new \EasyInvoice\Helpers\QuoteFormatter($quote);
        return $formatter->format($amount);
    }
}
