<?php

/**
 * Admin controller.
 *
 * @package CartNinjaRecovery
 */
if ( !defined( 'ABSPATH' ) ) {
    exit;
}
class CARTNINJA_Admin {
    private $notification_manager;

    private $cart_tracker;

    public function __construct( CARTNINJA_Notification_Manager $notification_manager, CARTNINJA_Cart_Tracker $cart_tracker ) {
        $this->notification_manager = $notification_manager;
        $this->cart_tracker = $cart_tracker;
    }

    public function register_menu() {
        $capability = $this->get_admin_capability();
        add_menu_page(
            __( 'Cart Recovery', 'cartninja-recovery-for-woocommerce' ),
            __( 'Cart Recovery', 'cartninja-recovery-for-woocommerce' ),
            $capability,
            'cartninja-dashboard',
            array($this, 'render_dashboard_page'),
            'dashicons-format-status',
            56
        );
        add_submenu_page(
            'cartninja-dashboard',
            __( 'Dashboard', 'cartninja-recovery-for-woocommerce' ),
            __( 'Dashboard', 'cartninja-recovery-for-woocommerce' ),
            $capability,
            'cartninja-dashboard',
            array($this, 'render_dashboard_page')
        );
        add_submenu_page(
            'cartninja-dashboard',
            __( 'Carts', 'cartninja-recovery-for-woocommerce' ),
            __( 'Carts', 'cartninja-recovery-for-woocommerce' ),
            $capability,
            'cartninja-carts',
            array($this, 'render_carts_page')
        );
        add_submenu_page(
            'cartninja-dashboard',
            __( 'Settings', 'cartninja-recovery-for-woocommerce' ),
            __( 'Settings', 'cartninja-recovery-for-woocommerce' ),
            $capability,
            'cartninja-settings',
            array($this, 'render_settings_page')
        );
        if ( cartninja_is_premium_build() ) {
            if ( class_exists( 'CARTNINJA_Sequence_Builder' ) ) {
                add_submenu_page(
                    'cartninja-dashboard',
                    __( 'Sequences', 'cartninja-recovery-for-woocommerce' ),
                    __( 'Sequences', 'cartninja-recovery-for-woocommerce' ),
                    $capability,
                    'cartninja-sequences',
                    array($this, 'render_sequences_page')
                );
            }
        }
    }

    public function register_settings() {
        register_setting( 'cartninja_settings_group', 'cartninja_settings', array(
            'sanitize_callback' => array($this, 'sanitize_settings'),
            'default'           => array(),
        ) );
    }

    public function enqueue_assets( $hook_suffix ) {
        if ( false === strpos( $hook_suffix, 'cartninja' ) ) {
            return;
        }
        $admin_css_version = ( file_exists( CARTNINJA_PLUGIN_DIR . 'admin/css/admin.css' ) ? CARTNINJA_VERSION . '-' . filemtime( CARTNINJA_PLUGIN_DIR . 'admin/css/admin.css' ) : CARTNINJA_VERSION );
        $admin_js_version = ( file_exists( CARTNINJA_PLUGIN_DIR . 'admin/js/admin.js' ) ? CARTNINJA_VERSION . '-' . filemtime( CARTNINJA_PLUGIN_DIR . 'admin/js/admin.js' ) : CARTNINJA_VERSION );
        wp_enqueue_style(
            'cartninja-admin',
            CARTNINJA_PLUGIN_URL . 'admin/css/admin.css',
            array(),
            $admin_css_version
        );
        wp_enqueue_script( 'jquery-ui-sortable' );
        wp_enqueue_script(
            'chart-js',
            CARTNINJA_PLUGIN_URL . 'admin/js/chart.min.js',
            array(),
            CARTNINJA_VERSION,
            true
        );
        wp_enqueue_script(
            'cartninja-admin',
            CARTNINJA_PLUGIN_URL . 'admin/js/admin.js',
            array('jquery', 'jquery-ui-sortable', 'chart-js'),
            $admin_js_version,
            true
        );
        $selected_gateway = new CARTNINJA_Twilio_Gateway();
        if ( cartninja_is_premium_unlocked() ) {
            if ( class_exists( 'CARTNINJA_Gateway_Factory' ) ) {
                $selected_gateway = CARTNINJA_Gateway_Factory::make();
            }
        }
        $sms_sender = new CARTNINJA_SMS_Sender($selected_gateway);
        wp_localize_script( 'cartninja-admin', 'cartninjaAdmin', array(
            'ajaxUrl' => admin_url( 'admin-ajax.php' ),
            'nonce'   => wp_create_nonce( 'cartninja_admin_nonce' ),
            'strings' => array(
                'testError'      => __( 'Unable to send test SMS.', 'cartninja-recovery-for-woocommerce' ),
                'emailTestError' => __( 'Unable to send test email.', 'cartninja-recovery-for-woocommerce' ),
                'cartLoadError'  => __( 'Unable to load cart details.', 'cartninja-recovery-for-woocommerce' ),
                'loadingCart'    => __( 'Loading cart details...', 'cartninja-recovery-for-woocommerce' ),
                'smsPreview'     => $sms_sender->render_preview(),
                'stepLabel'      => __( 'Step', 'cartninja-recovery-for-woocommerce' ),
                'sequenceSaved'  => __( 'Sequence saved successfully.', 'cartninja-recovery-for-woocommerce' ),
                'sequenceError'  => __( 'Unable to save the sequence.', 'cartninja-recovery-for-woocommerce' ),
            ),
        ) );
    }

    public function add_plugin_action_links( $links ) {
        if ( !current_user_can( $this->get_admin_capability() ) ) {
            return $links;
        }
        $settings_link = sprintf( '<a href="%1$s">%2$s</a>', esc_url( admin_url( 'admin.php?page=cartninja-settings' ) ), esc_html__( 'Settings', 'cartninja-recovery-for-woocommerce' ) );
        array_unshift( $links, $settings_link );
        return $links;
    }

    public function add_plugin_row_meta(
        $links,
        $file,
        $plugin_data,
        $status
    ) {
        unset($plugin_data, $status);
        if ( CARTNINJA_PLUGIN_BASENAME !== $file ) {
            return $links;
        }
        $details_url = add_query_arg( array(
            'tab'       => 'plugin-information',
            'plugin'    => 'cartninja-recovery-for-woocommerce',
            'TB_iframe' => 'true',
            'width'     => '600',
            'height'    => '550',
        ), self_admin_url( 'plugin-install.php' ) );
        $links[] = sprintf(
            '<a href="%1$s" class="thickbox open-plugin-details-modal" aria-label="%2$s">%3$s</a>',
            esc_url( $details_url ),
            esc_attr__( 'View CartNinja Recovery for WooCommerce details', 'cartninja-recovery-for-woocommerce' ),
            esc_html__( 'View Details', 'cartninja-recovery-for-woocommerce' )
        );
        return $links;
    }

    public function add_plugin_auto_update_setting( $html, $plugin_file, $plugin_data ) {
        unset($plugin_data);
        if ( CARTNINJA_PLUGIN_BASENAME !== $plugin_file || !current_user_can( 'update_plugins' ) ) {
            return $html;
        }
        if ( false !== strpos( $html, 'toggle-auto-update' ) ) {
            return $html;
        }
        $auto_updates = (array) get_site_option( 'auto_update_plugins', array() );
        $enabled = in_array( CARTNINJA_PLUGIN_BASENAME, $auto_updates, true );
        $action = ( $enabled ? 'disable-auto-update' : 'enable-auto-update' );
        $wp_action = ( $enabled ? 'disable' : 'enable' );
        $label = ( $enabled ? __( 'Disable auto-updates', 'cartninja-recovery-for-woocommerce' ) : __( 'Enable auto-updates', 'cartninja-recovery-for-woocommerce' ) );
        $url = wp_nonce_url( add_query_arg( array(
            'action' => $action,
            'plugin' => CARTNINJA_PLUGIN_BASENAME,
        ), self_admin_url( 'plugins.php' ) ), 'updates' );
        return sprintf(
            '<a href="%1$s" class="toggle-auto-update" data-wp-action="%2$s">%3$s</a>',
            esc_url( $url ),
            esc_attr( $wp_action ),
            esc_html( $label )
        );
    }

    public function maybe_show_woocommerce_notice() {
        if ( !current_user_can( 'manage_options' ) || class_exists( 'WooCommerce' ) ) {
            return;
        }
        echo '<div class="notice notice-warning"><p>' . esc_html__( 'CartNinja Recovery for WooCommerce requires WooCommerce to be active for cart tracking and recovery.', 'cartninja-recovery-for-woocommerce' ) . '</p></div>';
    }

    public function ajax_send_test_sms() {
        check_ajax_referer( 'cartninja_admin_nonce', 'nonce' );
        if ( !current_user_can( $this->get_admin_capability() ) ) {
            wp_send_json_error( array(
                'message' => __( 'Permission denied.', 'cartninja-recovery-for-woocommerce' ),
            ), 403 );
        }
        $phone = ( isset( $_POST['phone'] ) ? sanitize_text_field( wp_unslash( $_POST['phone'] ) ) : '' );
        if ( empty( $phone ) ) {
            wp_send_json_error( array(
                'message' => __( 'Please provide a destination phone number.', 'cartninja-recovery-for-woocommerce' ),
            ), 400 );
        }
        $result = $this->notification_manager->send_test_sms( $phone );
        if ( 'sent' === $result['status'] ) {
            wp_send_json_success( array(
                'message' => __( 'Test SMS sent successfully.', 'cartninja-recovery-for-woocommerce' ),
            ) );
        }
        wp_send_json_error( array(
            'message' => $result['error_message'],
        ), 400 );
    }

