<?php
/**
 * GGM Commerce Transaction Export Class
 * Handles transaction exports for bookkeepers/accountants
 *
 * @package GGM_Commerce
 */

// Prevent direct access.
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery
// phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
// phpcs:disable PluginCheck.Security.DirectDB.UnescapedDBParameter

/**
 * Transaction Export class for generating accounting exports
 */
class GGM_Commerce_Transaction_Export {

    /**
     * Singleton instance
     *
     * @var GGM_Commerce_Transaction_Export
     */
    private static $instance = null;

    /**
     * Export directory path
     *
     * @var string
     */
    private $export_dir;

    /**
     * Export directory URL
     *
     * @var string
     */
    private $export_url;

    /**
     * Get singleton instance
     *
     * @return GGM_Commerce_Transaction_Export
     */
    public static function get_instance() {
        if ( null === self::$instance ) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * Constructor
     */
    private function __construct() {
        $upload_dir = wp_upload_dir();
        $this->export_dir = $upload_dir['basedir'] . '/ggm-commerce-exports/transactions/';
        $this->export_url = $upload_dir['baseurl'] . '/ggm-commerce-exports/transactions/';

        // Ensure export directory exists.
        $this->ensure_export_directory();

        // Register AJAX handlers.
        add_action( 'wp_ajax_ggmc_generate_transaction_export', array( $this, 'ajax_generate_export' ) );
        add_action( 'wp_ajax_ggmc_list_export_batches', array( $this, 'ajax_list_batches' ) );
        add_action( 'wp_ajax_ggmc_download_export', array( $this, 'ajax_download_export' ) );
        add_action( 'wp_ajax_ggmc_mark_orders_unexported', array( $this, 'ajax_mark_unexported' ) );
        add_action( 'wp_ajax_ggmc_get_unexported_orders_count', array( $this, 'ajax_get_unexported_count' ) );
    }

    /**
     * Ensure export directory exists with security files
     */
    private function ensure_export_directory() {
        if ( ! file_exists( $this->export_dir ) ) {
            wp_mkdir_p( $this->export_dir );
        }

        // Add security files.
        $index_file = $this->export_dir . 'index.php';
        if ( ! file_exists( $index_file ) ) {
            // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
            file_put_contents( $index_file, '<?php // Silence is golden.' );
        }

        $htaccess_file = $this->export_dir . '.htaccess';
        if ( ! file_exists( $htaccess_file ) ) {
            // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
            file_put_contents( $htaccess_file, 'deny from all' );
        }
    }

    /**
     * Generate a new export batch
     *
     * @param string $date_from Start date (Y-m-d format).
     * @param string $date_to End date (Y-m-d format).
     * @param string $format Export format (csv or json).
     * @param string $notes Optional notes for the export.
     * @return array Result with success status and batch data or error message.
     */
    public function generate_export( $date_from, $date_to, $format = 'csv', $notes = '' ) {
        global $wpdb;

        // Validate dates.
        $date_from_obj = \DateTime::createFromFormat( 'Y-m-d', $date_from );
        $date_to_obj = \DateTime::createFromFormat( 'Y-m-d', $date_to );

        if ( ! $date_from_obj || ! $date_to_obj ) {
            return array(
                'success' => false,
                'message' => __( 'Invalid date format. Please use YYYY-MM-DD.', 'ggm-commerce' ),
            );
        }

        // Set times for full day range.
        $date_from_full = $date_from . ' 00:00:00';
        $date_to_full = $date_to . ' 23:59:59';

        // Get orders in date range that haven't been exported.
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $orders = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT * FROM {$wpdb->prefix}ggmc_orders
                WHERE created_at >= %s
                AND created_at <= %s
                AND (export_batch_id IS NULL OR export_batch_id = 0)
                AND payment_status IN ('paid', 'refunded')
                ORDER BY created_at ASC",
                $date_from_full,
                $date_to_full
            )
        );

        if ( empty( $orders ) ) {
            return array(
                'success' => false,
                'message' => __( 'No unexported orders found in the selected date range.', 'ggm-commerce' ),
            );
        }

        // Calculate totals.
        $totals = $this->calculate_totals( $orders );

        // Generate batch number.
        $batch_number = $this->generate_batch_number();

