<?php

/**
 * Cart tracker.
 *
 * @package CartNinjaRecovery
 */
if ( !defined( 'ABSPATH' ) ) {
    exit;
}
class CARTNINJA_Cart_Tracker {
    private $session;

    private $scheduler;

    private $notification_manager;

    public function __construct( CARTNINJA_Cart_Session $session, CARTNINJA_Scheduler $scheduler, CARTNINJA_Notification_Manager $notification_manager ) {
        $this->session = $session;
        $this->scheduler = $scheduler;
        $this->notification_manager = $notification_manager;
    }

    public function track_current_cart() {
        $this->maybe_sync_overdue_carts();
        $this->persist_current_cart();
    }

    public function track_cart_totals( $cart ) {
        if ( !$cart || is_admin() && !wp_doing_ajax() ) {
            return;
        }
        $this->maybe_sync_overdue_carts();
        $this->persist_current_cart();
    }

    public function ajax_capture_checkout_fields() {
        check_ajax_referer( 'cartninja_public_nonce', 'nonce' );
        $payload = array(
            'email'        => ( isset( $_POST['email'] ) ? sanitize_email( wp_unslash( $_POST['email'] ) ) : '' ),
            'phone'        => ( isset( $_POST['phone'] ) ? sanitize_text_field( wp_unslash( $_POST['phone'] ) ) : '' ),
            'first_name'   => ( isset( $_POST['first_name'] ) ? sanitize_text_field( wp_unslash( $_POST['first_name'] ) ) : '' ),
            'country_code' => ( isset( $_POST['country_code'] ) ? sanitize_text_field( wp_unslash( $_POST['country_code'] ) ) : '+91' ),
        );
        $cart_id = $this->persist_current_cart( $payload );
        wp_send_json_success( array(
            'cart_id' => $cart_id,
        ) );
    }

    public function maybe_sync_overdue_carts() {
        if ( wp_installing() ) {
            return;
        }
        $this->sync_overdue_carts( 20 );
        $this->sync_lost_carts( 20 );
    }

    public function maybe_schedule_cleanup() {
        if ( wp_installing() ) {
            return;
        }
        $settings = $this->get_cleanup_settings();
        $this->scheduler->sync_cleanup_schedule( 'yes' === $settings['enable_auto_cleanup'] );
    }

    public function run_scheduled_cleanup() {
        $settings = $this->get_cleanup_settings();
        if ( 'yes' !== $settings['enable_auto_cleanup'] ) {
            return 0;
        }
        return $this->cleanup_tracking_data( 500 );
    }

    public function cleanup_tracking_data( $limit = 500 ) {
        global $wpdb;
        $settings = $this->get_cleanup_settings();
        $table = $wpdb->prefix . 'cartninja_carts';
        $limit = max( 1, absint( $limit ) );
        $suppressed_cutoff = $this->get_cleanup_cutoff( $settings['suppressed_retention_days'] );
        $guest_cutoff = $this->get_cleanup_cutoff( $settings['guest_retention_days'] );
        $terminal_cutoff = $this->get_cleanup_cutoff( $settings['terminal_retention_days'] );
        $keep_order_linked = 'yes' === $settings['keep_order_linked_carts'];
        $order_linked_clause = ( $keep_order_linked ? 'AND recovered_order_id IS NULL' : '' );
        $ids = $wpdb->get_col( $wpdb->prepare(
            "SELECT id FROM {$table}\n\t\t\t\tWHERE (\n\t\t\t\t\t( tracking_status = %s AND cart_updated_at <= %s )\n\t\t\t\t\tOR ( user_id IS NULL AND recovered_order_id IS NULL AND cart_updated_at <= %s )\n\t\t\t\t\tOR ( status IN ('recovered','lost') AND cart_updated_at <= %s {$order_linked_clause} )\n\t\t\t\t)\n\t\t\t\tORDER BY cart_updated_at ASC\n\t\t\t\tLIMIT %d",
            'suppressed',
            $suppressed_cutoff,
            $guest_cutoff,
            $terminal_cutoff,
            $limit
        ) );
        if ( empty( $ids ) ) {
            return 0;
        }
        return $this->delete_tracking_records( $ids );
    }