    public function ajax_send_test_email() {
        check_ajax_referer( 'cartninja_admin_nonce', 'nonce' );
        if ( !current_user_can( $this->get_admin_capability() ) ) {
            wp_send_json_error( array(
                'message' => __( 'Permission denied.', 'cartninja-recovery-for-woocommerce' ),
            ), 403 );
        }
        $email = ( isset( $_POST['email'] ) ? sanitize_email( wp_unslash( $_POST['email'] ) ) : '' );
        if ( empty( $email ) || !is_email( $email ) ) {
            wp_send_json_error( array(
                'message' => __( 'Please provide a valid destination email address.', 'cartninja-recovery-for-woocommerce' ),
            ), 400 );
        }
        $context = array(
            'email_subject'     => ( isset( $_POST['email_subject'] ) ? sanitize_text_field( wp_unslash( $_POST['email_subject'] ) ) : '' ),
            'email_heading'     => ( isset( $_POST['email_heading'] ) ? sanitize_text_field( wp_unslash( $_POST['email_heading'] ) ) : '' ),
            'email_template'    => ( isset( $_POST['email_template'] ) ? wp_kses_post( wp_unslash( $_POST['email_template'] ) ) : '' ),
            'email_button_text' => ( isset( $_POST['email_button_text'] ) ? sanitize_text_field( wp_unslash( $_POST['email_button_text'] ) ) : '' ),
            'email_footer_text' => ( isset( $_POST['email_footer_text'] ) ? wp_kses_post( wp_unslash( $_POST['email_footer_text'] ) ) : '' ),
        );
        $result = $this->notification_manager->send_test_email( $email, $context );
        if ( 'sent' === $result['status'] ) {
            wp_send_json_success( array(
                'message' => __( 'Test email sent successfully.', 'cartninja-recovery-for-woocommerce' ),
            ) );
        }
        wp_send_json_error( array(
            'message' => $result['error_message'],
        ), 400 );
    }

    public function ajax_get_cart_details() {
        check_ajax_referer( 'cartninja_admin_nonce', 'nonce' );
        if ( !current_user_can( $this->get_admin_capability() ) ) {
            wp_send_json_error( array(
                'message' => __( 'Permission denied.', 'cartninja-recovery-for-woocommerce' ),
            ), 403 );
        }
        $cart_id = ( isset( $_POST['cart_id'] ) ? absint( wp_unslash( $_POST['cart_id'] ) ) : 0 );
        $cart = ( $cart_id ? $this->get_cart_for_view( $cart_id ) : null );
        if ( empty( $cart ) ) {
            wp_send_json_error( array(
                'message' => __( 'Cart not found.', 'cartninja-recovery-for-woocommerce' ),
            ), 404 );
        }
        wp_send_json_success( array(
            'html' => $this->get_cart_details_markup( $cart ),
        ) );
    }

    public function handle_delete_cart() {
        if ( !current_user_can( $this->get_admin_capability() ) ) {
            wp_safe_redirect( admin_url( 'admin.php?page=cartninja-carts' ) );
            return;
        }
        check_admin_referer( 'cartninja_delete_cart' );
        $cart_id = ( isset( $_GET['cart_id'] ) ? absint( $_GET['cart_id'] ) : 0 );
        if ( $cart_id ) {
            global $wpdb;
            $wpdb->delete( $wpdb->prefix . 'cartninja_notifications', array(
                'cart_id' => $cart_id,
            ), array('%d') );
            $wpdb->delete( $wpdb->prefix . 'cartninja_carts', array(
                'id' => $cart_id,
            ), array('%d') );
        }
        wp_safe_redirect( admin_url( 'admin.php?page=cartninja-carts&deleted=1' ) );
    }

    public function handle_run_cleanup() {
        if ( !current_user_can( $this->get_admin_capability() ) ) {
            wp_safe_redirect( admin_url( 'admin.php?page=cartninja-settings' ) );
            return;
        }
        check_admin_referer( 'cartninja_run_cleanup' );
        $deleted = $this->cart_tracker->cleanup_tracking_data( 500 );
        wp_safe_redirect( add_query_arg( array(
            'page'                    => 'cartninja-settings',
            'tab'                     => 'general',
            'cartninja_cleanup_done'  => '1',
            'cartninja_cleanup_count' => absint( $deleted ),
        ), admin_url( 'admin.php' ) ) );
    }

    public function handle_resend_notification() {
        if ( !current_user_can( $this->get_admin_capability() ) ) {
            wp_safe_redirect( admin_url( 'admin.php?page=cartninja-carts' ) );
            return;
        }
        check_admin_referer( 'cartninja_resend_notification' );
        $cart_id = ( isset( $_GET['cart_id'] ) ? absint( $_GET['cart_id'] ) : 0 );
        $channel = ( isset( $_GET['channel'] ) ? sanitize_key( wp_unslash( $_GET['channel'] ) ) : '' );
        $sent = false;
        $valid_channels = array('sms', 'email');
        if ( $cart_id && in_array( $channel, $valid_channels, true ) ) {
            $sent = $this->notification_manager->retry_failed_notification( $cart_id, $channel );
        }
        wp_safe_redirect( add_query_arg( array(
            'page'                  => 'cartninja-carts',
            'cartninja_resend_done' => ( $sent ? '1' : '0' ),
        ), admin_url( 'admin.php' ) ) );
    }