        // Generate export file.
        if ( 'json' === $format ) {
            $file_result = $this->generate_json_file( $orders, $totals, $batch_number );
        } else {
            $file_result = $this->generate_csv_file( $orders, $totals, $batch_number );
        }

        if ( ! $file_result['success'] ) {
            return $file_result;
        }

        // Get current user info.
        $current_user = wp_get_current_user();

        // Create export batch record.
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $inserted = $wpdb->insert(
            "{$wpdb->prefix}ggmc_export_batches",
            array(
                'batch_number'              => $batch_number,
                'export_type'               => 'transactions',
                'date_from'                 => $date_from_full,
                'date_to'                   => $date_to_full,
                'order_count'               => $totals['order_count'],
                'refund_count'              => $totals['refund_count'],
                'total_sales'               => $totals['total_sales'],
                'total_refunds'             => $totals['total_refunds'],
                'total_tax'                 => $totals['total_tax'],
                'total_shipping'            => $totals['total_shipping'],
                'net_total'                 => $totals['net_total'],
                'payment_method_breakdown'  => wp_json_encode( $totals['by_payment_method'] ),
                'export_format'             => $format,
                'file_path'                 => $file_result['file_path'],
                'notes'                     => sanitize_textarea_field( $notes ),
                'created_by'                => $current_user->ID,
                'created_by_name'           => $current_user->display_name,
            ),
            array( '%s', '%s', '%s', '%s', '%d', '%d', '%f', '%f', '%f', '%f', '%f', '%s', '%s', '%s', '%s', '%d', '%s' )
        );

        if ( ! $inserted ) {
            return array(
                'success' => false,
                'message' => __( 'Failed to create export batch record.', 'ggm-commerce' ),
            );
        }

        $batch_id = $wpdb->insert_id;

        // Mark orders as exported.
        $order_ids = wp_list_pluck( $orders, 'id' );
        $placeholders = implode( ',', array_fill( 0, count( $order_ids ), '%d' ) );

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
        $wpdb->query(
            $wpdb->prepare(
                "UPDATE {$wpdb->prefix}ggmc_orders SET export_batch_id = %d WHERE id IN ($placeholders)",
                array_merge( array( $batch_id ), $order_ids )
            )
        );

