<?php
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class NNAXFUF_License_Manager {
    private static $hooks_registered = false;
    const OPTION_LICENSES          = 'nnaxfuf_licenses';
    const OPTION_NOTICES           = 'nnaxfuf_license_action_notice';
    const CHECK_INTERVAL           = 43200;
    const DEFAULT_LICENSE_API_URL  = 'https://www.expertonaut.de/wp-json/nnax-licenses/v1';
    const CRON_HOOK                = 'nnaxfuf_license_scheduled_check';

    public function __construct() {
        if ( self::$hooks_registered ) {
            return;
        }
        add_action( 'admin_init', array( $this, 'handle_actions' ) );
        add_action( 'init', array( $this, 'ensure_cron_scheduled' ) );
        add_action( self::CRON_HOOK, array( $this, 'cron_check' ) );
        add_action( 'admin_notices', array( $this, 'render_admin_notices' ) );
        self::$hooks_registered = true;
    }

    public function get_products() {
        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Legacy hook kept for addon compatibility.
        $products = apply_filters( 'nnaxfuf_license_products', array() );
        return is_array( $products ) ? $products : array();
    }

    public function get_product( $plugin_slug ) {
        $products = $this->get_products();
        if ( isset( $products[ $plugin_slug ] ) && is_array( $products[ $plugin_slug ] ) ) {
            return $products[ $plugin_slug ];
        }
        return array();
    }

    public function is_product_active( $plugin_slug ) {
        $licenses = get_option( self::OPTION_LICENSES, array() );
        $status   = isset( $licenses[ $plugin_slug ]['status'] ) ? (string) $licenses[ $plugin_slug ]['status'] : '';
        return $status === 'active';
    }

    public function get_product_data( $plugin_slug ) {
        $licenses = get_option( self::OPTION_LICENSES, array() );
        $data     = isset( $licenses[ $plugin_slug ] ) && is_array( $licenses[ $plugin_slug ] ) ? $licenses[ $plugin_slug ] : array();

        return wp_parse_args(
            $data,
            array(
                'license_key'       => '',
                'status'            => '',
                'last_check'        => '',
                'expires_at'        => '',
                'activations_used'  => 0,
                'max_activations'   => 0,
                'error_code'        => '',
                'error_message'     => '',
                'last_successful_check' => '',
                'last_failed_check'     => '',
                'last_error_code'       => '',
                'last_error_message'    => '',
            )
        );
    }

    public function handle_actions() {
        if ( ! is_admin() || ! current_user_can( 'manage_options' ) ) {
            return;
        }
        if ( wp_doing_ajax() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) {
            return;
        }
        if ( empty( $_POST['nnaxfuf_license_action'] ) ) {
            return;
        }

        check_admin_referer( 'nnaxfuf_license_action', 'nnaxfuf_license_nonce' );
        $action      = sanitize_key( (string) wp_unslash( $_POST['nnaxfuf_license_action'] ) );
        $plugin_slug = isset( $_POST['nnaxfuf_license_plugin_slug'] ) ? sanitize_text_field( wp_unslash( $_POST['nnaxfuf_license_plugin_slug'] ) ) : '';
        $products    = $this->get_products();

        if ( $plugin_slug === '' || ! isset( $products[ $plugin_slug ] ) ) {
            return;
        }

        $product = $products[ $plugin_slug ];
        if ( $action === 'activate' ) {
            $license_key = isset( $_POST['nnaxfuf_license_key'] ) ? sanitize_text_field( wp_unslash( $_POST['nnaxfuf_license_key'] ) ) : '';
            $response    = $this->activate_license( $product, $license_key );
            $this->set_flash_notice( $this->build_action_message( 'activate', $response ) );
        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Legacy hook kept for addon compatibility.
            do_action( 'nnaxfuf_license_state_changed', $plugin_slug, 'activate' );
        } elseif ( $action === 'check' ) {
            $response = $this->check_license( $product, true );
            $this->set_flash_notice( $this->build_action_message( 'check', $response ) );
        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Legacy hook kept for addon compatibility.
            do_action( 'nnaxfuf_license_state_changed', $plugin_slug, 'check' );
        } elseif ( $action === 'deactivate' ) {
            $response = $this->deactivate_license( $product );
            $this->set_flash_notice( $this->build_action_message( 'deactivate', $response ) );
        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Legacy hook kept for addon compatibility.
            do_action( 'nnaxfuf_license_state_changed', $plugin_slug, 'deactivate' );
        }

        wp_safe_redirect( admin_url( 'admin.php?page=nnax-user-files-licenses' ) );
        exit;
    }

    public function cron_check() {
        foreach ( $this->get_products() as $plugin_slug => $product ) {
            if ( $this->should_auto_check( $plugin_slug ) ) {
                $this->check_license( $product, true );
            }
        }
    }

    public function ensure_cron_scheduled() {
        if ( ! wp_next_scheduled( self::CRON_HOOK ) ) {
            wp_schedule_event( time() + HOUR_IN_SECONDS, 'twicedaily', self::CRON_HOOK );
        }
    }

    public static function schedule_cron() {
        if ( ! wp_next_scheduled( self::CRON_HOOK ) ) {
            wp_schedule_event( time() + HOUR_IN_SECONDS, 'twicedaily', self::CRON_HOOK );
        }
    }

    public static function unschedule_cron() {
        $timestamp = wp_next_scheduled( self::CRON_HOOK );
        while ( $timestamp ) {
            wp_unschedule_event( $timestamp, self::CRON_HOOK );
            $timestamp = wp_next_scheduled( self::CRON_HOOK );
        }
    }

    public function render_admin_notices() {
        if ( ! is_admin() || ! current_user_can( 'manage_options' ) ) {
            return;
        }
        if ( wp_doing_ajax() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) {
            return;
        }

        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin page slug used for notice scoping.
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin page slug used for notice scoping.
        $current_page = isset( $_GET['page'] ) ? sanitize_key( (string) wp_unslash( $_GET['page'] ) ) : '';
        $this->render_flash_notice();
        if ( ! $this->is_api_url_https() ) {
            $this->print_notice( esc_html__( 'License validation is not configured correctly. Please contact support.', 'nnax-frontend-user-files' ) );
        }
        if ( $current_page === 'nnax-user-files-licenses' ) {
            return;
        }

        foreach ( $this->get_products() as $plugin_slug => $product ) {
            $label = isset( $product['label'] ) ? (string) $product['label'] : $plugin_slug;
            $data  = $this->get_product_data( $plugin_slug );
            if ( $data['status'] === 'active' && $data['error_code'] !== 'server_unreachable' && $data['error_code'] !== 'ssl_connection_failed' ) {
                continue;
            }

            $license_page = admin_url( 'admin.php?page=nnax-user-files-licenses' );
            if ( $data['license_key'] === '' ) {
                $this->print_notice(
                    sprintf(
                        '%s: %s <a href="%s">%s</a>',
                        esc_html( $label ),
                        esc_html__( 'No license key is stored.', 'nnax-frontend-user-files' ),
                        esc_url( $license_page ),
                        esc_html__( 'Open license page', 'nnax-frontend-user-files' )
                    )
                );
                continue;
            }

            $message = $this->message_for_code( $data['error_code'], $data['status'] );
            $this->print_notice(
                sprintf(
                    '%s: %s <a href="%s">%s</a>',
                    esc_html( $label ),
                    esc_html( $message ),
                    esc_url( $license_page ),
                    esc_html__( 'Check license', 'nnax-frontend-user-files' )
                )
            );
        }
    }

    public function activate_license( $product, $license_key ) {
        $plugin_slug = (string) $product['slug'];
        $data = $this->get_product_data( $plugin_slug );
        $data['license_key'] = strtoupper( trim( sanitize_text_field( (string) $license_key ) ) );
        $this->save_product_data( $plugin_slug, $data );

        return $this->request( 'activate', $product, $data['license_key'] );
    }

    public function check_license( $product, $force = true ) {
        $plugin_slug = (string) $product['slug'];
        $data = $this->get_product_data( $plugin_slug );
        if ( $data['license_key'] === '' ) {
            $response = array( 'success' => false, 'valid' => false, 'code' => 'missing_license' );
            $this->save_response_state( $plugin_slug, $response );
            return $response;
        }

        if ( ! $force && ! $this->should_auto_check( $plugin_slug ) ) {
            return array( 'success' => true, 'valid' => $data['status'] === 'active', 'cached' => true );
        }

        return $this->request( 'check', $product, $data['license_key'] );
    }

    public function deactivate_license( $product ) {
        $plugin_slug = (string) $product['slug'];
        $data = $this->get_product_data( $plugin_slug );
        if ( $data['license_key'] === '' ) {
            $response = array( 'success' => false, 'valid' => false, 'code' => 'missing_license' );
            $this->save_response_state( $plugin_slug, $response );
            return $response;
        }

        $response = $this->request( 'deactivate', $product, $data['license_key'] );
        if ( ! empty( $response['success'] ) ) {
            $data['status'] = 'inactive';
            $this->save_product_data( $plugin_slug, $data );
        }
        return $response;
    }

    public function update_check( $product, $plugin_basename, $plugin_version ) {
        $plugin_slug = isset( $product['slug'] ) ? (string) $product['slug'] : '';
        if ( $plugin_slug === '' ) {
            return array();
        }

        $data = $this->get_product_data( $plugin_slug );
        $has_license_key = $data['license_key'] !== '';
        $locale          = $this->get_request_locale();

        $payload = array(
            'license_key'     => $data['license_key'],
            'site_url'        => home_url(),
            'plugin_slug'     => $plugin_slug,
            'plugin_version'  => (string) $plugin_version,
            'plugin_basename' => (string) $plugin_basename,
            'locale'          => $locale,
        );

        $response = $this->request_endpoint( 'update-check', $payload, false );
        if ( $has_license_key && is_array( $response ) && ( empty( $response['success'] ) || empty( $response['valid'] ) || ! empty( $response['code'] ) ) ) {
            $this->save_response_state( $plugin_slug, $response );
        }

        return is_array( $response ) ? $response : array();
    }

    public function get_masked_license_key( $license_key ) {
        return $this->mask_license_key( (string) $license_key );
    }

    private function request( $endpoint, $product, $license_key ) {
        $body = array(
            'license_key'    => $license_key,
            'site_url'       => home_url(),
            'plugin_slug'    => (string) $product['slug'],
            'plugin_version' => isset( $product['version'] ) ? (string) $product['version'] : '',
            'locale'         => $this->get_request_locale(),
        );
        $decoded = $this->request_endpoint( $endpoint, $body, false );
        $this->save_response_state( (string) $product['slug'], $decoded );
        return $decoded;
    }

    private function request_endpoint( $endpoint, $body, $allow_package_in_logs ) {
        $base_url = $this->get_api_url();
        if ( stripos( $base_url, 'https://' ) !== 0 ) {
            $this->log_debug(
                'request_blocked_insecure_url',
                array(
                    'endpoint' => $endpoint,
                    'url'      => $base_url,
                )
            );
            return array(
                'success' => false,
                'valid'   => false,
                'code'    => 'http_license_url_blocked',
                'message' => __( 'License validation is not configured correctly. Please contact support.', 'nnax-frontend-user-files' ),
            );
        }

        $url      = trailingslashit( $base_url ) . ltrim( $endpoint, '/' );

        $this->log_debug(
            'request_start',
            array(
                'endpoint' => $endpoint,
                'url'      => $url,
                'payload'  => $this->sanitize_log_data( $body, false ),
            )
        );

        $args = array(
            'timeout' => 15,
            'headers' => array( 'Content-Type' => 'application/json; charset=utf-8' ),
            'body'    => wp_json_encode( $body ),
        );
        $response = wp_remote_post( $url, $args );

        if ( is_wp_error( $response ) ) {
            $error_code = $this->is_ssl_error( $response ) ? 'ssl_connection_failed' : 'server_unreachable';
            $message    = $error_code === 'ssl_connection_failed'
                ? __( 'License validation could not be completed right now. Please try again later.', 'nnax-frontend-user-files' )
                : __( 'License validation could not be completed right now. Please try again later.', 'nnax-frontend-user-files' );
            $this->log_debug(
                'request_wp_error',
                array(
                    'endpoint'      => $endpoint,
                    'error_code'    => $response->get_error_code(),
                    'error_message' => $response->get_error_message(),
                )
            );
            return array(
                'success' => false,
                'valid'   => false,
                'code'    => $error_code,
                'message' => $message,
            );
        }

        $status_code = (int) wp_remote_retrieve_response_code( $response );
        $raw_body    = wp_remote_retrieve_body( $response );
        $decoded     = json_decode( $raw_body, true );
        $this->log_debug(
            'request_http_response',
            array(
                'endpoint'    => $endpoint,
                'status_code' => $status_code,
                'raw_body'    => $this->sanitize_log_data( $raw_body, $allow_package_in_logs ),
            )
        );

        if ( ! is_array( $decoded ) ) {
            $this->log_debug(
                'request_invalid_json',
                array(
                    'endpoint'    => $endpoint,
                    'status_code' => $status_code,
                )
            );
            return array(
                'success' => false,
                'valid'   => false,
                'code'    => 'json_parse_error',
                'message' => __( 'License validation could not be completed right now. Please try again later.', 'nnax-frontend-user-files' ),
            );
        }

        if ( $status_code < 200 || $status_code >= 300 ) {
            $decoded['success'] = false;
            $decoded['valid']   = false;
            $this->log_debug(
                'request_http_error_status',
                array(
                    'endpoint'    => $endpoint,
                    'status_code' => $status_code,
                    'decoded'     => $this->sanitize_log_data( $decoded, $allow_package_in_logs ),
                )
            );
        }

        $this->log_debug(
            'request_end',
            array(
                'endpoint' => $endpoint,
                'decoded'  => $this->sanitize_log_data( $decoded, $allow_package_in_logs ),
            )
        );

        return $decoded;
    }

    private function save_response_state( $plugin_slug, $response ) {
        $data = $this->get_product_data( $plugin_slug );
        $code = isset( $response['code'] ) ? sanitize_key( (string) $response['code'] ) : '';

        if ( $this->is_technical_failure_code( $code ) ) {
            $now = current_time( 'mysql' );
            $data['last_check']          = $now;
            $data['last_failed_check']   = $now;
            $data['last_error_code']     = $code;
            $data['last_error_message']  = isset( $response['message'] ) ? sanitize_text_field( (string) $response['message'] ) : $this->message_for_code( $code, '' );
            $data['error_code']          = $code;
            $data['error_message']       = $data['last_error_message'];
            $this->save_product_data( $plugin_slug, $data );
            return;
        }

        if ( $code !== 'missing_license' ) {
            $data['last_successful_check'] = current_time( 'mysql' );
            $data['last_error_code']       = '';
            $data['last_error_message']    = '';
        }

        $status = isset( $response['license_status'] ) ? sanitize_key( (string) $response['license_status'] ) : '';
        if ( ! empty( $response['valid'] ) ) {
            $status = $status !== '' ? $status : 'active';
        } elseif ( $status === '' ) {
            $status = 'invalid';
            if ( $code === 'license_expired' ) {
                $status = 'expired';
            } elseif ( $code === 'license_paused' ) {
                $status = 'paused';
            } elseif ( $code === 'license_cancelled' ) {
                $status = 'cancelled';
            } elseif ( $code === 'license_revoked' ) {
                $status = 'revoked';
            } elseif ( $code === 'license_inactive' ) {
                $status = 'inactive';
            }
        }

        $data['status']           = $status;
        $data['last_check']       = current_time( 'mysql' );
        $data['expires_at']       = isset( $response['expires_at'] ) ? sanitize_text_field( (string) $response['expires_at'] ) : '';
        $data['activations_used'] = isset( $response['activations_used'] ) ? (int) $response['activations_used'] : 0;
        $data['max_activations']  = isset( $response['max_activations'] ) ? (int) $response['max_activations'] : 0;
        $data['error_code']       = $code;
        $data['error_message']    = isset( $response['message'] ) ? sanitize_text_field( (string) $response['message'] ) : '';

        $this->save_product_data( $plugin_slug, $data );
    }

    private function save_product_data( $plugin_slug, $data ) {
        $licenses                = get_option( self::OPTION_LICENSES, array() );
        $licenses[ $plugin_slug ] = $data;
        update_option( self::OPTION_LICENSES, $licenses, false );
    }

    private function should_auto_check( $plugin_slug ) {
        $data = $this->get_product_data( $plugin_slug );
        if ( $data['license_key'] === '' ) {
            return false;
        }
        if ( $data['last_check'] === '' ) {
            return true;
        }
        $timestamp = strtotime( (string) $data['last_check'] );
        if ( ! $timestamp ) {
            return true;
        }
        return ( time() - $timestamp ) >= self::CHECK_INTERVAL;
    }

    private function get_api_url() {
        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Legacy hook kept for addon compatibility.
        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Legacy hook kept for addon compatibility.
        return (string) apply_filters( 'nnaxfuf_license_api_url', self::DEFAULT_LICENSE_API_URL );
    }

    private function build_action_message( $action, $response ) {
        $ok = ! empty( $response['success'] ) && ( ! empty( $response['valid'] ) || $action === 'deactivate' );
        if ( $ok ) {
            if ( $action === 'activate' ) {
                return array( 'type' => 'success', 'text' => __( 'License activated successfully.', 'nnax-frontend-user-files' ) );
            }
            if ( $action === 'deactivate' ) {
                return array( 'type' => 'success', 'text' => __( 'License deactivated successfully.', 'nnax-frontend-user-files' ) );
            }
            return array( 'type' => 'success', 'text' => __( 'License status checked successfully.', 'nnax-frontend-user-files' ) );
        }

        $code    = isset( $response['code'] ) ? sanitize_key( (string) $response['code'] ) : '';
        $message = isset( $response['message'] ) ? sanitize_text_field( (string) $response['message'] ) : '';
        if ( $code !== '' ) {
            $message = $this->message_for_code( $code, '' );
        } elseif ( $message === '' ) {
            $message = $this->message_for_code( $code, '' );
        }
        return array( 'type' => 'error', 'text' => $message );
    }

    private function message_for_code( $code, $status ) {
        $map = array(
            'license_not_found'        => __( 'License was not found.', 'nnax-frontend-user-files' ),
            'license_expired'          => __( 'Your license has expired.', 'nnax-frontend-user-files' ),
            'license_revoked'          => __( 'This license has been revoked. Please contact support.', 'nnax-frontend-user-files' ),
            'license_paused'           => __( 'This license is paused.', 'nnax-frontend-user-files' ),
            'license_cancelled'        => __( 'This license has been cancelled.', 'nnax-frontend-user-files' ),
            'plugin_mismatch'          => __( 'This license does not belong to this plugin.', 'nnax-frontend-user-files' ),
            'activation_limit_reached' => __( 'The activation limit has been reached.', 'nnax-frontend-user-files' ),
            'site_not_activated'       => __( 'This website is not activated.', 'nnax-frontend-user-files' ),
            'rate_limited'             => __( 'Too many requests. Please try again later.', 'nnax-frontend-user-files' ),
            'server_error'             => __( 'License validation could not be completed right now. Please try again later.', 'nnax-frontend-user-files' ),
            'license_inactive'         => __( 'License is inactive.', 'nnax-frontend-user-files' ),
            'connection_failed'        => __( 'License validation could not be completed right now. Please try again later.', 'nnax-frontend-user-files' ),
            'server_unreachable'       => __( 'License validation could not be completed right now. Please try again later.', 'nnax-frontend-user-files' ),
            'ssl_connection_failed'    => __( 'License validation could not be completed right now. Please try again later.', 'nnax-frontend-user-files' ),
            'json_parse_error'         => __( 'License validation could not be completed right now. Please try again later.', 'nnax-frontend-user-files' ),
            'invalid_response'         => __( 'License validation could not be completed right now. Please try again later.', 'nnax-frontend-user-files' ),
            'http_license_url_blocked' => __( 'License validation is not configured correctly. Please contact support.', 'nnax-frontend-user-files' ),
            'invalid_license_api_url'  => __( 'License validation is not configured correctly. Please contact support.', 'nnax-frontend-user-files' ),
        );
        if ( isset( $map[ $code ] ) ) {
            return $map[ $code ];
        }
        if ( $status === 'expired' ) {
            return __( 'License has expired.', 'nnax-frontend-user-files' );
        }
        if ( $status === 'paused' ) {
            return __( 'License is paused.', 'nnax-frontend-user-files' );
        }
        if ( $status === 'cancelled' ) {
            return __( 'License has been cancelled.', 'nnax-frontend-user-files' );
        }
        if ( $status === 'revoked' ) {
            return __( 'License has been revoked.', 'nnax-frontend-user-files' );
        }
        return __( 'License is invalid.', 'nnax-frontend-user-files' );
    }

    private function set_flash_notice( $notice ) {
        set_transient( self::OPTION_NOTICES . '_' . get_current_user_id(), $notice, 120 );
    }

    private function render_flash_notice() {
        $key    = self::OPTION_NOTICES . '_' . get_current_user_id();
        $notice = get_transient( $key );
        if ( ! is_array( $notice ) || empty( $notice['text'] ) ) {
            return;
        }
        $class = ( ! empty( $notice['type'] ) && $notice['type'] === 'success' ) ? 'notice-success' : 'notice-warning';
        echo '<div class="notice ' . esc_attr( $class ) . '"><p>' . esc_html( (string) $notice['text'] ) . '</p></div>';
        delete_transient( $key );
    }

    private function print_notice( $message ) {
        echo '<div class="notice notice-warning"><p>' . wp_kses_post( $message ) . '</p></div>';
    }

    private function log_debug( $event, $context = array() ) {
        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Legacy hook kept for addon compatibility.
        $enabled = (bool) apply_filters( 'nnaxfuf_license_debug_logging', defined( 'WP_DEBUG' ) && WP_DEBUG );
        if ( ! $enabled ) {
            return;
        }

        if ( ! is_array( $context ) ) {
            $context = array();
        }

        $json = wp_json_encode( $context );
        if ( ! is_string( $json ) || $json === '' ) {
            $json = '{}';
        }

        do_action( 'nnax_nnaxfuf_license_debug', sanitize_key( (string) $event ), $context, $json );
    }

    private function mask_license_key( $key ) {
        $key = trim( $key );
        if ( $key === '' ) {
            return '';
        }
        $length = strlen( $key );
        if ( $length <= 4 ) {
            return str_repeat( '*', $length );
        }
        return str_repeat( '*', max( 0, $length - 4 ) ) . substr( $key, -4 );
    }

    private function is_ssl_error( $response ) {
        if ( ! is_wp_error( $response ) ) {
            return false;
        }

        $message = (string) $response->get_error_message();
        $ssl_markers = array(
            'ssl certificate',
            'self signed certificate',
            'certificate verify failed',
            'cURL error 60',
            'unable to get local issuer certificate',
        );
        foreach ( $ssl_markers as $marker ) {
            if ( stripos( $message, $marker ) !== false ) {
                return true;
            }
        }

        return false;
    }

    private function is_api_url_https() {
        return stripos( $this->get_api_url(), 'https://' ) === 0;
    }

    private function get_request_locale() {
        if ( function_exists( 'determine_locale' ) ) {
            $locale = sanitize_text_field( (string) determine_locale() );
            if ( '' !== $locale ) {
                return $locale;
            }
        }

        if ( function_exists( 'get_user_locale' ) ) {
            $locale = sanitize_text_field( (string) get_user_locale() );
            if ( '' !== $locale ) {
                return $locale;
            }
        }

        if ( function_exists( 'get_locale' ) ) {
            $locale = sanitize_text_field( (string) get_locale() );
            if ( '' !== $locale ) {
                return $locale;
            }
        }

        return 'en_US';
    }

    private function is_technical_failure_code( $code ) {
        return in_array(
            $code,
            array(
                'server_unreachable',
                'ssl_connection_failed',
                'invalid_license_api_url',
                'http_license_url_blocked',
                'invalid_response',
                'json_parse_error',
                'connection_failed',
                'rate_limited',
            ),
            true
        );
    }

    private function sanitize_log_data( $data, $allow_package ) {
        if ( is_array( $data ) ) {
            $safe = array();
            foreach ( $data as $key => $value ) {
                $lower_key = is_string( $key ) ? strtolower( $key ) : $key;
                if ( $lower_key === 'license_key' && is_string( $value ) ) {
                    $safe[ $key ] = $this->mask_license_key( $value );
                    continue;
                }
                if ( ( $lower_key === 'package' || $lower_key === 'download_link' ) && is_string( $value ) ) {
                    $safe[ $key ] = $allow_package ? $value : '[redacted]';
                    continue;
                }
                $safe[ $key ] = $this->sanitize_log_data( $value, $allow_package );
            }
            return $safe;
        }
        if ( is_object( $data ) ) {
            return $this->sanitize_log_data( (array) $data, $allow_package );
        }
        if ( is_string( $data ) ) {
            $decoded = json_decode( $data, true );
            if ( is_array( $decoded ) ) {
                return $this->sanitize_log_data( $decoded, $allow_package );
            }
        }
        return $data;
    }
}