    public function sanitize_settings( $input ) {
        $existing = get_option( 'cartninja_settings', array() );
        $output = wp_parse_args( $existing, array(
            'general'    => array(
                'abandonment_threshold' => 15,
                'lost_threshold_days'   => 30,
                'recovery_url_base'     => home_url( '/cart/' ),
                'enable_sms'            => 'yes',
                'enable_email'          => 'yes',
            ),
            'twilio'     => array(
                'account_sid' => '',
                'auth_token'  => '',
                'from_phone'  => '',
            ),
            'message'    => array(
                'sms_template'      => '',
                'email_subject'     => __( 'Complete your purchase at {{site_name}}', 'cartninja-recovery-for-woocommerce' ),
                'email_heading'     => __( 'You left something behind', 'cartninja-recovery-for-woocommerce' ),
                'email_template'    => __( "Hi {{customer_name}},\n\nYou left items worth {{cart_total}} in your cart at {{site_name}}.", 'cartninja-recovery-for-woocommerce' ),
                'email_button_text' => __( 'Resume your checkout', 'cartninja-recovery-for-woocommerce' ),
                'email_footer_text' => '',
            ),
            'protection' => array(
                'enable_guest_protection' => 'yes',
                'daily_guest_ip_limit'    => 20,
                'short_guest_ip_limit'    => 5,
                'short_window_minutes'    => 15,
                'blocked_ips'             => '',
                'blocked_countries'       => '',
                'trust_proxy_headers'     => 'no',
            ),
            'cleanup'    => array(
                'enable_auto_cleanup'       => 'no',
                'guest_retention_days'      => 30,
                'suppressed_retention_days' => 7,
                'terminal_retention_days'   => 90,
                'keep_order_linked_carts'   => 'yes',
            ),
        ) );
        $protection_defaults = array(
            'enable_guest_protection' => 'yes',
            'daily_guest_ip_limit'    => 20,
            'short_guest_ip_limit'    => 5,
            'short_window_minutes'    => 15,
            'blocked_ips'             => '',
            'blocked_countries'       => '',
            'trust_proxy_headers'     => 'no',
        );
        $output['protection'] = wp_parse_args( ( isset( $output['protection'] ) && is_array( $output['protection'] ) ? $output['protection'] : array() ), $protection_defaults );
        $cleanup_defaults = array(
            'enable_auto_cleanup'       => 'no',
            'guest_retention_days'      => 30,
            'suppressed_retention_days' => 7,
            'terminal_retention_days'   => 90,
            'keep_order_linked_carts'   => 'yes',
        );
        $output['cleanup'] = wp_parse_args( ( isset( $output['cleanup'] ) && is_array( $output['cleanup'] ) ? $output['cleanup'] : array() ), $cleanup_defaults );
        $message_defaults = array(
            'sms_template'      => '',
            'email_subject'     => __( 'Complete your purchase at {{site_name}}', 'cartninja-recovery-for-woocommerce' ),
            'email_heading'     => __( 'You left something behind', 'cartninja-recovery-for-woocommerce' ),
            'email_template'    => __( "Hi {{customer_name}},\n\nYou left items worth {{cart_total}} in your cart at {{site_name}}.", 'cartninja-recovery-for-woocommerce' ),
            'email_button_text' => __( 'Resume your checkout', 'cartninja-recovery-for-woocommerce' ),
            'email_footer_text' => '',
        );
        $output['message'] = wp_parse_args( ( isset( $output['message'] ) && is_array( $output['message'] ) ? $output['message'] : array() ), $message_defaults );
        if ( cartninja_is_premium_build() ) {
            $output = wp_parse_args( $output, array(
                'gateway'   => array(
                    'provider' => 'twilio',
                ),
                'vonage'    => array(
                    'api_key'    => '',
                    'api_secret' => ( isset( $existing['vonage']['api_secret'] ) ? $existing['vonage']['api_secret'] : '' ),
                    'from'       => '',
                ),
                'sinch'     => array(
                    'service_plan_id' => '',
                    'api_token'       => ( isset( $existing['sinch']['api_token'] ) ? $existing['sinch']['api_token'] : '' ),
                    'from'            => '',
                ),
                'plivo'     => array(
                    'auth_id'    => '',
                    'auth_token' => ( isset( $existing['plivo']['auth_token'] ) ? $existing['plivo']['auth_token'] : '' ),
                    'from'       => '',
                ),
                'sequences' => array(
                    'active_sequence_id' => 0,
                ),
                'popup'     => array(
                    'enable_popup'             => 'no',
                    'enable_add_to_cart_popup' => 'no',
                    'headline'                 => __( 'Wait! Don\'t leave your cart behind', 'cartninja-recovery-for-woocommerce' ),
                    'subtext'                  => __( 'Enter your phone and we\'ll send you a reminder', 'cartninja-recovery-for-woocommerce' ),
                    'button_text'              => __( 'Save My Cart', 'cartninja-recovery-for-woocommerce' ),
                    'delay_seconds'            => 30,
                    'show_gdpr'                => 'yes',
                    'gdpr_text'                => __( 'I agree to receive cart recovery reminders about my cart', 'cartninja-recovery-for-woocommerce' ),
                    'card_start'               => '#F0FAFA',
                    'card_end'                 => '#ffffff',
                    'button_start'             => '#0EA5A0',
                    'button_end'               => '#0C8A86',
                ),
            ) );
        }
        if ( isset( $input['general'] ) && is_array( $input['general'] ) ) {
            $output['general'] = array(
                'abandonment_threshold' => max( 1, absint( ( isset( $input['general']['abandonment_threshold'] ) ? $input['general']['abandonment_threshold'] : $output['general']['abandonment_threshold'] ) ) ),
                'lost_threshold_days'   => max( 1, absint( ( isset( $input['general']['lost_threshold_days'] ) ? $input['general']['lost_threshold_days'] : $output['general']['lost_threshold_days'] ) ) ),
                'recovery_url_base'     => ( isset( $input['general']['recovery_url_base'] ) ? esc_url_raw( $input['general']['recovery_url_base'] ) : $output['general']['recovery_url_base'] ),
                'enable_sms'            => ( !empty( $input['general']['enable_sms'] ) ? 'yes' : 'no' ),
                'enable_email'          => ( !empty( $input['general']['enable_email'] ) ? 'yes' : 'no' ),
            );
        }
        if ( isset( $input['twilio'] ) && is_array( $input['twilio'] ) ) {
            $output['twilio'] = array(
                'account_sid' => ( isset( $input['twilio']['account_sid'] ) ? sanitize_text_field( $input['twilio']['account_sid'] ) : $output['twilio']['account_sid'] ),
                'auth_token'  => ( isset( $input['twilio']['auth_token'] ) && '' !== $input['twilio']['auth_token'] ? sanitize_text_field( $input['twilio']['auth_token'] ) : $output['twilio']['auth_token'] ),
                'from_phone'  => ( isset( $input['twilio']['from_phone'] ) ? sanitize_text_field( $input['twilio']['from_phone'] ) : $output['twilio']['from_phone'] ),
            );
        }
        if ( isset( $input['message'] ) && is_array( $input['message'] ) ) {
            $output['message'] = array(
                'sms_template'      => ( isset( $input['message']['sms_template'] ) ? sanitize_textarea_field( $input['message']['sms_template'] ) : $output['message']['sms_template'] ),
                'email_subject'     => ( isset( $input['message']['email_subject'] ) ? sanitize_text_field( $input['message']['email_subject'] ) : $output['message']['email_subject'] ),
                'email_heading'     => ( isset( $input['message']['email_heading'] ) ? sanitize_text_field( $input['message']['email_heading'] ) : $output['message']['email_heading'] ),
                'email_template'    => ( isset( $input['message']['email_template'] ) ? wp_kses_post( $input['message']['email_template'] ) : $output['message']['email_template'] ),
                'email_button_text' => ( isset( $input['message']['email_button_text'] ) ? sanitize_text_field( $input['message']['email_button_text'] ) : $output['message']['email_button_text'] ),
                'email_footer_text' => ( isset( $input['message']['email_footer_text'] ) ? wp_kses_post( $input['message']['email_footer_text'] ) : $output['message']['email_footer_text'] ),
            );
        }
        if ( isset( $input['protection'] ) && is_array( $input['protection'] ) ) {
            $output['protection'] = array(
                'enable_guest_protection' => ( !empty( $input['protection']['enable_guest_protection'] ) ? 'yes' : 'no' ),
                'daily_guest_ip_limit'    => max( 1, absint( ( isset( $input['protection']['daily_guest_ip_limit'] ) ? $input['protection']['daily_guest_ip_limit'] : $output['protection']['daily_guest_ip_limit'] ) ) ),
                'short_guest_ip_limit'    => max( 1, absint( ( isset( $input['protection']['short_guest_ip_limit'] ) ? $input['protection']['short_guest_ip_limit'] : $output['protection']['short_guest_ip_limit'] ) ) ),
                'short_window_minutes'    => max( 1, absint( ( isset( $input['protection']['short_window_minutes'] ) ? $input['protection']['short_window_minutes'] : $output['protection']['short_window_minutes'] ) ) ),
                'blocked_ips'             => ( isset( $input['protection']['blocked_ips'] ) ? $this->sanitize_ip_list( $input['protection']['blocked_ips'] ) : $output['protection']['blocked_ips'] ),
                'blocked_countries'       => ( isset( $input['protection']['blocked_countries'] ) ? $this->sanitize_country_code_list( $input['protection']['blocked_countries'] ) : $output['protection']['blocked_countries'] ),
                'trust_proxy_headers'     => ( !empty( $input['protection']['trust_proxy_headers'] ) ? 'yes' : 'no' ),
            );
        }
        if ( isset( $input['cleanup'] ) && is_array( $input['cleanup'] ) ) {
            $output['cleanup'] = array(
                'enable_auto_cleanup'       => ( !empty( $input['cleanup']['enable_auto_cleanup'] ) ? 'yes' : 'no' ),
                'guest_retention_days'      => max( 1, absint( ( isset( $input['cleanup']['guest_retention_days'] ) ? $input['cleanup']['guest_retention_days'] : $output['cleanup']['guest_retention_days'] ) ) ),
                'suppressed_retention_days' => max( 1, absint( ( isset( $input['cleanup']['suppressed_retention_days'] ) ? $input['cleanup']['suppressed_retention_days'] : $output['cleanup']['suppressed_retention_days'] ) ) ),
                'terminal_retention_days'   => max( 1, absint( ( isset( $input['cleanup']['terminal_retention_days'] ) ? $input['cleanup']['terminal_retention_days'] : $output['cleanup']['terminal_retention_days'] ) ) ),
                'keep_order_linked_carts'   => ( !empty( $input['cleanup']['keep_order_linked_carts'] ) ? 'yes' : 'no' ),
            );
        }
        if ( cartninja_is_premium_unlocked() ) {
            $output['gateway'] = array(
                'provider' => ( isset( $input['gateway']['provider'] ) && in_array( sanitize_key( $input['gateway']['provider'] ), array(
                    'twilio',
                    'vonage',
                    'sinch',
                    'plivo'
                ), true ) ? sanitize_key( $input['gateway']['provider'] ) : $output['gateway']['provider'] ),
            );
            $output['vonage'] = array(
                'api_key'    => ( isset( $input['vonage']['api_key'] ) ? sanitize_text_field( $input['vonage']['api_key'] ) : $output['vonage']['api_key'] ),
                'api_secret' => ( isset( $input['vonage']['api_secret'] ) && '' !== $input['vonage']['api_secret'] ? sanitize_text_field( $input['vonage']['api_secret'] ) : $output['vonage']['api_secret'] ),
                'from'       => ( isset( $input['vonage']['from'] ) ? sanitize_text_field( $input['vonage']['from'] ) : $output['vonage']['from'] ),
            );
            $output['sinch'] = array(
                'service_plan_id' => ( isset( $input['sinch']['service_plan_id'] ) ? sanitize_text_field( $input['sinch']['service_plan_id'] ) : $output['sinch']['service_plan_id'] ),
                'api_token'       => ( isset( $input['sinch']['api_token'] ) && '' !== $input['sinch']['api_token'] ? sanitize_text_field( $input['sinch']['api_token'] ) : $output['sinch']['api_token'] ),
                'from'            => ( isset( $input['sinch']['from'] ) ? sanitize_text_field( $input['sinch']['from'] ) : $output['sinch']['from'] ),
            );
            $output['plivo'] = array(
                'auth_id'    => ( isset( $input['plivo']['auth_id'] ) ? sanitize_text_field( $input['plivo']['auth_id'] ) : $output['plivo']['auth_id'] ),
                'auth_token' => ( isset( $input['plivo']['auth_token'] ) && '' !== $input['plivo']['auth_token'] ? sanitize_text_field( $input['plivo']['auth_token'] ) : $output['plivo']['auth_token'] ),
                'from'       => ( isset( $input['plivo']['from'] ) ? sanitize_text_field( $input['plivo']['from'] ) : $output['plivo']['from'] ),
            );
            $output['sequences'] = array(
                'active_sequence_id' => ( isset( $input['sequences']['active_sequence_id'] ) ? absint( $input['sequences']['active_sequence_id'] ) : $output['sequences']['active_sequence_id'] ),
            );
            $output['popup'] = array(
                'enable_popup'             => ( !empty( $input['popup']['enable_popup'] ) ? 'yes' : 'no' ),
                'enable_add_to_cart_popup' => ( !empty( $input['popup']['enable_add_to_cart_popup'] ) ? 'yes' : 'no' ),
                'headline'                 => ( isset( $input['popup']['headline'] ) ? sanitize_text_field( $input['popup']['headline'] ) : $output['popup']['headline'] ),
                'subtext'                  => ( isset( $input['popup']['subtext'] ) ? sanitize_textarea_field( $input['popup']['subtext'] ) : $output['popup']['subtext'] ),
                'button_text'              => ( isset( $input['popup']['button_text'] ) ? sanitize_text_field( $input['popup']['button_text'] ) : $output['popup']['button_text'] ),
                'delay_seconds'            => max( 1, absint( ( isset( $input['popup']['delay_seconds'] ) ? $input['popup']['delay_seconds'] : $output['popup']['delay_seconds'] ) ) ),
                'show_gdpr'                => ( !empty( $input['popup']['show_gdpr'] ) ? 'yes' : 'no' ),
                'gdpr_text'                => ( isset( $input['popup']['gdpr_text'] ) ? sanitize_textarea_field( $input['popup']['gdpr_text'] ) : $output['popup']['gdpr_text'] ),
                'card_start'               => ( isset( $input['popup']['card_start'] ) && sanitize_hex_color( $input['popup']['card_start'] ) ? sanitize_hex_color( $input['popup']['card_start'] ) : $output['popup']['card_start'] ),
                'card_end'                 => ( isset( $input['popup']['card_end'] ) && sanitize_hex_color( $input['popup']['card_end'] ) ? sanitize_hex_color( $input['popup']['card_end'] ) : $output['popup']['card_end'] ),
                'button_start'             => ( isset( $input['popup']['button_start'] ) && sanitize_hex_color( $input['popup']['button_start'] ) ? sanitize_hex_color( $input['popup']['button_start'] ) : $output['popup']['button_start'] ),
                'button_end'               => ( isset( $input['popup']['button_end'] ) && sanitize_hex_color( $input['popup']['button_end'] ) ? sanitize_hex_color( $input['popup']['button_end'] ) : $output['popup']['button_end'] ),
            );
        }
        return $output;
    }

