<?php
/**
 * SSP (Server Side Proxy) Sync class
 *
 * Handles synchronization of CAPI tokens and configuration
 * from WooCommerce / Pixel Manager to the SweetCode SSP proxy.
 *
 * @since 1.57.0
 */

namespace SweetCode\Pixel_Manager;

use SweetCode\Pixel_Manager\Admin\Environment;

defined('ABSPATH') || exit; // Exit if accessed directly

class SSP_Sync {

	private static $instance;

	/**
	 * SSP API base URL (production default).
	 * Override locally with: define( 'PMW_SSP_API_BASE', 'http://localhost:8787' );
	 */
	const SSP_API_BASE = 'https://ssp.sweetcode.cloud';

	/**
	 * Get the effective SSP API base URL.
	 *
	 * Checks for a PMW_SSP_API_BASE constant (set in wp-config.php) first,
	 * which allows pointing the plugin at a local wrangler dev server.
	 *
	 * @return string
	 * @since 1.57.0
	 */
	private static function get_api_base() {
		if ( defined( 'PMW_SSP_API_BASE' ) ) {
			return PMW_SSP_API_BASE;
		}
		return self::SSP_API_BASE;
	}

	/**
	 * Action Scheduler hook for daily sync
	 */
	const DAILY_SYNC_HOOK = 'pmw_ssp_daily_sync';