    public function persist_current_cart( $checkout_data = array() ) {
        global $wpdb;
        if ( !$this->session->is_available() ) {
            return 0;
        }
        $cart_contents = $this->session->get_cart_contents();
        $session_id = $this->session->get_session_id();
        if ( empty( $session_id ) || empty( $cart_contents ) ) {
            return 0;
        }
        $table = $wpdb->prefix . 'cartninja_carts';
        $existing = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$table} WHERE session_id = %s", $session_id ), ARRAY_A );
        $recovery = ( !empty( $existing['recovery_token'] ) ? $existing['recovery_token'] : wp_generate_password( 32, false, false ) );
        $user_id = ( get_current_user_id() ? get_current_user_id() : null );
        $current_ts = current_time( 'mysql' );
        $customer = $this->get_checkout_snapshot( $checkout_data );
        $identity = $this->get_request_identity();
        $existing_id = ( !empty( $existing['id'] ) ? absint( $existing['id'] ) : 0 );
        if ( $existing && $this->is_cart_suppressed( $existing ) ) {
            return 0;
        }
        if ( $this->should_suppress_tracking(
            $existing_id,
            $user_id,
            $customer,
            $identity
        ) ) {
            $this->record_suppressed_attempt( $identity['suppression_reason'] );
            if ( $existing_id ) {
                $this->suppress_existing_cart( $existing_id, $identity['suppression_reason'] );
            } else {
                $this->insert_suppressed_cart(
                    $table,
                    $session_id,
                    $user_id,
                    $customer,
                    $identity,
                    $cart_contents,
                    $recovery,
                    $current_ts
                );
            }
            return 0;
        }
        $status = ( !empty( $existing['status'] ) && in_array( $existing['status'], array(
            'active',
            'abandoned',
            'recovering',
            'recovered',
            'lost'
        ), true ) ? $existing['status'] : 'active' );
        $is_terminal = in_array( $status, array(
            'recovered',
            'recovering',
            'abandoned',
            'lost'
        ), true );
        $abandoned_at = ( $is_terminal && !empty( $existing['abandoned_at'] ) ? $existing['abandoned_at'] : null );
        $data = array(
            'session_id'            => $session_id,
            'user_id'               => $user_id,
            'email'                 => $customer['email'],
            'phone'                 => $customer['phone'],
            'country_code'          => $customer['country_code'],
            'customer_ip'           => $identity['ip'],
            'customer_country_code' => $identity['country_code'],
            'customer_country_name' => $identity['country_name'],
            'customer_ip_hash'      => $identity['ip_hash'],
            'user_agent_hash'       => $identity['user_agent_hash'],
            'tracking_status'       => 'tracked',
            'suppression_reason'    => null,
            'cart_contents'         => wp_json_encode( $cart_contents ),
            'cart_total'            => $this->session->get_cart_total(),
            'currency'              => $this->session->get_currency(),
            'status'                => ( $is_terminal ? $status : 'active' ),
            'recovery_token'        => $recovery,
            'cart_updated_at'       => $current_ts,
            'abandoned_at'          => $abandoned_at,
        );
        $formats = array(
            '%s',
            '%d',
            '%s',
            '%s',
            '%s',
            '%s',
            '%s',
            '%s',
            '%s',
            '%s',
            '%s',
            '%s',
            '%s',
            '%f',
            '%s',
            '%s',
            '%s',
            '%s',
            '%s'
        );
        if ( $existing ) {
            $wpdb->update(
                $table,
                $data,
                array(
                    'id' => absint( $existing['id'] ),
                ),
                $formats,
                array('%d')
            );
            $cart_id = absint( $existing['id'] );
        } else {
            $data['created_at'] = $current_ts;
            $formats[] = '%s';
            $wpdb->insert( $table, $data, $formats );
            $cart_id = absint( $wpdb->insert_id );
        }
        if ( $cart_id ) {
            $this->scheduler->schedule_cart_abandonment( $cart_id, $this->get_abandonment_threshold() );
        }
        return $cart_id;
    }

    public function mark_cart_abandoned( $cart_id ) {
        global $wpdb;
        $cart_id = absint( $cart_id );
        if ( !$cart_id ) {
            return;
        }
        $table = $wpdb->prefix . 'cartninja_carts';
        $cart = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$table} WHERE id = %d", $cart_id ), ARRAY_A );
        if ( !$cart || !empty( $cart['recovered_order_id'] ) || $this->is_cart_suppressed( $cart ) ) {
            return;
        }
        $threshold_minutes = $this->get_abandonment_threshold();
        $updated_at = strtotime( $cart['cart_updated_at'] );
        $deadline = $updated_at + $threshold_minutes * MINUTE_IN_SECONDS;
        if ( time() < $deadline ) {
            $this->scheduler->schedule_cart_abandonment( $cart_id, $threshold_minutes );
            return;
        }
        if ( in_array( $cart['status'], array('abandoned', 'recovering', 'recovered'), true ) ) {
            return;
        }
        $this->transition_cart_to_abandoned( $cart );
    }

    private function get_checkout_snapshot( $checkout_data ) {
        $defaults = array(
            'email'        => '',
            'phone'        => '',
            'country_code' => '+91',
            'first_name'   => '',
        );
        $data = wp_parse_args( $checkout_data, $defaults );
        if ( function_exists( 'WC' ) && WC() && WC()->customer ) {
            if ( empty( $data['email'] ) ) {
                $data['email'] = WC()->customer->get_billing_email();
            }
            if ( empty( $data['phone'] ) ) {
                $data['phone'] = WC()->customer->get_billing_phone();
            }
            if ( empty( $data['first_name'] ) ) {
                $data['first_name'] = WC()->customer->get_billing_first_name();
            }
        }
        if ( function_exists( 'WC' ) && WC() && WC()->session && !empty( $data['first_name'] ) ) {
            WC()->session->set( 'cartninja_customer_name', sanitize_text_field( $data['first_name'] ) );
        }
        return array(
            'email'        => sanitize_email( $data['email'] ),
            'phone'        => preg_replace( '/[^0-9+\\-]/', '', (string) $data['phone'] ),
            'country_code' => sanitize_text_field( $data['country_code'] ),
        );
    }

    private function get_abandonment_threshold() {
        $settings = get_option( 'cartninja_settings', array() );
        $general = ( isset( $settings['general'] ) ? $settings['general'] : array() );
        return max( 1, absint( ( isset( $general['abandonment_threshold'] ) ? $general['abandonment_threshold'] : 15 ) ) );
    }

    private function get_request_identity() {
        $ip = $this->resolve_customer_ip();
        $user_agent = ( isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '' );
        $country = $this->resolve_ip_country( $ip );
        return array(
            'ip'                 => $ip,
            'country_code'       => $country['code'],
            'country_name'       => $country['name'],
            'ip_hash'            => ( $ip ? hash( 'sha256', wp_salt( 'nonce' ) . '|' . $ip ) : '' ),
            'user_agent_hash'    => ( $user_agent ? hash( 'sha256', $user_agent ) : '' ),
            'suppression_reason' => '',
        );
    }

    private function resolve_ip_country( $ip ) {
        $country = array(
            'code' => '',
            'name' => '',
        );
        if ( empty( $ip ) || !$this->is_public_ip( $ip ) || !class_exists( 'WC_Geolocation' ) ) {
            return $country;
        }
        $location = WC_Geolocation::geolocate_ip( $ip, false, true );
        $code = ( isset( $location['country'] ) ? strtoupper( sanitize_text_field( $location['country'] ) ) : '' );
        if ( !preg_match( '/^[A-Z]{2}$/', $code ) ) {
            return $country;
        }
        $country['code'] = $code;
        $country['name'] = $this->get_country_name_from_code( $code );
        return $country;
    }

    private function get_country_name_from_code( $code ) {
        if ( function_exists( 'WC' ) && WC() && isset( WC()->countries ) ) {
            $countries = WC()->countries->get_countries();
            if ( isset( $countries[$code] ) ) {
                return sanitize_text_field( $countries[$code] );
            }
        }
        return $code;
    }

    private function is_public_ip( $ip ) {
        return false !== filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE );
    }

    private function resolve_customer_ip() {
        $candidates = array();
        if ( !empty( $_SERVER['REMOTE_ADDR'] ) ) {
            $candidates[] = wp_unslash( $_SERVER['REMOTE_ADDR'] );
        }
        if ( $this->trust_proxy_headers() ) {
            foreach ( array('HTTP_CF_CONNECTING_IP', 'HTTP_X_REAL_IP', 'HTTP_X_FORWARDED_FOR') as $header ) {
                if ( empty( $_SERVER[$header] ) ) {
                    continue;
                }
                $raw = sanitize_text_field( wp_unslash( $_SERVER[$header] ) );
                foreach ( explode( ',', $raw ) as $part ) {
                    $candidates[] = trim( $part );
                }
            }
        }
        foreach ( $candidates as $candidate ) {
            $ip = $this->normalize_ip( $candidate );
            if ( $ip ) {
                return $ip;
            }
        }
        return '';
    }

    private function normalize_ip( $ip ) {
        $ip = trim( (string) $ip );
        if ( false !== strpos( $ip, ':' ) && substr_count( $ip, ':' ) === 1 && false !== strpos( $ip, '.' ) ) {
            $parts = explode( ':', $ip );
            $ip = $parts[0];
        }
        return ( filter_var( $ip, FILTER_VALIDATE_IP ) ? $ip : '' );
    }

    private function should_suppress_tracking(
        $existing_id,
        $user_id,
        $customer,
        &$identity
    ) {
        $settings = $this->get_protection_settings();
        $ip = $identity['ip'];
        $ip_hash = $identity['ip_hash'];
        $is_blocked = $ip && in_array( $ip, $this->get_blocked_ips( $settings ), true );
        $country_code = ( isset( $identity['country_code'] ) ? strtoupper( (string) $identity['country_code'] ) : '' );
        $is_country_blocked = $country_code && in_array( $country_code, $this->get_blocked_countries( $settings ), true );
        if ( $is_blocked ) {
            $identity['suppression_reason'] = 'blocked_ip';
            return true;
        }
        if ( $is_country_blocked ) {
            $identity['suppression_reason'] = 'blocked_country';
            return true;
        }
        if ( 'yes' !== $settings['enable_guest_protection'] || $user_id || $existing_id || !$ip_hash ) {
            return false;
        }
        $has_contact = !empty( $customer['email'] ) || !empty( $customer['phone'] );
        $short_limit = max( 1, absint( $settings['short_guest_ip_limit'] ) );
        $short_window = max( 1, absint( $settings['short_window_minutes'] ) );
        $short_count = $this->count_recent_guest_carts_by_ip_hash( $ip_hash, $short_window * MINUTE_IN_SECONDS );
        if ( $short_count >= $short_limit ) {
            $identity['suppression_reason'] = 'guest_ip_rate_limit';
            return true;
        }
        if ( $has_contact ) {
            return false;
        }
        $daily_limit = max( 1, absint( $settings['daily_guest_ip_limit'] ) );
        $daily_count = $this->count_recent_guest_carts_by_ip_hash( $ip_hash, DAY_IN_SECONDS );
        if ( $daily_count >= $daily_limit ) {
            $identity['suppression_reason'] = 'guest_ip_rate_limit';
            return true;
        }
        return false;
    }

    private function count_recent_guest_carts_by_ip_hash( $ip_hash, $window_seconds ) {
        global $wpdb;
        $table = $wpdb->prefix . 'cartninja_carts';
        $cutoff = get_date_from_gmt( gmdate( 'Y-m-d H:i:s', time() - absint( $window_seconds ) ), 'Y-m-d H:i:s' );
        return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$table} WHERE user_id IS NULL AND customer_ip_hash = %s AND created_at >= %s", $ip_hash, $cutoff ) );
    }

    private function suppress_existing_cart( $cart_id, $reason ) {
        global $wpdb;
        $wpdb->update(
            $wpdb->prefix . 'cartninja_carts',
            array(
                'tracking_status'    => 'suppressed',
                'suppression_reason' => sanitize_key( $reason ),
            ),
            array(
                'id' => absint( $cart_id ),
            ),
            array('%s', '%s'),
            array('%d')
        );
    }

    private function insert_suppressed_cart(
        $table,
        $session_id,
        $user_id,
        $customer,
        $identity,
        $cart_contents,
        $recovery,
        $current_ts
    ) {
        global $wpdb;
        $wpdb->insert( $table, array(
            'session_id'            => sanitize_text_field( $session_id ),
            'user_id'               => ( $user_id ? absint( $user_id ) : null ),
            'email'                 => sanitize_email( $customer['email'] ),
            'phone'                 => preg_replace( '/[^0-9+\\-]/', '', (string) $customer['phone'] ),
            'country_code'          => sanitize_text_field( $customer['country_code'] ),
            'customer_ip'           => sanitize_text_field( $identity['ip'] ),
            'customer_country_code' => sanitize_text_field( $identity['country_code'] ),
            'customer_country_name' => sanitize_text_field( $identity['country_name'] ),
            'customer_ip_hash'      => sanitize_text_field( $identity['ip_hash'] ),
            'user_agent_hash'       => sanitize_text_field( $identity['user_agent_hash'] ),
            'tracking_status'       => 'suppressed',
            'suppression_reason'    => sanitize_key( $identity['suppression_reason'] ),
            'cart_contents'         => wp_json_encode( $cart_contents ),
            'cart_total'            => $this->session->get_cart_total(),
            'currency'              => $this->session->get_currency(),
            'status'                => 'active',
            'recovery_token'        => sanitize_text_field( $recovery ),
            'cart_updated_at'       => $current_ts,
            'abandoned_at'          => null,
            'created_at'            => $current_ts,
        ), array(
            '%s',
            '%d',
            '%s',
            '%s',
            '%s',
            '%s',
            '%s',
            '%s',
            '%s',
            '%s',
            '%s',
            '%s',
            '%s',
            '%f',
            '%s',
            '%s',
            '%s',
            '%s',
            '%s',
            '%s'
        ) );
    }

    private function record_suppressed_attempt( $reason ) {
        $today = current_time( 'Y-m-d' );
        $stats = get_option( 'cartninja_suppression_stats', array() );
        if ( !is_array( $stats ) || !isset( $stats['date'] ) || $today !== $stats['date'] ) {
            $stats = array(
                'date'    => $today,
                'total'   => 0,
                'reasons' => array(),
            );
        }
        $reason = sanitize_key( $reason );
        if ( empty( $reason ) ) {
            $reason = 'unknown';
        }
        $stats['total'] = ( isset( $stats['total'] ) ? absint( $stats['total'] ) + 1 : 1 );
        if ( !isset( $stats['reasons'] ) || !is_array( $stats['reasons'] ) ) {
            $stats['reasons'] = array();
        }
        $stats['reasons'][$reason] = ( isset( $stats['reasons'][$reason] ) ? absint( $stats['reasons'][$reason] ) + 1 : 1 );
        update_option( 'cartninja_suppression_stats', $stats, false );
    }

    private function is_cart_suppressed( $cart ) {
        return isset( $cart['tracking_status'] ) && 'suppressed' === $cart['tracking_status'];
    }

    private function trust_proxy_headers() {
        $settings = $this->get_protection_settings();
        return 'yes' === $settings['trust_proxy_headers'];
    }

    private function get_protection_settings() {
        $settings = get_option( 'cartninja_settings', array() );
        $protection = ( isset( $settings['protection'] ) && is_array( $settings['protection'] ) ? $settings['protection'] : array() );
        return wp_parse_args( $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',
        ) );
    }

    private function get_blocked_ips( $settings ) {
        $blocked = ( isset( $settings['blocked_ips'] ) ? (string) $settings['blocked_ips'] : '' );
        $ips = preg_split( '/[\\r\\n,]+/', $blocked );
        $result = array();
        foreach ( $ips as $ip ) {
            $normalized = $this->normalize_ip( $ip );
            if ( $normalized ) {
                $result[] = $normalized;
            }
        }
        return array_values( array_unique( $result ) );
    }

    private function get_blocked_countries( $settings ) {
        $blocked = ( isset( $settings['blocked_countries'] ) ? (string) $settings['blocked_countries'] : '' );
        $codes = preg_split( '/[\\r\\n,]+/', $blocked );
        $result = array();
        foreach ( $codes as $code ) {
            $code = strtoupper( trim( sanitize_text_field( $code ) ) );
            if ( preg_match( '/^[A-Z]{2}$/', $code ) ) {
                $result[] = $code;
            }
        }
        return array_values( array_unique( $result ) );
    }

    private function sync_overdue_carts( $limit = 20 ) {
        global $wpdb;
        $threshold_minutes = $this->get_abandonment_threshold();
        $cutoff = gmdate( 'Y-m-d H:i:s', time() - $threshold_minutes * MINUTE_IN_SECONDS );
        $table = $wpdb->prefix . 'cartninja_carts';
        $rows = $wpdb->get_results( $wpdb->prepare(
            "SELECT * FROM {$table} WHERE status = %s AND tracking_status <> 'suppressed' AND recovered_order_id IS NULL AND cart_updated_at <= %s ORDER BY cart_updated_at ASC LIMIT %d",
            'active',
            get_date_from_gmt( $cutoff, 'Y-m-d H:i:s' ),
            absint( $limit )
        ), ARRAY_A );
        foreach ( $rows as $row ) {
            $this->transition_cart_to_abandoned( $row );
        }
    }

    private function sync_lost_carts( $limit = 20 ) {
        global $wpdb;
        $lost_days = $this->get_lost_threshold_days();
        $cutoff = gmdate( 'Y-m-d H:i:s', time() - $lost_days * DAY_IN_SECONDS );
        $table = $wpdb->prefix . 'cartninja_carts';
        $rows = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table} WHERE status IN ('abandoned','recovering') AND recovered_order_id IS NULL AND abandoned_at IS NOT NULL AND abandoned_at <= %s ORDER BY abandoned_at ASC LIMIT %d", get_date_from_gmt( $cutoff, 'Y-m-d H:i:s' ), absint( $limit ) ), ARRAY_A );
        foreach ( $rows as $row ) {
            $wpdb->update(
                $table,
                array(
                    'status' => 'lost',
                ),
                array(
                    'id' => absint( $row['id'] ),
                ),
                array('%s'),
                array('%d')
            );
        }
    }

    private function transition_cart_to_abandoned( $cart ) {
        global $wpdb;
        $cart_id = absint( $cart['id'] );
        $threshold_minutes = $this->get_abandonment_threshold();
        $abandoned_at_value = gmdate( 'Y-m-d H:i:s', strtotime( $cart['cart_updated_at'] . ' UTC' ) + $threshold_minutes * MINUTE_IN_SECONDS );
        $wpdb->update(
            $wpdb->prefix . 'cartninja_carts',
            array(
                'status'       => 'abandoned',
                'abandoned_at' => get_date_from_gmt( $abandoned_at_value, 'Y-m-d H:i:s' ),
            ),
            array(
                'id' => $cart_id,
            ),
            array('%s', '%s'),
            array('%d')
        );
        if ( cartninja_is_premium_unlocked() ) {
            if ( class_exists( 'CARTNINJA_Sequence_Runner' ) ) {
                $sequence_runner = new CARTNINJA_Sequence_Runner();
                $sequence_runner->start_sequence_for_cart( $cart_id );
                $settings = get_option( 'cartninja_settings', array() );
                $active_sequence_id = ( isset( $settings['sequences']['active_sequence_id'] ) ? absint( $settings['sequences']['active_sequence_id'] ) : 0 );
                if ( $active_sequence_id ) {
                    return;
                }
            }
        }
        if ( !empty( $cart['phone'] ) ) {
            $this->notification_manager->schedule_notification( $cart_id, 'sms' );
        }
        if ( !empty( $cart['email'] ) ) {
            $this->notification_manager->schedule_notification( $cart_id, 'email' );
        }
    }

    private function get_lost_threshold_days() {
        $settings = get_option( 'cartninja_settings', array() );
        $general = ( isset( $settings['general'] ) ? $settings['general'] : array() );
        return max( 1, absint( ( isset( $general['lost_threshold_days'] ) ? $general['lost_threshold_days'] : 30 ) ) );
    }

    private function get_cleanup_settings() {
        $settings = get_option( 'cartninja_settings', array() );
        $cleanup = ( isset( $settings['cleanup'] ) && is_array( $settings['cleanup'] ) ? $settings['cleanup'] : array() );
        $cleanup = wp_parse_args( $cleanup, array(
            'enable_auto_cleanup'       => 'no',
            'guest_retention_days'      => 30,
            'suppressed_retention_days' => 7,
            'terminal_retention_days'   => 90,
            'keep_order_linked_carts'   => 'yes',
        ) );
        return array(
            'enable_auto_cleanup'       => ( 'yes' === $cleanup['enable_auto_cleanup'] ? 'yes' : 'no' ),
            'guest_retention_days'      => max( 1, absint( $cleanup['guest_retention_days'] ) ),
            'suppressed_retention_days' => max( 1, absint( $cleanup['suppressed_retention_days'] ) ),
            'terminal_retention_days'   => max( 1, absint( $cleanup['terminal_retention_days'] ) ),
            'keep_order_linked_carts'   => ( 'no' === $cleanup['keep_order_linked_carts'] ? 'no' : 'yes' ),
        );
    }

    private function get_cleanup_cutoff( $days ) {
        $cutoff = gmdate( 'Y-m-d H:i:s', time() - max( 1, absint( $days ) ) * DAY_IN_SECONDS );
        return get_date_from_gmt( $cutoff, 'Y-m-d H:i:s' );
    }

    private function delete_tracking_records( $ids ) {
        global $wpdb;
        $ids = array_values( array_filter( array_map( 'absint', (array) $ids ) ) );
        if ( empty( $ids ) ) {
            return 0;
        }
        $this->delete_rows_by_ids( $wpdb->prefix . 'cartninja_notifications', 'cart_id', $ids );
        if ( $this->table_exists( $wpdb->prefix . 'cartninja_cart_sequence_progress' ) ) {
            $this->delete_rows_by_ids( $wpdb->prefix . 'cartninja_cart_sequence_progress', 'cart_id', $ids );
        }
        return $this->delete_rows_by_ids( $wpdb->prefix . 'cartninja_carts', 'id', $ids );
    }

    private function delete_rows_by_ids( $table, $column, $ids ) {
        global $wpdb;
        $allowed_tables = array($wpdb->prefix . 'cartninja_notifications', $wpdb->prefix . 'cartninja_cart_sequence_progress', $wpdb->prefix . 'cartninja_carts');
        $allowed_columns = array('cart_id', 'id');
        if ( !in_array( $table, $allowed_tables, true ) || !in_array( $column, $allowed_columns, true ) ) {
            return 0;
        }
        $ids = array_values( array_filter( array_map( 'absint', (array) $ids ) ) );
        if ( empty( $ids ) ) {
            return 0;
        }
        $placeholders = implode( ',', array_fill( 0, count( $ids ), '%d' ) );
        return (int) $wpdb->query( $wpdb->prepare( "DELETE FROM {$table} WHERE {$column} IN ({$placeholders})", $ids ) );
    }

    private function table_exists( $table ) {
        global $wpdb;
        return $table === $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
    }

}