    public function render_dashboard_page() {
        if ( !current_user_can( $this->get_admin_capability() ) ) {
            return;
        }
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only filter state.
        $period = ( isset( $_GET['period'] ) ? sanitize_key( wp_unslash( $_GET['period'] ) ) : 'last_7_days' );
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only filter state.
        $period_date = ( isset( $_GET['period_date'] ) ? $this->sanitize_dashboard_filter_date( wp_unslash( $_GET['period_date'] ) ) : '' );
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only filter state.
        $period_date_to = ( isset( $_GET['period_date_to'] ) ? $this->sanitize_dashboard_filter_date( wp_unslash( $_GET['period_date_to'] ) ) : '' );
        $is_pro = false;
        $pro_dashboard = null;
        $stats = $this->get_dashboard_stats();
        $chart_data = $this->get_dashboard_chart_data( $period, $period_date, $period_date_to );
        $channel_breakdown = array();
        $top_products = array();
        if ( cartninja_is_premium_build() ) {
            $is_pro = cartninja_is_premium_unlocked() && class_exists( 'CARTNINJA_Revenue_Dashboard' );
            $pro_dashboard = ( $is_pro ? new CARTNINJA_Revenue_Dashboard() : null );
            $stats = ( $is_pro ? $pro_dashboard->get_stats( $period, $period_date, $period_date_to ) : $stats );
            $channel_breakdown = ( $is_pro ? $pro_dashboard->get_channel_breakdown() : $channel_breakdown );
            $top_products = ( $is_pro ? $pro_dashboard->get_top_abandoned_products( 5 ) : $top_products );
        }
        wp_add_inline_script( 'cartninja-admin', 'window.cartninjaDashboardChartData=' . wp_json_encode( $chart_data ) . ';', 'before' );
        include CARTNINJA_PLUGIN_DIR . 'admin/partials/dashboard.php';
    }

    public function render_carts_page() {
        if ( !current_user_can( $this->get_admin_capability() ) ) {
            return;
        }
        $this->maybe_load_list_table();
        $list_table = new CARTNINJA_Carts_List_Table();
        $list_table->prepare_items();
        include CARTNINJA_PLUGIN_DIR . 'admin/partials/carts.php';
    }

    public function render_settings_page() {
        if ( !current_user_can( $this->get_admin_capability() ) ) {
            return;
        }
        $email_template_notice = $this->handle_email_template_actions();
        $settings = get_option( 'cartninja_settings', array() );
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only tab state.
        $active_tab = ( isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : 'general' );
        if ( !cartninja_is_premium_build() && !in_array( $active_tab, array('general', 'twilio', 'message'), true ) ) {
            $active_tab = 'general';
        }
        $masked_auth = $this->mask_secret( ( isset( $settings['twilio']['auth_token'] ) ? (string) $settings['twilio']['auth_token'] : '' ) );
        $masked_vonage_secret = $this->mask_secret( ( isset( $settings['vonage']['api_secret'] ) ? (string) $settings['vonage']['api_secret'] : '' ) );
        $masked_sinch_token = $this->mask_secret( ( isset( $settings['sinch']['api_token'] ) ? (string) $settings['sinch']['api_token'] : '' ) );
        $masked_plivo_token = $this->mask_secret( ( isset( $settings['plivo']['auth_token'] ) ? (string) $settings['plivo']['auth_token'] : '' ) );
        include CARTNINJA_PLUGIN_DIR . 'admin/partials/settings.php';
    }

    public function render_sequences_page() {
        if ( !current_user_can( $this->get_admin_capability() ) ) {
            return;
        }
        $sequences = array();
        $current_sequence = array(
            'id'        => 0,
            'name'      => '',
            'is_active' => 0,
            'steps'     => array(),
        );
        if ( cartninja_is_premium_build() ) {
            $builder = ( class_exists( 'CARTNINJA_Sequence_Builder' ) ? new CARTNINJA_Sequence_Builder() : null );
            if ( $builder ) {
                $builder->ensure_default_sequences();
                $sequences = $builder->get_all_sequences();
                // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only edit state.
                $edit_sequence_id = ( isset( $_GET['sequence_id'] ) ? absint( wp_unslash( $_GET['sequence_id'] ) ) : 0 );
                if ( $edit_sequence_id ) {
                    $current_sequence = $builder->get_sequence( $edit_sequence_id );
                } elseif ( !empty( $sequences ) ) {
                    $active_sequence = wp_list_filter( $sequences, array(
                        'is_active' => 1,
                    ) );
                    $selected = ( !empty( $active_sequence ) ? reset( $active_sequence ) : reset( $sequences ) );
                    $current_sequence = $builder->get_sequence( (int) $selected['id'] );
                }
            }
            include CARTNINJA_PLUGIN_DIR . 'pro/admin/partials/sequences.php';
            return;
        }
        wp_die( esc_html__( 'Permission denied.', 'cartninja-recovery-for-woocommerce' ) );
    }