	public static function get_instance() {
		if (is_null(self::$instance)) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	public function __construct() {
		$this->register_hooks();
	}

	/**
	 * Register hooks for automatic syncing.
	 *
	 * @since 1.57.0
	 */
	private function register_hooks() {

		// Auto-sync when PMW options are updated
		add_action('update_option_' . PMW_DB_OPTIONS_NAME, [ $this, 'on_options_updated' ], 10, 2);

		// Daily sync via Action Scheduler
		add_action(self::DAILY_SYNC_HOOK, [ __CLASS__, 'daily_sync_handler' ]);

		// Schedule daily sync if SSP is active and not already scheduled
		add_action('init', [ $this, 'maybe_schedule_daily_sync' ], 20);
	}

	/**
	 * Schedule the daily sync task if SSP is active.
	 *
	 * @since 1.57.0
	 */
	public function maybe_schedule_daily_sync() {

		if (!Options::is_ssp_active()) {

			// Unschedule if SSP is no longer active
			if (
				function_exists('as_has_scheduled_action')
				&& as_has_scheduled_action(self::DAILY_SYNC_HOOK)
			) {
				as_unschedule_all_actions(self::DAILY_SYNC_HOOK);
			}

			return;
		}

		if (
			function_exists('as_has_scheduled_action')
			&& !as_has_scheduled_action(self::DAILY_SYNC_HOOK)
		) {
			as_schedule_recurring_action(
				Helpers::datetime_string_to_unix_timestamp_in_local_timezone('today 3:15am'),
				DAY_IN_SECONDS,
				self::DAILY_SYNC_HOOK,
				[],
				'pmw_ssp',
				true
			);
		}
	}

	/**
	 * Handle the daily sync Action Scheduler task.
	 *
	 * @since 1.57.0
	 */
	public static function daily_sync_handler() {
		if (Options::is_ssp_active()) {
			self::push_config_to_ssp();
		}
	}

	/**
	 * Callback for when PMW options are updated.
	 * Compares old and new values for CAPI-related fields and triggers sync if they changed.
	 *
	 * @param array $old_value Old option value.
	 * @param array $new_value New option value.
	 *
	 * @since 1.57.0
	 */
	public function on_options_updated( $old_value, $new_value ) {

		// Only sync if SSP is active
		if (empty($new_value['ssp']['enabled']) || empty($new_value['ssp']['sync_token'])) {
			return;
		}

		// Check if any CAPI-related settings changed
		$capi_keys = self::get_capi_option_paths();
		$changed   = false;

		foreach ($capi_keys as $path) {
			$old_val = self::array_get_nested($old_value, $path);
			$new_val = self::array_get_nested($new_value, $path);

			if ($old_val !== $new_val) {
				$changed = true;
				break;
			}
		}

		if ($changed) {

			Logger::info('SSP: CAPI settings changed, triggering config sync.');

			// Fire async so we don't block the options save
			if (function_exists('as_enqueue_async_action')) {
				as_enqueue_async_action(self::DAILY_SYNC_HOOK, [], 'pmw_ssp');
			} else {
				self::push_config_to_ssp();
			}
		}
	}

	/**
	 * Returns an array of dot-notation paths to CAPI-related option fields.
	 *
	 * @return string[]
	 * @since 1.57.0
	 */
	private static function get_capi_option_paths() {
		return [
			'facebook.pixel_id',
			'facebook.capi.token',
			'facebook.capi.test_event_code',
			'tiktok.pixel_id',
			'tiktok.eapi.token',
			'tiktok.eapi.test_event_code',
			'pinterest.pixel_id',
			'pinterest.ad_account_id',
			'pinterest.apic.token',
			'snapchat.pixel_id',
			'snapchat.capi.token',
			'pixels.reddit.advertiser_id',
			'pixels.reddit.capi.token',
			'pixels.reddit.capi.test_event_code',
			'google.analytics.ga4.measurement_id',
			'google.analytics.ga4.api_secret',
		];
	}

	/**
	 * Get a nested value from an array using dot notation.
	 *
	 * @param array  $array The array to traverse.
	 * @param string $path  Dot-notation path (e.g. "facebook.capi.token").
	 *
	 * @return mixed|null
	 * @since 1.57.0
	 */
	private static function array_get_nested( $array, $path ) {
		$keys = explode('.', $path);

		foreach ($keys as $key) {
			if (!is_array($array) || !isset($array[$key])) {
				return null;
			}
			$array = $array[$key];
		}

		return $array;
	}

	/**
	 * Fetch domain info from the SSP using the sync token.
	 *
	 * Calls GET /v1/sync/domain-info on the SSP API.
	 *
	 * @param string $sync_token The domain sync token.
	 *
	 * @return array|\WP_Error Response data array on success, WP_Error on failure.
	 * @since 1.57.0
	 */
	public static function fetch_domain_info( $sync_token ) {

		$response = wp_remote_get(
			self::get_api_base() . '/v1/sync/domain-info',
			[
				'headers' => [
					'Authorization' => 'Bearer ' . $sync_token,
					'Content-Type'  => 'application/json',
				],
				'timeout' => 15,
			]
		);

		if (is_wp_error($response)) {
			Logger::error('SSP: Failed to fetch domain info: ' . $response->get_error_message());
			return $response;
		}

		$code = wp_remote_retrieve_response_code($response);
		$body = json_decode(wp_remote_retrieve_body($response), true);

		if (200 !== $code) {
			$message = isset($body['error']['message']) ? $body['error']['message'] : 'Unknown error (HTTP ' . $code . ')';
			Logger::error('SSP: Failed to fetch domain info: ' . $message);
			return new \WP_Error('ssp_api_error', $message, [ 'status' => $code ]);
		}

		// Unwrap the {success, data: {...}} envelope
		return isset($body['data']) ? $body['data'] : $body;
	}

	/**
	 * Register the callback URL with the SSP.
	 *
	 * Calls POST /v1/sync/register-callback on the SSP API.
	 *
	 * @param string $sync_token            The domain sync token.
	 * @param string $hostname              The proxy hostname.
	 * @param string $callback_url          The callback URL for resync.
	 * @param string $resync_callback_token The resync callback token.
	 *
	 * @return true|\WP_Error True on success, WP_Error on failure.
	 * @since 1.57.0
	 */
	public static function register_callback( $sync_token, $hostname, $callback_url, $resync_callback_token ) {

		$response = wp_remote_post(
			self::get_api_base() . '/v1/sync/register-callback',
			[
				'headers' => [
					'Authorization' => 'Bearer ' . $sync_token,
					'Content-Type'  => 'application/json',
				],
				'body'    => wp_json_encode([
					'hostname'            => $hostname,
					'callbackUrl'         => $callback_url,
					'resyncCallbackToken' => $resync_callback_token,
				]),
				'timeout' => 15,
			]
		);

		if (is_wp_error($response)) {
			Logger::error('SSP: Failed to register callback: ' . $response->get_error_message());
			return $response;
		}

		$code = wp_remote_retrieve_response_code($response);

		if ($code < 200 || $code >= 300) {
			$body    = json_decode(wp_remote_retrieve_body($response), true);
			$message = isset($body['error']['message']) ? $body['error']['message'] : 'Unknown error (HTTP ' . $code . ')';
			Logger::error('SSP: Failed to register callback: ' . $message);
			return new \WP_Error('ssp_callback_error', $message, [ 'status' => $code ]);
		}

		return true;
	}

	/**
	 * Push the current CAPI configuration to the SSP.
	 *
	 * Calls POST /v1/sync/domain-config on the SSP API.
	 *
	 * @param string|null $sync_token Optional. Override the stored sync token.
	 *
	 * @return true|\WP_Error True on success, WP_Error on failure.
	 * @since 1.57.0
	 */
	public static function push_config_to_ssp( $sync_token = null ) {

		$options = Options::get_options();

		if (is_null($sync_token)) {
			$sync_token = isset($options['ssp']['sync_token']) ? $options['ssp']['sync_token'] : '';
		}

		if (empty($sync_token)) {
			return new \WP_Error('ssp_no_token', 'No SSP sync token configured.');
		}

		$hostname = isset($options['ssp']['proxy_hostname']) ? $options['ssp']['proxy_hostname'] : '';

		if (empty($hostname)) {
			return new \WP_Error('ssp_no_hostname', 'No SSP proxy hostname configured.');
		}

		$destinations = self::get_destination_configs($options);

		$payload = [
			'hostname'     => $hostname,
			'shopOrigin'   => site_url(),
			'destinations' => $destinations,
		];

		// Add optional metadata
		$payload['pluginVersion'] = PMW_CURRENT_VERSION;

		if (function_exists('WC')) {
			$payload['wooVersion'] = WC()->version;
		}

		$payload['phpVersion'] = phpversion();

		$response = wp_remote_post(
			self::get_api_base() . '/v1/sync/domain-config',
			[
				'headers' => [
					'Authorization' => 'Bearer ' . $sync_token,
					'Content-Type'  => 'application/json',
				],
				'body'    => wp_json_encode($payload),
				'timeout' => 15,
			]
		);

		if (is_wp_error($response)) {
			Logger::error('SSP: Config push failed: ' . $response->get_error_message());
			self::update_sync_status(null, $response->get_error_message());
			return $response;
		}

		$code = wp_remote_retrieve_response_code($response);

		if ($code < 200 || $code >= 300) {
			$body    = json_decode(wp_remote_retrieve_body($response), true);
			$message = isset($body['error']['message']) ? $body['error']['message'] : 'Unknown error (HTTP ' . $code . ')';
			Logger::error('SSP: Config push failed: ' . $message);
			self::update_sync_status(null, $message);
			return new \WP_Error('ssp_config_push_error', $message, [ 'status' => $code ]);
		}

		// Capture verification key + plan/usage data from response
		$response_body = json_decode(wp_remote_retrieve_body($response), true);
		$data          = isset($response_body['data']) ? $response_body['data'] : [];

		$options = Options::get_options();
		$updated = false;

		if (!empty($data['verificationKey'])) {
			$options['ssp']['verification_key'] = $data['verificationKey'];
			$updated = true;
			Logger::info('SSP: Verification key updated.');
		}

		// Update routing and config status from the SSP
		if (isset($data['routingStatus'])) {
			$options['ssp']['routing_status'] = $data['routingStatus'];
			$updated = true;
		}

		if (isset($data['configStatus'])) {
			$options['ssp']['config_status'] = $data['configStatus'];
			$updated = true;
		}

		// Store plan and usage info returned by the SSP
		if (isset($data['planName'])) {
			$options['ssp']['plan_name']             = $data['planName'];
			$options['ssp']['subscription_status']   = isset($data['subscriptionStatus']) ? $data['subscriptionStatus'] : '';
			$options['ssp']['usage_percent']         = isset($data['usagePercent']) ? $data['usagePercent'] : 0;
			$options['ssp']['monthly_request_limit'] = isset($data['monthlyRequestLimit']) ? $data['monthlyRequestLimit'] : 0;
			$options['ssp']['billable_this_period']  = isset($data['billableThisPeriod']) ? $data['billableThisPeriod'] : 0;
			$options['ssp']['quota_exceeded']        = ! empty( $data['quotaExceeded'] );
			$updated = true;
		}

		if ($updated) {
			// Use direct update to avoid triggering our own hook
			remove_action('update_option_' . PMW_DB_OPTIONS_NAME, [ self::get_instance(), 'on_options_updated' ], 10);
			update_option(PMW_DB_OPTIONS_NAME, $options);
			add_action('update_option_' . PMW_DB_OPTIONS_NAME, [ self::get_instance(), 'on_options_updated' ], 10, 2);
			Options::invalidate_cache();
		}

		Logger::info('SSP: Config push successful.');
		self::update_sync_status(time(), '');

		return true;
	}

	/**
	 * Build the destination configuration array from PMW options.
	 *
	 * Maps each pixel's CAPI tokens to the SSP destination format.
	 *
	 * @param array $options PMW options array.
	 *
	 * @return array Array of destination config objects.
	 * @since 1.57.0
	 */
	public static function get_destination_configs( $options ) {
		$destinations = [];

		// Facebook CAPI
		if (!empty($options['facebook']['capi']['token']) && !empty($options['facebook']['pixel_id'])) {
			$dest = [
				'type'   => 'facebook',
				'config' => [
					'accessToken' => $options['facebook']['capi']['token'],
					'pixelId'     => $options['facebook']['pixel_id'],
				],
			];

			if (!empty($options['facebook']['capi']['test_event_code'])) {
				$dest['config']['testEventCode'] = $options['facebook']['capi']['test_event_code'];
			}

			$destinations[] = $dest;
		}

		// TikTok EAPI
		if (!empty($options['tiktok']['eapi']['token']) && !empty($options['tiktok']['pixel_id'])) {
			$dest = [
				'type'   => 'tiktok',
				'config' => [
					'accessToken' => $options['tiktok']['eapi']['token'],
					'pixelId'     => $options['tiktok']['pixel_id'],
				],
			];

			if (!empty($options['tiktok']['eapi']['test_event_code'])) {
				$dest['config']['testEventCode'] = $options['tiktok']['eapi']['test_event_code'];
			}

			$destinations[] = $dest;
		}

		// Pinterest APIC
		if (!empty($options['pinterest']['apic']['token']) && !empty($options['pinterest']['pixel_id'])) {
			$dest = [
				'type'   => 'pinterest',
				'config' => [
					'accessToken' => $options['pinterest']['apic']['token'],
					'pixelId'     => $options['pinterest']['pixel_id'],
				],
			];

			if (!empty($options['pinterest']['ad_account_id'])) {
				$dest['config']['adAccountId'] = $options['pinterest']['ad_account_id'];
			}

			$destinations[] = $dest;
		}

		// Snapchat CAPI
		if (!empty($options['snapchat']['capi']['token']) && !empty($options['snapchat']['pixel_id'])) {
			$destinations[] = [
				'type'   => 'snapchat',
				'config' => [
					'accessToken' => $options['snapchat']['capi']['token'],
					'pixelId'     => $options['snapchat']['pixel_id'],
				],
			];
		}

		// Reddit CAPI
		if (!empty($options['pixels']['reddit']['capi']['token']) && !empty($options['pixels']['reddit']['advertiser_id'])) {
			$dest = [
				'type'   => 'reddit',
				'config' => [
					'accessToken' => $options['pixels']['reddit']['capi']['token'],
					'pixelId'     => $options['pixels']['reddit']['advertiser_id'],
				],
			];

			if (!empty($options['pixels']['reddit']['capi']['test_event_code'])) {
				$dest['config']['testEventCode'] = $options['pixels']['reddit']['capi']['test_event_code'];
			}

			$destinations[] = $dest;
		}

		// Google GA4 is excluded from SSP sync.
		// GA4 Measurement Protocol only sends purchase events (server-to-server),
		// and the SSP only proxies non-purchase events. Purchase events continue
		// to be sent directly from WooCommerce.

		return $destinations;
	}

	/**
	 * Update the SSP sync status in options.
	 *
	 * @param int|null    $timestamp Unix timestamp of last successful sync, or null on failure.
	 * @param string      $error     Error message, empty on success.
	 *
	 * @since 1.57.0
	 */
	private static function update_sync_status( $timestamp, $error ) {
		$options = get_option(PMW_DB_OPTIONS_NAME);

		if (!$options) {
			return;
		}

		if (!is_null($timestamp)) {
			$options['ssp']['last_sync_at']    = $timestamp;
			$options['ssp']['config_status']   = 'synced';
		}

		if (!empty($error)) {
			$options['ssp']['config_status']   = 'sync_error';
		}

		$options['ssp']['last_sync_error'] = $error;

		// Use direct update to avoid triggering our own hook
		remove_action('update_option_' . PMW_DB_OPTIONS_NAME, [ self::get_instance(), 'on_options_updated' ], 10);
		update_option(PMW_DB_OPTIONS_NAME, $options);
		add_action('update_option_' . PMW_DB_OPTIONS_NAME, [ self::get_instance(), 'on_options_updated' ], 10, 2);

		Options::invalidate_cache();
	}

	/**
	 * Run the full SSP activation flow.
	 *
	 * 1. Fetch domain info from SSP
	 * 2. Generate resync callback token
	 * 3. Register callback with SSP
	 * 4. Push config to SSP
	 * 5. Store all SSP options
	 *
	 * @param string $sync_token The domain sync token.
	 *
	 * @return array|\WP_Error Domain info array on success, WP_Error on failure.
	 * @since 1.57.0
	 */
	public static function activate( $sync_token ) {

		// Step 1: Fetch domain info
		$domain_info = self::fetch_domain_info($sync_token);

		if (is_wp_error($domain_info)) {
			return $domain_info;
		}

		if (empty($domain_info['domain']['hostname'])) {
			return new \WP_Error('ssp_invalid_response', 'Invalid domain info response from SSP.');
		}

		$hostname = $domain_info['domain']['hostname'];

		// Step 2: Generate resync callback token
		$resync_callback_token = wp_generate_password(64, false);

		// Step 3: Register callback with SSP
		$callback_url = rest_url('pmw/v1/ssp/resync');
		$result       = self::register_callback($sync_token, $hostname, $callback_url, $resync_callback_token);

		if (is_wp_error($result)) {
			return $result;
		}

		// Step 4: Store SSP options
		$options = get_option(PMW_DB_OPTIONS_NAME);

		$options['ssp']['sync_token']            = $sync_token;
		$options['ssp']['enabled']               = true;
		$options['ssp']['proxy_hostname']        = $hostname;
		$options['ssp']['domain_id']             = isset($domain_info['domain']['id']) ? $domain_info['domain']['id'] : '';
		$options['ssp']['routing_status']        = isset($domain_info['domain']['routingStatus']) ? $domain_info['domain']['routingStatus'] : '';
		$options['ssp']['config_status']         = isset($domain_info['domain']['configStatus']) ? $domain_info['domain']['configStatus'] : '';
		$options['ssp']['resync_callback_token'] = $resync_callback_token;

		// Store plan and usage info from SSP
		$options['ssp']['plan_name']             = isset($domain_info['domain']['planName']) ? $domain_info['domain']['planName'] : '';
		$options['ssp']['subscription_status']   = isset($domain_info['domain']['subscriptionStatus']) ? $domain_info['domain']['subscriptionStatus'] : '';
		$options['ssp']['usage_percent']         = isset($domain_info['domain']['usagePercent']) ? $domain_info['domain']['usagePercent'] : 0;
		$options['ssp']['monthly_request_limit'] = isset($domain_info['domain']['monthlyRequestLimit']) ? $domain_info['domain']['monthlyRequestLimit'] : 0;
		$options['ssp']['billable_this_period']  = isset($domain_info['domain']['billableThisPeriod']) ? $domain_info['domain']['billableThisPeriod'] : 0;

		// Use direct update to avoid triggering our own hook
		remove_action('update_option_' . PMW_DB_OPTIONS_NAME, [ self::get_instance(), 'on_options_updated' ], 10);
		update_option(PMW_DB_OPTIONS_NAME, $options);
		add_action('update_option_' . PMW_DB_OPTIONS_NAME, [ self::get_instance(), 'on_options_updated' ], 10, 2);

		Options::invalidate_cache();

		// Step 5: Push config to SSP
		$push_result = self::push_config_to_ssp($sync_token);

		if (is_wp_error($push_result)) {
			// Non-fatal: domain is connected but config push failed
			Logger::warning('SSP: Activation succeeded but initial config push failed: ' . $push_result->get_error_message());
		}

		return $domain_info;
	}

	/**
	 * Disable SSP without clearing connection state.
	 *
	 * Sets enabled to false and unschedules the daily sync task,
	 * but preserves the sync token, proxy hostname, domain ID, and
	 * all other connection data so it can be re-enabled without
	 * re-entering the token.
	 *
	 * @since 1.57.0
	 */
	public static function disable() {

		$options = get_option(PMW_DB_OPTIONS_NAME);

		$options['ssp']['enabled'] = false;

		// Use direct update to avoid triggering our own hook
		remove_action('update_option_' . PMW_DB_OPTIONS_NAME, [ self::get_instance(), 'on_options_updated' ], 10);
		update_option(PMW_DB_OPTIONS_NAME, $options);
		add_action('update_option_' . PMW_DB_OPTIONS_NAME, [ self::get_instance(), 'on_options_updated' ], 10, 2);

		Options::invalidate_cache();

		// Unschedule daily sync
		if (function_exists('as_unschedule_all_actions')) {
			as_unschedule_all_actions(self::DAILY_SYNC_HOOK);
		}

		Logger::info('SSP: Disabled (connection state preserved).');
	}

	/**
	 * Re-enable SSP using the preserved connection state.
	 *
	 * Sets enabled back to true and re-schedules the daily sync.
	 * If routing/config status are still valid, SSP becomes active
	 * immediately. If statuses are stale, triggers a config re-sync.
	 *
	 * @return true|\WP_Error True on success, WP_Error if no token is stored.
	 * @since 1.57.0
	 */
	public static function enable() {

		$options = get_option(PMW_DB_OPTIONS_NAME);

		if ( empty( $options['ssp']['sync_token'] ) ) {
			return new \WP_Error(
				'ssp_no_token',
				'Cannot enable SSP: no sync token is stored. Please activate with a sync token first.',
				[ 'status' => 400 ]
			);
		}

		$options['ssp']['enabled'] = true;

		// Use direct update to avoid triggering our own hook
		remove_action('update_option_' . PMW_DB_OPTIONS_NAME, [ self::get_instance(), 'on_options_updated' ], 10);
		update_option(PMW_DB_OPTIONS_NAME, $options);
		add_action('update_option_' . PMW_DB_OPTIONS_NAME, [ self::get_instance(), 'on_options_updated' ], 10, 2);

		Options::invalidate_cache();

		// Re-schedule daily sync
		self::get_instance()->maybe_schedule_daily_sync();

		Logger::info('SSP: Enabled.');

		// If statuses look stale, trigger a config re-sync
		$routing = isset($options['ssp']['routing_status']) ? $options['ssp']['routing_status'] : '';
		$config  = isset($options['ssp']['config_status']) ? $options['ssp']['config_status'] : '';

		if ( 'active' !== $routing || 'synced' !== $config ) {
			Logger::info('SSP: Statuses may be stale, triggering config re-sync.');
			self::push_config_to_ssp();
		}

		return true;
	}

	/**
	 * Fully disconnect SSP and clear all stored state.
	 *
	 * Removes the sync token and all connection data. The user will
	 * need to re-enter the token from the SSP portal to reconnect.
	 *
	 * @since 1.57.0
	 */
	public static function disconnect() {

		$options = get_option(PMW_DB_OPTIONS_NAME);

		$options['ssp'] = [
			'sync_token'              => '',
			'enabled'                 => false,
			'proxy_hostname'          => '',
			'domain_id'               => '',
			'routing_status'          => '',
			'config_status'           => '',
			'last_sync_at'            => 0,
			'last_sync_error'         => '',
			'resync_callback_token'   => '',
			'verification_key'        => '',
			'proxy_failure_behavior'  => 'fallback_to_wc',
			'plan_name'               => '',
			'subscription_status'     => '',
			'usage_percent'           => 0,
			'monthly_request_limit'   => 0,
			'billable_this_period'    => 0,
		];

		update_option(PMW_DB_OPTIONS_NAME, $options);
		Options::invalidate_cache();

		// Unschedule daily sync
		if (function_exists('as_unschedule_all_actions')) {
			as_unschedule_all_actions(self::DAILY_SYNC_HOOK);
		}

		Logger::info('SSP: Disconnected and cleared all SSP settings.');
	}

	/**
	 * Deactivate SSP and clear all stored state.
	 *
	 * @deprecated 1.57.0 Use disconnect() instead.
	 * @since 1.57.0
	 */
	public static function deactivate() {
		self::disconnect();
	}

	/**
	 * Test the SSP proxy connectivity.
	 *
	 * @return array|\WP_Error Health check result or error.
	 * @since 1.57.0
	 */
	public static function test_connection() {

		$hostname = Options::get_ssp_proxy_hostname();

		if (empty($hostname)) {
			return new \WP_Error('ssp_no_hostname', 'No SSP proxy hostname configured.');
		}

		// In dev, use the overridden events URL; in production, use the proxy hostname
		$url = defined( 'PMW_SSP_EVENTS_URL' )
			? PMW_SSP_EVENTS_URL
			: 'https://' . $hostname . '/v1/events';

		$response = wp_remote_get(
			$url,
			[
				'timeout' => 10,
				'headers' => [
					'Accept' => 'application/json',
				],
			]
		);

		if (is_wp_error($response)) {
			return $response;
		}

		$code = wp_remote_retrieve_response_code($response);

		// The OPTIONS/GET to the events endpoint should respond (even with 405 Method Not Allowed)
		// A response means the proxy is reachable
		return [
			'reachable'   => $code > 0,
			'status_code' => $code,
			'hostname'    => $hostname,
		];
	}

	/**
	 * Get the current SSP status for the admin UI.
	 *
	 * @return array
	 * @since 1.57.0
	 */
	public static function get_status() {

		$options = Options::get_options();
		$ssp     = isset($options['ssp']) ? $options['ssp'] : [];

		$last_sync_at = isset($ssp['last_sync_at']) ? $ssp['last_sync_at'] : 0;

		return [
			'enabled'                 => !empty($ssp['enabled']),
			'has_token'               => !empty($ssp['sync_token']),
			'proxy_hostname'          => isset($ssp['proxy_hostname']) ? $ssp['proxy_hostname'] : '',
			'domain_id'               => isset($ssp['domain_id']) ? $ssp['domain_id'] : '',
			'routing_status'          => isset($ssp['routing_status']) ? $ssp['routing_status'] : '',
			'config_status'           => isset($ssp['config_status']) ? $ssp['config_status'] : '',
			'last_sync_at'            => $last_sync_at,
			'last_sync_at_formatted'  => $last_sync_at
				? wp_date(get_option('date_format') . ', ' . get_option('time_format'), $last_sync_at)
				: '',
			'last_sync_error'         => isset($ssp['last_sync_error']) ? $ssp['last_sync_error'] : '',
			'is_active'               => Options::is_ssp_active(),
			'plan_name'               => isset($ssp['plan_name']) ? $ssp['plan_name'] : '',
			'subscription_status'     => isset($ssp['subscription_status']) ? $ssp['subscription_status'] : '',
			'usage_percent'           => isset($ssp['usage_percent']) ? $ssp['usage_percent'] : 0,
			'monthly_request_limit'   => isset($ssp['monthly_request_limit']) ? $ssp['monthly_request_limit'] : 0,
			'billable_this_period'    => isset($ssp['billable_this_period']) ? $ssp['billable_this_period'] : 0,
		];
	}
}