        return array(
            'success'      => true,
            'message'      => sprintf(
                /* translators: %d: number of orders exported */
                __( 'Successfully exported %d orders.', 'ggm-commerce' ),
                count( $orders )
            ),
            'batch_id'     => $batch_id,
            'batch_number' => $batch_number,
            'file_url'     => $file_result['file_url'],
            'totals'       => $totals,
        );
    }

    /**
     * Calculate totals from orders
     *
     * @param array $orders Array of order objects.
     * @return array Calculated totals.
     */
    private function calculate_totals( $orders ) {
        $totals = array(
            'order_count'       => 0,
            'refund_count'      => 0,
            'total_sales'       => 0.00,
            'total_refunds'     => 0.00,
            'total_tax'         => 0.00,
            'total_shipping'    => 0.00,
            'net_total'         => 0.00,
            'by_payment_method' => array(),
        );

        foreach ( $orders as $order ) {
            $is_refund = 'refunded' === $order->payment_status;

            if ( $is_refund ) {
                $totals['refund_count']++;
                $totals['total_refunds'] += (float) $order->total_amount;
            } else {
                $totals['order_count']++;
                $totals['total_sales'] += (float) $order->total_amount;
            }

            $totals['total_tax'] += (float) $order->tax_amount;
            $totals['total_shipping'] += (float) $order->shipping_amount;

            // Track by payment method.
            $method = $order->payment_method ?: 'unknown';
            if ( ! isset( $totals['by_payment_method'][ $method ] ) ) {
                $totals['by_payment_method'][ $method ] = array(
                    'orders'  => 0,
                    'refunds' => 0,
                    'total'   => 0.00,
                );
            }

            if ( $is_refund ) {
                $totals['by_payment_method'][ $method ]['refunds']++;
                $totals['by_payment_method'][ $method ]['total'] -= (float) $order->total_amount;
            } else {
                $totals['by_payment_method'][ $method ]['orders']++;
                $totals['by_payment_method'][ $method ]['total'] += (float) $order->total_amount;
            }
        }

        $totals['net_total'] = $totals['total_sales'] - $totals['total_refunds'];

        return $totals;
    }

    /**
     * Generate batch number
     *
     * @return string Unique batch number.
     */
    private function generate_batch_number() {
        global $wpdb;

        $year = gmdate( 'Y' );

        // Get the highest batch number for this year.
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $last_batch = $wpdb->get_var(
            $wpdb->prepare(
                "SELECT batch_number FROM {$wpdb->prefix}ggmc_export_batches
                WHERE batch_number LIKE %s
                ORDER BY id DESC LIMIT 1",
                'EXP-' . $year . '-%'
            )
        );

        if ( $last_batch ) {
            $last_number = (int) substr( $last_batch, -6 );
            $new_number = $last_number + 1;
        } else {
            $new_number = 1;
        }

        return sprintf( 'EXP-%s-%06d', $year, $new_number );
    }

    /**
     * Generate CSV export file
     *
     * @param array  $orders Array of order objects.
     * @param array  $totals Calculated totals.
     * @param string $batch_number Batch number.
     * @return array Result with file path and URL or error.
     */
    private function generate_csv_file( $orders, $totals, $batch_number ) {
        $filename = 'transaction-export-' . $batch_number . '.csv';
        $filepath = $this->export_dir . $filename;

        // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
        $file = fopen( $filepath, 'w' );

        if ( ! $file ) {
            return array(
                'success' => false,
                'message' => __( 'Failed to create export file.', 'ggm-commerce' ),
            );
        }

        // Add UTF-8 BOM for Excel compatibility.
        // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fwrite
        fwrite( $file, "\xEF\xBB\xBF" );

        // Write summary section.
        fputcsv( $file, array( 'TRANSACTION EXPORT SUMMARY' ) );
        fputcsv( $file, array( 'Batch Number', $batch_number ) );
        fputcsv( $file, array( 'Generated', gmdate( 'Y-m-d H:i:s' ) ) );
        fputcsv( $file, array( 'Generated By', wp_get_current_user()->display_name ) );
        fputcsv( $file, array() );

        fputcsv( $file, array( 'TOTALS' ) );
        fputcsv( $file, array( 'Total Orders', $totals['order_count'] ) );
        fputcsv( $file, array( 'Total Refunds', $totals['refund_count'] ) );
        fputcsv( $file, array( 'Gross Sales', number_format( $totals['total_sales'], 2 ) ) );
        fputcsv( $file, array( 'Total Refunded', number_format( $totals['total_refunds'], 2 ) ) );
        fputcsv( $file, array( 'Total Tax', number_format( $totals['total_tax'], 2 ) ) );
        fputcsv( $file, array( 'Total Shipping', number_format( $totals['total_shipping'], 2 ) ) );
        fputcsv( $file, array( 'Net Total', number_format( $totals['net_total'], 2 ) ) );
        fputcsv( $file, array() );

        // Payment method breakdown.
        fputcsv( $file, array( 'BY PAYMENT METHOD' ) );
        fputcsv( $file, array( 'Method', 'Orders', 'Refunds', 'Total' ) );
        foreach ( $totals['by_payment_method'] as $method => $data ) {
            fputcsv( $file, array( $method, $data['orders'], $data['refunds'], number_format( $data['total'], 2 ) ) );
        }
        fputcsv( $file, array() );

        // Transaction details.
        fputcsv( $file, array( 'TRANSACTION DETAILS' ) );
        fputcsv(
            $file,
            array(
                'Order Number',
                'Date',
                'Type',
                'Customer Email',
                'Customer Name',
                'Subtotal',
                'Tax',
                'Shipping',
                'Discount',
                'Total',
                'Payment Method',
                'Payment Status',
            )
        );

        foreach ( $orders as $order ) {
            $type = 'refunded' === $order->payment_status ? 'Refund' : 'Sale';
            $discount = isset( $order->discount_amount ) ? (float) $order->discount_amount : 0.00;

            fputcsv(
                $file,
                array(
                    $order->order_number,
                    $order->created_at,
                    $type,
                    $order->customer_email,
                    $order->customer_first_name . ' ' . $order->customer_last_name,
                    number_format( (float) $order->subtotal, 2 ),
                    number_format( (float) $order->tax_amount, 2 ),
                    number_format( (float) $order->shipping_amount, 2 ),
                    number_format( $discount, 2 ),
                    number_format( (float) $order->total_amount, 2 ),
                    $order->payment_method,
                    $order->payment_status,
                )
            );
        }

        // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
        fclose( $file );

        return array(
            'success'   => true,
            'file_path' => $filepath,
            'file_url'  => $this->export_url . $filename,
        );
    }

    /**
     * Generate JSON export file
     *
     * @param array  $orders Array of order objects.
     * @param array  $totals Calculated totals.
     * @param string $batch_number Batch number.
     * @return array Result with file path and URL or error.
     */
    private function generate_json_file( $orders, $totals, $batch_number ) {
        $filename = 'transaction-export-' . $batch_number . '.json';
        $filepath = $this->export_dir . $filename;

        $export_data = array(
            'meta' => array(
                'batch_number'  => $batch_number,
                'generated_at'  => gmdate( 'Y-m-d H:i:s' ),
                'generated_by'  => wp_get_current_user()->display_name,
                'export_format' => '1.0',
            ),
            'summary' => array(
                'order_count'       => $totals['order_count'],
                'refund_count'      => $totals['refund_count'],
                'total_sales'       => $totals['total_sales'],
                'total_refunds'     => $totals['total_refunds'],
                'total_tax'         => $totals['total_tax'],
                'total_shipping'    => $totals['total_shipping'],
                'net_total'         => $totals['net_total'],
                'by_payment_method' => $totals['by_payment_method'],
            ),
            'transactions' => array(),
        );

        foreach ( $orders as $order ) {
            $discount = isset( $order->discount_amount ) ? (float) $order->discount_amount : 0.00;

            $export_data['transactions'][] = array(
                'order_number'    => $order->order_number,
                'date'            => $order->created_at,
                'type'            => 'refunded' === $order->payment_status ? 'refund' : 'sale',
                'customer_email'  => $order->customer_email,
                'customer_name'   => $order->customer_first_name . ' ' . $order->customer_last_name,
                'subtotal'        => (float) $order->subtotal,
                'tax'             => (float) $order->tax_amount,
                'shipping'        => (float) $order->shipping_amount,
                'discount'        => $discount,
                'total'           => (float) $order->total_amount,
                'payment_method'  => $order->payment_method,
                'payment_status'  => $order->payment_status,
            );
        }

        // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
        $result = file_put_contents( $filepath, wp_json_encode( $export_data, JSON_PRETTY_PRINT ) );

        if ( false === $result ) {
            return array(
                'success' => false,
                'message' => __( 'Failed to create export file.', 'ggm-commerce' ),
            );
        }

        return array(
            'success'   => true,
            'file_path' => $filepath,
            'file_url'  => $this->export_url . $filename,
        );
    }

    /**
     * Get unexported orders
     *
     * @param string $date_from Optional start date filter.
     * @param string $date_to Optional end date filter.
     * @return array Array of unexported orders.
     */
    public function get_unexported_orders( $date_from = '', $date_to = '' ) {
        global $wpdb;

        $where = "WHERE (export_batch_id IS NULL OR export_batch_id = 0) AND payment_status IN ('paid', 'refunded')";
        $params = array();

        if ( $date_from ) {
            $where .= ' AND created_at >= %s';
            $params[] = $date_from . ' 00:00:00';
        }

        if ( $date_to ) {
            $where .= ' AND created_at <= %s';
            $params[] = $date_to . ' 23:59:59';
        }

        $query = "SELECT * FROM {$wpdb->prefix}ggmc_orders $where ORDER BY created_at DESC";

        if ( ! empty( $params ) ) {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
            return $wpdb->get_results( $wpdb->prepare( $query, $params ) );
        }

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
        return $wpdb->get_results( $query );
    }

    /**
     * Get export batch by ID
     *
     * @param int $batch_id Batch ID.
     * @return object|null Batch object or null.
     */
    public function get_export_batch( $batch_id ) {
        global $wpdb;

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $batch = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT * FROM {$wpdb->prefix}ggmc_export_batches WHERE id = %d",
                $batch_id
            )
        );

        if ( $batch && $batch->payment_method_breakdown ) {
            $batch->payment_method_breakdown = json_decode( $batch->payment_method_breakdown, true );
        }

        return $batch;
    }

    /**
     * List export batches
     *
     * @param int $page Page number.
     * @param int $per_page Items per page.
     * @return array Array with batches and pagination info.
     */
    public function list_export_batches( $page = 1, $per_page = 20 ) {
        global $wpdb;

        $offset = ( $page - 1 ) * $per_page;

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $total = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}ggmc_export_batches" );

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $batches = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT * FROM {$wpdb->prefix}ggmc_export_batches ORDER BY created_at DESC LIMIT %d OFFSET %d",
                $per_page,
                $offset
            )
        );

        foreach ( $batches as $batch ) {
            if ( $batch->payment_method_breakdown ) {
                $batch->payment_method_breakdown = json_decode( $batch->payment_method_breakdown, true );
            }
            // Add download URL.
            if ( $batch->file_path && file_exists( $batch->file_path ) ) {
                $batch->download_url = admin_url( 'admin-ajax.php?action=ggmc_download_export&batch_id=' . $batch->id . '&nonce=' . wp_create_nonce( 'ggmc_download_export_' . $batch->id ) );
            }
        }

        return array(
            'batches'    => $batches,
            'total'      => (int) $total,
            'page'       => $page,
            'per_page'   => $per_page,
            'total_pages' => ceil( $total / $per_page ),
        );
    }

    /**
     * Regenerate an existing export batch
     *
     * @param int $batch_id Batch ID to regenerate.
     * @return array Result with success status.
     */
    public function regenerate_export( $batch_id ) {
        global $wpdb;

        $batch = $this->get_export_batch( $batch_id );

        if ( ! $batch ) {
            return array(
                'success' => false,
                'message' => __( 'Export batch not found.', 'ggm-commerce' ),
            );
        }

        // Get orders that were in this batch.
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $orders = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT * FROM {$wpdb->prefix}ggmc_orders WHERE export_batch_id = %d ORDER BY created_at ASC",
                $batch_id
            )
        );

        if ( empty( $orders ) ) {
            return array(
                'success' => false,
                'message' => __( 'No orders found for this batch.', 'ggm-commerce' ),
            );
        }

        // Recalculate totals.
        $totals = $this->calculate_totals( $orders );

        // Regenerate file.
        if ( 'json' === $batch->export_format ) {
            $file_result = $this->generate_json_file( $orders, $totals, $batch->batch_number );
        } else {
            $file_result = $this->generate_csv_file( $orders, $totals, $batch->batch_number );
        }

        if ( ! $file_result['success'] ) {
            return $file_result;
        }

        // Update batch record.
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->update(
            "{$wpdb->prefix}ggmc_export_batches",
            array( 'file_path' => $file_result['file_path'] ),
            array( 'id' => $batch_id ),
            array( '%s' ),
            array( '%d' )
        );

        return array(
            'success'  => true,
            'message'  => __( 'Export regenerated successfully.', 'ggm-commerce' ),
            'file_url' => $file_result['file_url'],
        );
    }

    /**
     * Mark orders as unexported
     *
     * @param array $order_ids Array of order IDs.
     * @return array Result with success status.
     */
    public function mark_orders_unexported( $order_ids ) {
        global $wpdb;

        if ( empty( $order_ids ) ) {
            return array(
                'success' => false,
                'message' => __( 'No order IDs provided.', 'ggm-commerce' ),
            );
        }

        $order_ids = array_map( 'absint', $order_ids );
        $placeholders = implode( ',', array_fill( 0, count( $order_ids ), '%d' ) );

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
        $updated = $wpdb->query(
            $wpdb->prepare(
                "UPDATE {$wpdb->prefix}ggmc_orders SET export_batch_id = NULL WHERE id IN ($placeholders)",
                $order_ids
            )
        );

        return array(
            'success' => true,
            'message' => sprintf(
                /* translators: %d: number of orders marked */
                __( '%d orders marked as unexported.', 'ggm-commerce' ),
                $updated
            ),
            'count'   => $updated,
        );
    }

    /**
     * AJAX: Generate export
     */
    public function ajax_generate_export() {
        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce verification.
        if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'ggmc_transaction_export' ) ) {
            wp_send_json_error( array( 'message' => __( 'Security check failed.', 'ggm-commerce' ) ) );
        }

        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( array( 'message' => __( 'Permission denied.', 'ggm-commerce' ) ) );
        }

        $date_from = isset( $_POST['date_from'] ) ? sanitize_text_field( wp_unslash( $_POST['date_from'] ) ) : '';
        $date_to = isset( $_POST['date_to'] ) ? sanitize_text_field( wp_unslash( $_POST['date_to'] ) ) : '';
        $format = isset( $_POST['format'] ) ? sanitize_text_field( wp_unslash( $_POST['format'] ) ) : 'csv';
        $notes = isset( $_POST['notes'] ) ? sanitize_textarea_field( wp_unslash( $_POST['notes'] ) ) : '';

        if ( empty( $date_from ) || empty( $date_to ) ) {
            wp_send_json_error( array( 'message' => __( 'Please provide both start and end dates.', 'ggm-commerce' ) ) );
        }

        $result = $this->generate_export( $date_from, $date_to, $format, $notes );

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

    /**
     * AJAX: List batches
     */
    public function ajax_list_batches() {
        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce verification.
        if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'ggmc_transaction_export' ) ) {
            wp_send_json_error( array( 'message' => __( 'Security check failed.', 'ggm-commerce' ) ) );
        }

        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( array( 'message' => __( 'Permission denied.', 'ggm-commerce' ) ) );
        }

        $page = isset( $_POST['page'] ) ? absint( $_POST['page'] ) : 1;
        $per_page = isset( $_POST['per_page'] ) ? absint( $_POST['per_page'] ) : 20;

        $result = $this->list_export_batches( $page, $per_page );

        wp_send_json_success( $result );
    }

    /**
     * AJAX: Download export file
     */
    public function ajax_download_export() {
        $batch_id = isset( $_GET['batch_id'] ) ? absint( $_GET['batch_id'] ) : 0;
        $nonce = isset( $_GET['nonce'] ) ? sanitize_text_field( wp_unslash( $_GET['nonce'] ) ) : '';

        if ( ! wp_verify_nonce( $nonce, 'ggmc_download_export_' . $batch_id ) ) {
            wp_die( esc_html__( 'Security check failed.', 'ggm-commerce' ) );
        }

        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( esc_html__( 'Permission denied.', 'ggm-commerce' ) );
        }

        $batch = $this->get_export_batch( $batch_id );

        if ( ! $batch || ! $batch->file_path || ! file_exists( $batch->file_path ) ) {
            wp_die( esc_html__( 'Export file not found.', 'ggm-commerce' ) );
        }

        $filename = basename( $batch->file_path );
        $content_type = 'json' === $batch->export_format ? 'application/json' : 'text/csv';

        header( 'Content-Type: ' . $content_type );
        header( 'Content-Disposition: attachment; filename="' . $filename . '"' );
        header( 'Content-Length: ' . filesize( $batch->file_path ) );
        header( 'Cache-Control: no-cache, no-store, must-revalidate' );

        // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_readfile
        readfile( $batch->file_path );
        exit;
    }

    /**
     * AJAX: Mark orders as unexported
     */
    public function ajax_mark_unexported() {
        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce verification.
        if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'ggmc_transaction_export' ) ) {
            wp_send_json_error( array( 'message' => __( 'Security check failed.', 'ggm-commerce' ) ) );
        }

        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( array( 'message' => __( 'Permission denied.', 'ggm-commerce' ) ) );
        }

        $order_ids = isset( $_POST['order_ids'] ) ? array_map( 'absint', (array) $_POST['order_ids'] ) : array();

        $result = $this->mark_orders_unexported( $order_ids );

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

    /**
     * AJAX: Get unexported orders count
     */
    public function ajax_get_unexported_count() {
        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce verification.
        if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'ggmc_transaction_export' ) ) {
            wp_send_json_error( array( 'message' => __( 'Security check failed.', 'ggm-commerce' ) ) );
        }

        $date_from = isset( $_POST['date_from'] ) ? sanitize_text_field( wp_unslash( $_POST['date_from'] ) ) : '';
        $date_to = isset( $_POST['date_to'] ) ? sanitize_text_field( wp_unslash( $_POST['date_to'] ) ) : '';

        $orders = $this->get_unexported_orders( $date_from, $date_to );

        // Calculate preview totals.
        $totals = $this->calculate_totals( $orders );

        wp_send_json_success(
            array(
                'count'  => count( $orders ),
                'totals' => $totals,
            )
        );
    }
}