    private function get_dashboard_stats() {
        global $wpdb;
        $table = $wpdb->prefix . 'cartninja_carts';
        $total_abandoned = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$table} WHERE tracking_status <> 'suppressed' AND status IN ('abandoned', 'recovering', 'recovered', 'lost')" );
        $total_recovered = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$table} WHERE status = %s", 'recovered' ) );
        $revenue = (float) $wpdb->get_var( $wpdb->prepare( "SELECT COALESCE(SUM(cart_total),0) FROM {$table} WHERE status = %s", 'recovered' ) );
        $total_suppressed = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$table} WHERE tracking_status = %s", 'suppressed' ) );
        $rate = ( $total_abandoned > 0 ? round( $total_recovered / $total_abandoned * 100, 2 ) : 0 );
        return array(
            'total_abandoned'   => $total_abandoned,
            'total_recovered'   => $total_recovered,
            'recovery_rate'     => $rate,
            'revenue_recovered' => $revenue,
            'total_suppressed'  => $total_suppressed,
        );
    }

    private function get_dashboard_chart_data( string $period, string $period_date = '', string $period_date_to = '' ) : array {
        global $wpdb;
        $table = $wpdb->prefix . 'cartninja_carts';
        $abandoned_date_sql = $this->get_dashboard_period_sql(
            $period,
            'COALESCE(abandoned_at, cart_updated_at, created_at)',
            $period_date,
            $period_date_to
        );
        $created_date_sql = $this->get_dashboard_period_sql(
            $period,
            'created_at',
            $period_date,
            $period_date_to
        );
        $rows = $wpdb->get_results( "SELECT DATE(day_bucket) AS day,\n\t\t\t\tSUM(abandoned) AS abandoned,\n\t\t\t\tSUM(recovered) AS recovered\n\t\t\tFROM (\n\t\t\t\tSELECT COALESCE(abandoned_at, cart_updated_at, created_at) AS day_bucket,\n\t\t\t\t\t1 AS abandoned,\n\t\t\t\t\t0 AS recovered\n\t\t\t\tFROM {$table}\n\t\t\t\tWHERE status IN ('abandoned','recovering','recovered','lost')\n\t\t\t\t\tAND tracking_status <> 'suppressed'\n\t\t\t\t\t{$abandoned_date_sql}\n\n\t\t\t\tUNION ALL\n\n\t\t\t\tSELECT created_at AS day_bucket,\n\t\t\t\t\t0 AS abandoned,\n\t\t\t\t\t1 AS recovered\n\t\t\t\tFROM {$table}\n\t\t\t\tWHERE status = 'recovered'\n\t\t\t\t\t{$created_date_sql}\n\t\t\t) chart_rows\n\t\t\tWHERE day_bucket IS NOT NULL\n\t\t\tGROUP BY DATE(day_bucket)\n\t\t\tORDER BY DATE(day_bucket) ASC", ARRAY_A );
        return ( is_array( $rows ) ? $rows : array() );
    }

    private function get_dashboard_period_sql(
        string $period,
        string $column,
        string $period_date = '',
        string $period_date_to = ''
    ) : string {
        global $wpdb;
        $bounds = $this->get_dashboard_period_bounds( $period, $period_date, $period_date_to );
        if ( !empty( $bounds['exclude'] ) ) {
            return $wpdb->prepare( " AND ( {$column} < %s OR {$column} >= %s )", $bounds['start'], $bounds['end'] );
        }
        if ( !empty( $bounds['start'] ) && !empty( $bounds['end'] ) ) {
            return $wpdb->prepare( " AND {$column} >= %s AND {$column} < %s", $bounds['start'], $bounds['end'] );
        }
        if ( !empty( $bounds['start'] ) ) {
            return $wpdb->prepare( " AND {$column} >= %s", $bounds['start'] );
        }
        if ( !empty( $bounds['end'] ) ) {
            return $wpdb->prepare( " AND {$column} < %s", $bounds['end'] );
        }
        return '';
    }

    private function get_dashboard_period_bounds( string $period, string $period_date = '', string $period_date_to = '' ) : array {
        $today = current_time( 'Y-m-d' );
        switch ( $period ) {
            case 'equals':
                return $this->get_dashboard_day_bounds( ( $period_date ? $period_date : $today ) );
            case 'not_on':
                $bounds = $this->get_dashboard_day_bounds( ( $period_date ? $period_date : $today ) );
                $bounds['exclude'] = true;
                return $bounds;
            case 'after':
                return array(
                    'start' => $this->get_dashboard_day_bounds( ( $period_date ? $period_date : $today ) )['end'],
                    'end'   => '',
                );
            case 'before':
                return array(
                    'start' => '',
                    'end'   => $this->get_dashboard_day_bounds( ( $period_date ? $period_date : $today ) )['start'],
                );
            case 'last_7_days':
                return array(
                    'start' => date_i18n( 'Y-m-d 00:00:00', strtotime( '-6 days', current_time( 'timestamp' ) ) ),
                    'end'   => date_i18n( 'Y-m-d 00:00:00', strtotime( '+1 day', current_time( 'timestamp' ) ) ),
                );
            case 'last_month':
                return array(
                    'start' => date_i18n( 'Y-m-01 00:00:00', strtotime( 'first day of previous month', current_time( 'timestamp' ) ) ),
                    'end'   => date_i18n( 'Y-m-01 00:00:00', current_time( 'timestamp' ) ),
                );
            case 'this_month':
                return array(
                    'start' => date_i18n( 'Y-m-01 00:00:00', current_time( 'timestamp' ) ),
                    'end'   => date_i18n( 'Y-m-01 00:00:00', strtotime( 'first day of next month', current_time( 'timestamp' ) ) ),
                );
            case 'last_year':
                $year = (int) date_i18n( 'Y', current_time( 'timestamp' ) ) - 1;
                return array(
                    'start' => $year . '-01-01 00:00:00',
                    'end'   => $year + 1 . '-01-01 00:00:00',
                );
            case 'this_year':
                $year = (int) date_i18n( 'Y', current_time( 'timestamp' ) );
                return array(
                    'start' => $year . '-01-01 00:00:00',
                    'end'   => $year + 1 . '-01-01 00:00:00',
                );
            case 'between':
                $from = ( $period_date ? $period_date : $today );
                $to = ( $period_date_to ? $period_date_to : $from );
                if ( strtotime( $to ) < strtotime( $from ) ) {
                    $to = $from;
                }
                return array(
                    'start' => $this->get_dashboard_day_bounds( $from )['start'],
                    'end'   => $this->get_dashboard_day_bounds( $to )['end'],
                );
            case 'last_30_days':
                return array(
                    'start' => date_i18n( 'Y-m-d 00:00:00', strtotime( '-29 days', current_time( 'timestamp' ) ) ),
                    'end'   => date_i18n( 'Y-m-d 00:00:00', strtotime( '+1 day', current_time( 'timestamp' ) ) ),
                );
            default:
                return array(
                    'start' => date_i18n( 'Y-m-d 00:00:00', strtotime( '-6 days', current_time( 'timestamp' ) ) ),
                    'end'   => date_i18n( 'Y-m-d 00:00:00', strtotime( '+1 day', current_time( 'timestamp' ) ) ),
                );
        }
    }

    private function get_dashboard_day_bounds( string $date ) : array {
        return array(
            'start' => $date . ' 00:00:00',
            'end'   => date_i18n( 'Y-m-d 00:00:00', strtotime( $date . ' +1 day' ) ),
        );
    }

    private function sanitize_dashboard_filter_date( $date ) : string {
        $date = sanitize_text_field( (string) $date );
        return ( preg_match( '/^\\d{4}-\\d{2}-\\d{2}$/', $date ) ? $date : '' );
    }

    private function handle_email_template_actions() : string {
        if ( empty( $_GET['cartninja_copy_email_template'] ) ) {
            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only action presence check.
            return '';
        }
        if ( !current_user_can( 'edit_themes' ) ) {
            return __( 'You do not have permission to copy email templates.', 'cartninja-recovery-for-woocommerce' );
        }
        if ( empty( $_GET['_cartninja_email_template_nonce'] ) || !wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_cartninja_email_template_nonce'] ) ), 'cartninja_email_template_action' ) ) {
            return __( 'Template action failed. Please refresh the page and try again.', 'cartninja-recovery-for-woocommerce' );
        }
        $source = CARTNINJA_PLUGIN_DIR . 'templates/emails/cartninja-abandoned-cart.php';
        $target = get_stylesheet_directory() . '/cartninja/emails/cartninja-abandoned-cart.php';
        $target_dir = dirname( $target );
        if ( !file_exists( $source ) ) {
            return __( 'Template file was not found.', 'cartninja-recovery-for-woocommerce' );
        }
        if ( !wp_mkdir_p( $target_dir ) || !is_writable( $target_dir ) ) {
            return __( 'Theme template directory is not writable.', 'cartninja-recovery-for-woocommerce' );
        }
        if ( file_exists( $target ) ) {
            return __( 'Template already exists in your theme.', 'cartninja-recovery-for-woocommerce' );
        }
        if ( !copy( $source, $target ) ) {
            // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_copy
            return __( 'Unable to copy template to your theme.', 'cartninja-recovery-for-woocommerce' );
        }
        return __( 'Template copied to your theme.', 'cartninja-recovery-for-woocommerce' );
    }

    private function get_cart_for_view( $cart_id = 0 ) {
        global $wpdb;
        if ( !$cart_id ) {
            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only cart inspection state.
            $cart_id = ( isset( $_GET['view_cart'] ) ? absint( $_GET['view_cart'] ) : 0 );
        }
        if ( !$cart_id ) {
            return null;
        }
        return $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}cartninja_carts WHERE id = %d", $cart_id ), ARRAY_A );
    }

    private function mask_secret( string $token ) : string {
        if ( strlen( $token ) <= 4 ) {
            return str_repeat( '*', strlen( $token ) );
        }
        return str_repeat( '*', strlen( $token ) - 4 ) . substr( $token, -4 );
    }

    private function sanitize_ip_list( $value ) {
        $ips = preg_split( '/[\\r\\n,]+/', (string) wp_unslash( $value ) );
        $result = array();
        foreach ( $ips as $ip ) {
            $ip = trim( $ip );
            if ( filter_var( $ip, FILTER_VALIDATE_IP ) ) {
                $result[] = $ip;
            }
        }
        return implode( "\n", array_values( array_unique( $result ) ) );
    }

    private function sanitize_country_code_list( $value ) {
        $codes = preg_split( '/[\\r\\n,]+/', (string) wp_unslash( $value ) );
        $result = array();
        foreach ( $codes as $code ) {
            $code = strtoupper( trim( sanitize_text_field( $code ) ) );
            if ( preg_match( '/^[A-Z]{2}$/', $code ) ) {
                $result[] = $code;
            }
        }
        return implode( "\n", array_values( array_unique( $result ) ) );
    }

    private function sanitize_whatsapp_body_variables( $variables ) {
        $allowed = array(
            'customer_name',
            'cart_total',
            'site_name',
            'cart_url',
            'coupon_code',
            'customer_email',
            'customer_phone',
            'none'
        );
        $result = array();
        foreach ( (array) $variables as $variable ) {
            $variable = sanitize_key( $variable );
            if ( in_array( $variable, $allowed, true ) ) {
                $result[] = $variable;
            }
        }
        $result = array_slice( $result, 0, 8 );
        return ( !empty( $result ) ? $result : array(
            'customer_name',
            'cart_total',
            'site_name',
            'cart_url'
        ) );
    }

    private function get_admin_capability() {
        return ( class_exists( 'WooCommerce' ) ? 'manage_woocommerce' : 'manage_options' );
    }

    private function maybe_load_list_table() {
        if ( !class_exists( 'WP_List_Table' ) ) {
            require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
        }
        if ( !class_exists( 'CARTNINJA_Carts_List_Table' ) ) {
            cartninja_define_carts_list_table_class();
        }
    }

    private function get_cart_details_markup( $cart ) {
        ob_start();
        ?>
		<div class="cartninja-cart-details cartninja-cart-details--drawer">
			<div class="cartninja-cart-details__head">
				<div>
					<span class="cartninja-page-hero__eyebrow"><?php 
        esc_html_e( 'Cart Inspector', 'cartninja-recovery-for-woocommerce' );
        ?></span>
					<h2><?php 
        esc_html_e( 'Cart Details', 'cartninja-recovery-for-woocommerce' );
        ?></h2>
				</div>
				<span class="cartninja-status-pill cartninja-status-pill--<?php 
        echo esc_attr( strtolower( $cart['status'] ) );
        ?>"><?php 
        echo esc_html( ucfirst( $cart['status'] ) );
        ?></span>
			</div>
			<div class="cartninja-detail-grid">
				<div class="cartninja-detail-card">
					<span class="dashicons dashicons-email-alt" aria-hidden="true"></span>
					<div>
						<label><?php 
        esc_html_e( 'Email', 'cartninja-recovery-for-woocommerce' );
        ?></label>
						<strong><?php 
        echo esc_html( ( !empty( $cart['email'] ) ? $cart['email'] : __( 'Guest', 'cartninja-recovery-for-woocommerce' ) ) );
        ?></strong>
					</div>
				</div>
				<div class="cartninja-detail-card">
					<span class="dashicons dashicons-phone" aria-hidden="true"></span>
					<div>
						<label><?php 
        esc_html_e( 'Phone', 'cartninja-recovery-for-woocommerce' );
        ?></label>
						<strong><?php 
        echo esc_html( ( !empty( $cart['phone'] ) ? $cart['phone'] : __( 'Unavailable', 'cartninja-recovery-for-woocommerce' ) ) );
        ?></strong>
					</div>
				</div>
				<div class="cartninja-detail-card">
					<span class="dashicons dashicons-tickets-alt" aria-hidden="true"></span>
					<div>
						<label><?php 
        esc_html_e( 'Coupon Code', 'cartninja-recovery-for-woocommerce' );
        ?></label>
						<strong><?php 
        echo esc_html( ( !empty( $cart['coupon_code'] ) ? $cart['coupon_code'] : __( 'None', 'cartninja-recovery-for-woocommerce' ) ) );
        ?></strong>
					</div>
				</div>
				<div class="cartninja-detail-card">
					<span class="dashicons dashicons-location" aria-hidden="true"></span>
					<div>
						<label><?php 
        esc_html_e( 'Customer IP', 'cartninja-recovery-for-woocommerce' );
        ?></label>
						<strong><?php 
        echo esc_html( ( !empty( $cart['customer_ip'] ) ? $cart['customer_ip'] : __( 'Unavailable', 'cartninja-recovery-for-woocommerce' ) ) );
        ?></strong>
					</div>
				</div>
				<div class="cartninja-detail-card">
					<span class="dashicons dashicons-admin-site-alt3" aria-hidden="true"></span>
					<div>
						<label><?php 
        esc_html_e( 'IP Country', 'cartninja-recovery-for-woocommerce' );
        ?></label>
						<strong><?php 
        echo esc_html( $this->format_ip_country( $cart ) );
        ?></strong>
					</div>
				</div>
				<div class="cartninja-detail-card">
					<span class="dashicons dashicons-shield" aria-hidden="true"></span>
					<div>
						<label><?php 
        esc_html_e( 'Protection Status', 'cartninja-recovery-for-woocommerce' );
        ?></label>
						<strong><?php 
        echo esc_html( ( isset( $cart['tracking_status'] ) && 'suppressed' === $cart['tracking_status'] ? __( 'Suppressed', 'cartninja-recovery-for-woocommerce' ) : __( 'Tracked', 'cartninja-recovery-for-woocommerce' ) ) );
        ?></strong>
					</div>
				</div>
				<div class="cartninja-detail-card">
					<span class="dashicons dashicons-admin-links" aria-hidden="true"></span>
					<div>
						<label><?php 
        esc_html_e( 'Recovery Token', 'cartninja-recovery-for-woocommerce' );
        ?></label>
						<strong><?php 
        echo esc_html( $cart['recovery_token'] );
        ?></strong>
					</div>
				</div>
			</div>
			<?php 
        $this->render_cart_items_table( $cart );
        ?>
		</div>
		<?php 
        return ob_get_clean();
    }

    private function format_ip_country( $cart ) {
        $name = ( !empty( $cart['customer_country_name'] ) ? sanitize_text_field( $cart['customer_country_name'] ) : '' );
        $code = ( !empty( $cart['customer_country_code'] ) ? strtoupper( sanitize_text_field( $cart['customer_country_code'] ) ) : '' );
        if ( $name && $code && $name !== $code ) {
            return sprintf( '%1$s (%2$s)', $name, $code );
        }
        if ( $name ) {
            return $name;
        }
        if ( $code ) {
            return $code;
        }
        return '-';
    }

    private function render_cart_items_table( $cart ) {
        $items = json_decode( ( isset( $cart['cart_contents'] ) ? (string) $cart['cart_contents'] : '' ), true );
        if ( !is_array( $items ) ) {
            $items = array();
        }
        ?>
		<div class="cartninja-cart-items-panel">
			<div class="cartninja-cart-items-panel__head">
				<h3><?php 
        esc_html_e( 'Cart Items', 'cartninja-recovery-for-woocommerce' );
        ?></h3>
				<span><?php 
        echo esc_html( sprintf( _n(
            '%d item',
            '%d items',
            count( $items ),
            'cartninja-recovery-for-woocommerce'
        ), count( $items ) ) );
        ?></span>
			</div>
			<?php 
        if ( empty( $items ) ) {
            ?>
				<p class="cartninja-empty-state"><?php 
            esc_html_e( 'No cart items were saved for this record.', 'cartninja-recovery-for-woocommerce' );
            ?></p>
			<?php 
        } else {
            ?>
				<div class="cartninja-cart-items-table-wrap">
					<table class="widefat fixed striped cartninja-cart-items-table">
						<thead>
							<tr>
								<th><?php 
            esc_html_e( 'Product', 'cartninja-recovery-for-woocommerce' );
            ?></th>
								<th><?php 
            esc_html_e( 'SKU', 'cartninja-recovery-for-woocommerce' );
            ?></th>
								<th><?php 
            esc_html_e( 'Qty', 'cartninja-recovery-for-woocommerce' );
            ?></th>
								<th><?php 
            esc_html_e( 'Unit Price', 'cartninja-recovery-for-woocommerce' );
            ?></th>
								<th><?php 
            esc_html_e( 'Line Total', 'cartninja-recovery-for-woocommerce' );
            ?></th>
							</tr>
						</thead>
						<tbody>
							<?php 
            foreach ( $items as $item ) {
                ?>
								<?php 
                $display_item = $this->get_cart_item_display_data( $item, ( isset( $cart['currency'] ) ? $cart['currency'] : '' ) );
                ?>
								<tr>
									<td>
										<div class="cartninja-cart-product">
											<div class="cartninja-cart-product__image"><?php 
                echo wp_kses_post( $display_item['image'] );
                ?></div>
											<div class="cartninja-cart-product__meta">
												<strong><?php 
                echo esc_html( $display_item['name'] );
                ?></strong>
												<?php 
                if ( !empty( $display_item['variation'] ) ) {
                    ?>
													<small><?php 
                    echo esc_html( $display_item['variation'] );
                    ?></small>
												<?php 
                }
                ?>
												<?php 
                if ( !empty( $display_item['deleted'] ) ) {
                    ?>
													<small><?php 
                    esc_html_e( 'Product unavailable', 'cartninja-recovery-for-woocommerce' );
                    ?></small>
												<?php 
                }
                ?>
											</div>
										</div>
									</td>
									<td><?php 
                echo esc_html( $display_item['sku'] );
                ?></td>
									<td><?php 
                echo esc_html( $display_item['quantity'] );
                ?></td>
									<td><?php 
                echo wp_kses_post( $display_item['unit_price'] );
                ?></td>
									<td><?php 
                echo wp_kses_post( $display_item['line_total'] );
                ?></td>
								</tr>
							<?php 
            }
            ?>
						</tbody>
					</table>
				</div>
			<?php 
        }
        ?>
		</div>
		<?php 
    }

    private function get_cart_item_display_data( $item, $currency ) {
        $item = ( is_array( $item ) ? $item : array() );
        $product_id = ( isset( $item['product_id'] ) ? absint( $item['product_id'] ) : 0 );
        $variation_id = ( isset( $item['variation_id'] ) ? absint( $item['variation_id'] ) : 0 );
        $quantity = ( isset( $item['quantity'] ) ? max( 1, absint( $item['quantity'] ) ) : 1 );
        $line_total = ( isset( $item['line_total'] ) ? (float) $item['line_total'] : 0.0 );
        $product = ( function_exists( 'wc_get_product' ) ? wc_get_product( ( $variation_id ? $variation_id : $product_id ) ) : false );
        $name = ( !empty( $item['product_name'] ) ? sanitize_text_field( $item['product_name'] ) : __( 'Unknown product', 'cartninja-recovery-for-woocommerce' ) );
        $variation = $this->format_cart_item_variation( ( isset( $item['variation'] ) ? $item['variation'] : array() ) );
        if ( $product ) {
            $name = $product->get_name();
            $sku = $product->get_sku();
            $image = $product->get_image( array(52, 52), array(
                'class' => 'cartninja-cart-product__thumb',
            ) );
        } else {
            $sku = '-';
            $image = ( function_exists( 'wc_placeholder_img' ) ? wc_placeholder_img( array(52, 52) ) : '<span class="cartninja-cart-product__placeholder" aria-hidden="true"></span>' );
        }
        return array(
            'name'       => $name,
            'sku'        => ( $sku ? $sku : '-' ),
            'quantity'   => $quantity,
            'variation'  => $variation,
            'image'      => $image,
            'unit_price' => $this->format_cart_item_money( ( $quantity ? $line_total / $quantity : 0 ), $currency ),
            'line_total' => $this->format_cart_item_money( $line_total, $currency ),
            'deleted'    => !$product,
        );
    }

    private function format_cart_item_variation( $variation ) {
        if ( !is_array( $variation ) || empty( $variation ) ) {
            return '';
        }
        $parts = array();
        foreach ( $variation as $attribute => $value ) {
            $value = sanitize_text_field( (string) $value );
            if ( '' === $value ) {
                continue;
            }
            $label = ( function_exists( 'wc_attribute_label' ) ? wc_attribute_label( str_replace( 'attribute_', '', (string) $attribute ) ) : $attribute );
            $parts[] = sprintf( '%s: %s', $label, $value );
        }
        return implode( ', ', $parts );
    }

    private function format_cart_item_money( $amount, $currency ) {
        if ( function_exists( 'wc_price' ) ) {
            $args = array();
            if ( $currency ) {
                $args['currency'] = sanitize_text_field( $currency );
            }
            return wc_price( (float) $amount, $args );
        }
        return esc_html( number_format_i18n( (float) $amount, 2 ) );
    }

}

function cartninja_define_carts_list_table_class() {
    if ( class_exists( 'CARTNINJA_Carts_List_Table', false ) ) {
        return;
    }
    class CARTNINJA_Carts_List_Table extends WP_List_Table {
        private $notification_map = array();

        public function __construct() {
            parent::__construct( array(
                'singular' => 'cartninja-cart',
                'plural'   => 'cartninja-carts',
                'ajax'     => false,
            ) );
        }

        public function get_columns() {
            return array(
                'customer'         => __( 'Customer', 'cartninja-recovery-for-woocommerce' ),
                'phone'            => __( 'Phone', 'cartninja-recovery-for-woocommerce' ),
                'customer_ip'      => __( 'IP', 'cartninja-recovery-for-woocommerce' ),
                'customer_country' => __( 'Country', 'cartninja-recovery-for-woocommerce' ),
                'cart_total'       => __( 'Cart Total', 'cartninja-recovery-for-woocommerce' ),
                'status'           => __( 'Status', 'cartninja-recovery-for-woocommerce' ),
                'reminders'        => __( 'Reminders', 'cartninja-recovery-for-woocommerce' ),
                'abandoned_at'     => __( 'Abandoned At', 'cartninja-recovery-for-woocommerce' ),
                'actions'          => __( 'Actions', 'cartninja-recovery-for-woocommerce' ),
            );
        }

        public function prepare_items() {
            global $wpdb;
            $per_page = 20;
            $current_page = $this->get_pagenum();
            $offset = ($current_page - 1) * $per_page;
            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only filter state.
            $status_filter = ( isset( $_GET['cartninja_status'] ) ? sanitize_key( wp_unslash( $_GET['cartninja_status'] ) ) : '' );
            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only filter state.
            $reminder_filter = ( isset( $_GET['cartninja_reminder'] ) ? sanitize_key( wp_unslash( $_GET['cartninja_reminder'] ) ) : '' );
            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only filter state.
            $customer_filter = ( isset( $_GET['cartninja_customer'] ) ? sanitize_key( wp_unslash( $_GET['cartninja_customer'] ) ) : '' );
            $columns = $this->get_columns();
            if ( !in_array( $status_filter, array(
                'lost',
                'abandoned',
                'recovering',
                'recovered',
                'suppressed'
            ), true ) ) {
                $status_filter = '';
            }
            if ( !in_array( $reminder_filter, array(
                'sms_sent',
                'email_sent',
                'no_reminder',
                'failed'
            ), true ) ) {
                $reminder_filter = '';
            }
            if ( !in_array( $customer_filter, array('with_guests', 'guests_only'), true ) ) {
                $customer_filter = '';
            }
            if ( 'suppressed' === $status_filter && '' === $customer_filter ) {
                $customer_filter = 'with_guests';
            }
            $query_args = array(
                $status_filter,
                $status_filter,
                $status_filter,
                $status_filter,
                $status_filter,
                $reminder_filter,
                $reminder_filter,
                $reminder_filter,
                $reminder_filter,
                $reminder_filter,
                $customer_filter,
                $customer_filter,
                $customer_filter
            );
            $total_items = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}cartninja_carts c\n\t\t\t\t\tWHERE (\n\t\t\t\t\t\t( %s = '' AND c.status <> 'active' AND c.tracking_status <> 'suppressed' )\n\t\t\t\t\t\tOR ( %s = 'suppressed' AND c.tracking_status = 'suppressed' )\n\t\t\t\t\t\tOR ( %s <> '' AND %s <> 'suppressed' AND c.status = %s AND c.tracking_status <> 'suppressed' )\n\t\t\t\t\t)\n\t\t\t\t\tAND (\n\t\t\t\t\t\t%s = ''\n\t\t\t\t\t\tOR ( %s = 'sms_sent' AND EXISTS ( SELECT 1 FROM {$wpdb->prefix}cartninja_notifications n WHERE n.cart_id = c.id AND n.channel = 'sms' AND n.status IN ('sent','clicked') ) )\n\t\t\t\t\t\tOR ( %s = 'email_sent' AND EXISTS ( SELECT 1 FROM {$wpdb->prefix}cartninja_notifications n WHERE n.cart_id = c.id AND n.channel = 'email' AND n.status IN ('sent','clicked') ) )\n\t\t\t\t\t\tOR ( %s = 'no_reminder' AND NOT EXISTS ( SELECT 1 FROM {$wpdb->prefix}cartninja_notifications n WHERE n.cart_id = c.id AND n.status IN ('sent','clicked') ) )\n\t\t\t\t\t\tOR ( %s = 'failed' AND EXISTS ( SELECT 1 FROM {$wpdb->prefix}cartninja_notifications n WHERE n.cart_id = c.id AND n.status = 'failed' ) )\n\t\t\t\t\t)\n\t\t\t\t\tAND (\n\t\t\t\t\t\t%s = 'with_guests'\n\t\t\t\t\t\tOR ( %s = 'guests_only' AND c.user_id IS NULL )\n\t\t\t\t\t\tOR ( %s = '' AND ( c.user_id IS NOT NULL OR c.email <> '' OR c.phone <> '' ) )\n\t\t\t\t\t)", $query_args ) );
            $this->_column_headers = array($columns, array(), array());
            $query_args[] = $per_page;
            $query_args[] = $offset;
            $this->items = $wpdb->get_results( $wpdb->prepare( "SELECT c.* FROM {$wpdb->prefix}cartninja_carts c\n\t\t\t\t\tWHERE (\n\t\t\t\t\t\t( %s = '' AND c.status <> 'active' AND c.tracking_status <> 'suppressed' )\n\t\t\t\t\t\tOR ( %s = 'suppressed' AND c.tracking_status = 'suppressed' )\n\t\t\t\t\t\tOR ( %s <> '' AND %s <> 'suppressed' AND c.status = %s AND c.tracking_status <> 'suppressed' )\n\t\t\t\t\t)\n\t\t\t\t\tAND (\n\t\t\t\t\t\t%s = ''\n\t\t\t\t\t\tOR ( %s = 'sms_sent' AND EXISTS ( SELECT 1 FROM {$wpdb->prefix}cartninja_notifications n WHERE n.cart_id = c.id AND n.channel = 'sms' AND n.status IN ('sent','clicked') ) )\n\t\t\t\t\t\tOR ( %s = 'email_sent' AND EXISTS ( SELECT 1 FROM {$wpdb->prefix}cartninja_notifications n WHERE n.cart_id = c.id AND n.channel = 'email' AND n.status IN ('sent','clicked') ) )\n\t\t\t\t\t\tOR ( %s = 'no_reminder' AND NOT EXISTS ( SELECT 1 FROM {$wpdb->prefix}cartninja_notifications n WHERE n.cart_id = c.id AND n.status IN ('sent','clicked') ) )\n\t\t\t\t\t\tOR ( %s = 'failed' AND EXISTS ( SELECT 1 FROM {$wpdb->prefix}cartninja_notifications n WHERE n.cart_id = c.id AND n.status = 'failed' ) )\n\t\t\t\t\t)\n\t\t\t\t\tAND (\n\t\t\t\t\t\t%s = 'with_guests'\n\t\t\t\t\t\tOR ( %s = 'guests_only' AND c.user_id IS NULL )\n\t\t\t\t\t\tOR ( %s = '' AND ( c.user_id IS NOT NULL OR c.email <> '' OR c.phone <> '' ) )\n\t\t\t\t\t)\n\t\t\t\t\tORDER BY c.created_at DESC LIMIT %d OFFSET %d", $query_args ), ARRAY_A );
            $this->prime_notification_map();
            $this->set_pagination_args( array(
                'total_items' => $total_items,
                'per_page'    => $per_page,
                'total_pages' => (int) ceil( $total_items / $per_page ),
            ) );
        }

        protected function extra_tablenav( $which ) {
            if ( 'top' !== $which ) {
                return;
            }
            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only filter state.
            $status_filter = ( isset( $_GET['cartninja_status'] ) ? sanitize_key( wp_unslash( $_GET['cartninja_status'] ) ) : '' );
            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only filter state.
            $reminder_filter = ( isset( $_GET['cartninja_reminder'] ) ? sanitize_key( wp_unslash( $_GET['cartninja_reminder'] ) ) : '' );
            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only filter state.
            $customer_filter = ( isset( $_GET['cartninja_customer'] ) ? sanitize_key( wp_unslash( $_GET['cartninja_customer'] ) ) : '' );
            if ( 'suppressed' === $status_filter && '' === $customer_filter ) {
                $customer_filter = 'with_guests';
            }
            ?>
			<div class="alignleft actions">
				<label class="screen-reader-text" for="filter-by-cartninja-status"><?php 
            esc_html_e( 'Filter by status', 'cartninja-recovery-for-woocommerce' );
            ?></label>
				<select name="cartninja_status" id="filter-by-cartninja-status">
					<option value=""><?php 
            esc_html_e( 'All visible statuses', 'cartninja-recovery-for-woocommerce' );
            ?></option>
					<option value="lost" <?php 
            selected( $status_filter, 'lost' );
            ?>><?php 
            esc_html_e( 'Lost', 'cartninja-recovery-for-woocommerce' );
            ?></option>
					<option value="abandoned" <?php 
            selected( $status_filter, 'abandoned' );
            ?>><?php 
            esc_html_e( 'Abandoned', 'cartninja-recovery-for-woocommerce' );
            ?></option>
					<option value="recovering" <?php 
            selected( $status_filter, 'recovering' );
            ?>><?php 
            esc_html_e( 'Recovering', 'cartninja-recovery-for-woocommerce' );
            ?></option>
					<option value="recovered" <?php 
            selected( $status_filter, 'recovered' );
            ?>><?php 
            esc_html_e( 'Recovered', 'cartninja-recovery-for-woocommerce' );
            ?></option>
					<option value="suppressed" <?php 
            selected( $status_filter, 'suppressed' );
            ?>><?php 
            esc_html_e( 'Suppressed', 'cartninja-recovery-for-woocommerce' );
            ?></option>
				</select>
				<label class="screen-reader-text" for="filter-by-cartninja-reminder"><?php 
            esc_html_e( 'Filter by reminder', 'cartninja-recovery-for-woocommerce' );
            ?></label>
				<select name="cartninja_reminder" id="filter-by-cartninja-reminder">
					<option value=""><?php 
            esc_html_e( 'All reminders', 'cartninja-recovery-for-woocommerce' );
            ?></option>
					<option value="sms_sent" <?php 
            selected( $reminder_filter, 'sms_sent' );
            ?>><?php 
            esc_html_e( 'SMS sent', 'cartninja-recovery-for-woocommerce' );
            ?></option>
					<option value="email_sent" <?php 
            selected( $reminder_filter, 'email_sent' );
            ?>><?php 
            esc_html_e( 'Email sent', 'cartninja-recovery-for-woocommerce' );
            ?></option>
					<option value="failed" <?php 
            selected( $reminder_filter, 'failed' );
            ?>><?php 
            esc_html_e( 'Failed reminders', 'cartninja-recovery-for-woocommerce' );
            ?></option>
					<option value="no_reminder" <?php 
            selected( $reminder_filter, 'no_reminder' );
            ?>><?php 
            esc_html_e( 'No reminders sent', 'cartninja-recovery-for-woocommerce' );
            ?></option>
				</select>
				<label class="screen-reader-text" for="filter-by-cartninja-customer"><?php 
            esc_html_e( 'Filter by customer type', 'cartninja-recovery-for-woocommerce' );
            ?></label>
				<select name="cartninja_customer" id="filter-by-cartninja-customer">
					<option value="" <?php 
            selected( $customer_filter, '' );
            ?>><?php 
            esc_html_e( 'Customers with contact info', 'cartninja-recovery-for-woocommerce' );
            ?></option>
					<option value="with_guests" <?php 
            selected( $customer_filter, 'with_guests' );
            ?>><?php 
            esc_html_e( 'All carts including anonymous guests', 'cartninja-recovery-for-woocommerce' );
            ?></option>
					<option value="guests_only" <?php 
            selected( $customer_filter, 'guests_only' );
            ?>><?php 
            esc_html_e( 'Guests only', 'cartninja-recovery-for-woocommerce' );
            ?></option>
				</select>
				<?php 
            submit_button(
                __( 'Filter', 'cartninja-recovery-for-woocommerce' ),
                'button',
                'filter_action',
                false
            );
            ?>
			</div>
			<?php 
        }

        protected function column_default( $item, $column_name ) {
            switch ( $column_name ) {
                case 'customer':
                    return esc_html( ( !empty( $item['email'] ) ? $item['email'] : __( 'Guest', 'cartninja-recovery-for-woocommerce' ) ) );
                case 'phone':
                    return esc_html( $item['phone'] );
                case 'customer_ip':
                    return esc_html( ( !empty( $item['customer_ip'] ) ? $this->mask_ip( $item['customer_ip'] ) : '-' ) );
                case 'customer_country':
                    return esc_html( $this->format_ip_country( $item ) );
                case 'cart_total':
                    return ( function_exists( 'wc_price' ) ? wp_kses_post( wc_price( (float) $item['cart_total'] ) ) : esc_html( number_format_i18n( (float) $item['cart_total'], 2 ) ) );
                case 'status':
                    return esc_html( ( isset( $item['tracking_status'] ) && 'suppressed' === $item['tracking_status'] ? __( 'Suppressed', 'cartninja-recovery-for-woocommerce' ) : $this->get_status_label( $item['status'] ) ) );
                case 'reminders':
                    return $this->render_reminders_column( $item );
                case 'abandoned_at':
                    return esc_html( ( $item['abandoned_at'] ? $item['abandoned_at'] : '-' ) );
                case 'actions':
                    $view_url = add_query_arg( array(
                        'page'      => 'cartninja-carts',
                        'view_cart' => absint( $item['id'] ),
                    ), admin_url( 'admin.php' ) );
                    $delete_url = wp_nonce_url( add_query_arg( array(
                        'action'  => 'cartninja_delete_cart',
                        'cart_id' => absint( $item['id'] ),
                    ), admin_url( 'admin-post.php' ) ), 'cartninja_delete_cart' );
                    $actions = array(sprintf(
                        '<a href="%1$s" class="cartninja-view-cart cartninja-action-link" data-cart-id="%2$d" title="%3$s"><span class="dashicons dashicons-visibility" aria-hidden="true"></span><span>%3$s</span></a>',
                        esc_url( $view_url ),
                        absint( $item['id'] ),
                        esc_html__( 'View', 'cartninja-recovery-for-woocommerce' )
                    ));
                    foreach ( array('sms', 'email') as $channel ) {
                        $record = ( isset( $this->notification_map[absint( $item['id'] )][$channel] ) ? $this->notification_map[absint( $item['id'] )][$channel] : null );
                        if ( $record && 'failed' === $record['status'] ) {
                            $resend_url = wp_nonce_url( add_query_arg( array(
                                'action'  => 'cartninja_resend_notification',
                                'cart_id' => absint( $item['id'] ),
                                'channel' => $channel,
                            ), admin_url( 'admin-post.php' ) ), 'cartninja_resend_notification' );
                            $actions[] = sprintf(
                                '<a href="%1$s" class="cartninja-action-link cartninja-action-link--retry" title="%2$s"><span class="dashicons %3$s" aria-hidden="true"></span><span>%2$s</span></a>',
                                esc_url( $resend_url ),
                                esc_html( ( 'sms' === $channel ? __( 'Retry SMS', 'cartninja-recovery-for-woocommerce' ) : __( 'Retry Email', 'cartninja-recovery-for-woocommerce' ) ) ),
                                esc_attr( ( 'sms' === $channel ? 'dashicons-update' : 'dashicons-email-alt' ) )
                            );
                        }
                    }
                    $actions[] = sprintf(
                        '<a href="%1$s" class="cartninja-action-link cartninja-action-link--delete" onclick="return confirm(\'%2$s\');" title="%3$s"><span class="dashicons dashicons-trash" aria-hidden="true"></span><span>%3$s</span></a>',
                        esc_url( $delete_url ),
                        esc_js( __( 'Delete this cart?', 'cartninja-recovery-for-woocommerce' ) ),
                        esc_html__( 'Delete', 'cartninja-recovery-for-woocommerce' )
                    );
                    return '<div class="cartninja-table-actions">' . implode( '', $actions ) . '</div>';
            }
            return '';
        }

        public function no_items() {
            esc_html_e( 'No carts found.', 'cartninja-recovery-for-woocommerce' );
        }

        private function get_status_label( $status ) {
            $labels = array(
                'active'     => __( 'Active', 'cartninja-recovery-for-woocommerce' ),
                'lost'       => __( 'Lost', 'cartninja-recovery-for-woocommerce' ),
                'abandoned'  => __( 'Abandoned', 'cartninja-recovery-for-woocommerce' ),
                'recovering' => __( 'Recovering', 'cartninja-recovery-for-woocommerce' ),
                'recovered'  => __( 'Recovered', 'cartninja-recovery-for-woocommerce' ),
            );
            return ( isset( $labels[$status] ) ? $labels[$status] : ucfirst( $status ) );
        }

        private function mask_ip( $ip ) {
            $ip = (string) $ip;
            if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ) {
                $parts = explode( '.', $ip );
                if ( 4 === count( $parts ) ) {
                    $parts[3] = 'x';
                    return implode( '.', $parts );
                }
            }
            if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) ) {
                return preg_replace( '/:[0-9a-f]{1,4}$/i', ':xxxx', $ip );
            }
            return $ip;
        }

        private function format_ip_country( $item ) {
            $name = ( !empty( $item['customer_country_name'] ) ? sanitize_text_field( $item['customer_country_name'] ) : '' );
            $code = ( !empty( $item['customer_country_code'] ) ? strtoupper( sanitize_text_field( $item['customer_country_code'] ) ) : '' );
            if ( $name && $code && $name !== $code ) {
                return sprintf( '%1$s (%2$s)', $name, $code );
            }
            if ( $name ) {
                return $name;
            }
            if ( $code ) {
                return $code;
            }
            return '-';
        }

        private function prime_notification_map() {
            global $wpdb;
            $cart_ids = wp_list_pluck( $this->items, 'id' );
            if ( empty( $cart_ids ) ) {
                $this->notification_map = array();
                return;
            }
            $placeholders = implode( ',', array_fill( 0, count( $cart_ids ), '%d' ) );
            $rows = $wpdb->get_results( $wpdb->prepare( "SELECT cart_id, channel, status, sent_at, clicked_at, error_message FROM {$wpdb->prefix}cartninja_notifications WHERE cart_id IN ({$placeholders}) ORDER BY id DESC", $cart_ids ), ARRAY_A );
            $this->notification_map = array();
            foreach ( $rows as $row ) {
                $cart_id = absint( $row['cart_id'] );
                $channel = sanitize_key( $row['channel'] );
                if ( !isset( $this->notification_map[$cart_id] ) ) {
                    $this->notification_map[$cart_id] = array();
                }
                if ( !isset( $this->notification_map[$cart_id][$channel] ) ) {
                    $this->notification_map[$cart_id][$channel] = $row;
                }
            }
        }

        private function render_reminders_column( $item ) {
            $cart_id = absint( $item['id'] );
            $sms_record = ( isset( $this->notification_map[$cart_id]['sms'] ) ? $this->notification_map[$cart_id]['sms'] : null );
            $email_record = ( isset( $this->notification_map[$cart_id]['email'] ) ? $this->notification_map[$cart_id]['email'] : null );
            $sms_icon = $this->build_channel_icon( 'sms', !empty( $item['phone'] ), $sms_record );
            $email_icon = $this->build_channel_icon( 'email', !empty( $item['email'] ), $email_record );
            return '<div class="cartninja-reminder-icons">' . $sms_icon . $email_icon . '</div>';
        }

        private function build_channel_icon( $channel, $available, $record ) {
            if ( !$available ) {
                return '<span class="cartninja-reminder-icon is-unavailable" aria-hidden="true"></span>';
            }
            $status_class = 'is-pending';
            $title = __( 'Reminder available, not sent yet', 'cartninja-recovery-for-woocommerce' );
            if ( !empty( $record ) ) {
                if ( in_array( $record['status'], array('sent', 'clicked'), true ) ) {
                    $status_class = 'is-sent';
                    $title = __( 'Reminder sent', 'cartninja-recovery-for-woocommerce' );
                } elseif ( 'failed' === $record['status'] ) {
                    $status_class = 'is-failed';
                    $title = ( !empty( $record['error_message'] ) ? $record['error_message'] : __( 'Reminder failed', 'cartninja-recovery-for-woocommerce' ) );
                }
            }
            $icon_map = array(
                'sms'   => 'dashicons-smartphone',
                'email' => 'dashicons-email-alt',
            );
            return sprintf(
                '<span class="cartninja-reminder-icon %1$s" title="%2$s"><span class="dashicons %3$s" aria-hidden="true"></span></span>',
                esc_attr( $status_class ),
                esc_attr( $title ),
                esc_attr( ( isset( $icon_map[$channel] ) ? $icon_map[$channel] : 'dashicons-admin-generic' ) )
            );
        }

    }

}
