<?php
/**
 * Polar-backed site licensing.
 *
 * @package WPAgentCompanion
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

define( 'WPAGENT_LICENSE_ORGANIZATION_ID', '3cff9825-51c6-4556-a814-4bddc33b9368' );
define(
	'WPAGENT_LICENSE_BENEFIT_IDS',
	array(
		'80812ddd-94b8-406f-96de-066b5049c79e', // Solo.
		'baeb20f5-6c5b-4719-9a77-6381c649921c', // Pro.
		'277cc71a-7614-4097-8abf-e6c4fff64cb7', // Agentur.
	)
);
define( 'WPAGENT_LICENSE_API_BASE', 'https://api.polar.sh/v1/customer-portal/license-keys' );
define( 'WPAGENT_LICENSE_OPTION', 'wp_agent_license_state' );
define( 'WPAGENT_LICENSE_LOCK_OPTION', 'wp_agent_license_mutation_lock' );
define( 'WPAGENT_LICENSE_LOCK_TTL', 5 * MINUTE_IN_SECONDS );
define( 'WPAGENT_LICENSE_CRON_HOOK', 'wpagent_companion_license_daily_check' );
define( 'WPAGENT_LICENSE_OFFLINE_GRACE', 7 * DAY_IN_SECONDS );
define( 'WPAGENT_LICENSE_RESPONSE_LIMIT', 256 * 1024 );

require_once __DIR__ . '/license-crypto.php';
require_once __DIR__ . '/license-storage.php';

/**
 * Stable activation identity. A subsite path counts as a separate site.
 */
function wpagent_companion_license_normalize_site_id( string $url ): string {
	$url = untrailingslashit( trim( $url ) );
	$normalized = preg_replace_callback(
		'/^([A-Za-z][A-Za-z0-9+.-]*):\/\/([^\/?#]+)/',
		static fn( array $match ): string => strtolower( $match[1] ) . '://' . strtolower( $match[2] ),
		$url
	);
	return substr( is_string( $normalized ) ? $normalized : $url, 0, 500 );
}

function wpagent_companion_license_site_id(): string {
	return wpagent_companion_license_normalize_site_id( home_url( '/' ) );
}

/**
 * Explicit local/CI bypass. Never enabled by the distributed plugin itself.
 */
function wpagent_companion_license_bypassed(): bool {
	return defined( 'WP_AGENT_LICENSE_DEV_BYPASS' ) && true === WP_AGENT_LICENSE_DEV_BYPASS;
}

/** @param array<string,mixed> $state */
function wpagent_companion_license_state_has_valid_structure( array $state, string $site_id ): bool {
	$allowed_benefits = apply_filters( 'wpagent_companion_license_benefit_ids', WPAGENT_LICENSE_BENEFIT_IDS );
	return 'valid' === ( $state['status'] ?? null )
		&& isset( $state['key'] )
		&& is_string( $state['key'] )
		&& strlen( $state['key'] ) >= 8
		&& strlen( $state['key'] ) <= 500
		&& ! preg_match( '/[\x00-\x1F\x7F]/', $state['key'] )
		&& isset( $state['activation_id'] )
		&& is_string( $state['activation_id'] )
		&& wp_is_uuid( $state['activation_id'] )
		&& isset( $state['site_id'] )
		&& is_string( $state['site_id'] )
		&& hash_equals( $state['site_id'], $site_id )
		&& isset( $state['benefit_id'] )
		&& is_string( $state['benefit_id'] )
		&& is_array( $allowed_benefits )
		&& in_array( $state['benefit_id'], $allowed_benefits, true )
		&& isset( $state['checked_at'] )
		&& is_int( $state['checked_at'] )
		&& array_key_exists( 'expires_at', $state )
		&& ( null === $state['expires_at'] || is_string( $state['expires_at'] ) )
		&& array_key_exists( 'limit_activations', $state )
		&& ( null === $state['limit_activations'] || ( is_int( $state['limit_activations'] ) && $state['limit_activations'] > 0 ) );
}

/**
 * Decide whether a stored proof is accepted at a specific point in time.
 *
 * This pure form keeps update-entitlement tests independent of the explicit
 * development bypass used by the local integration installation.
 *
 * @param array<string,mixed> $state   Stored license state.
 * @param int                 $now     Current Unix timestamp.
 * @param string              $site_id Expected normalized site identity.
 */
function wpagent_companion_license_state_is_valid( array $state, int $now, string $site_id ): bool {
	if ( ! wpagent_companion_license_state_has_valid_structure( $state, $site_id ) ) {
		return false;
	}

	if ( null !== $state['expires_at'] ) {
		$expires_at = strtotime( $state['expires_at'] );
		if ( false === $expires_at || $expires_at <= $now ) {
			return false;
		}
	}

	$checked_at = (int) ( $state['checked_at'] ?? 0 );
	return $checked_at > 0
		&& $checked_at <= $now + ( 5 * MINUTE_IN_SECONDS )
		&& $checked_at >= $now - WPAGENT_LICENSE_OFFLINE_GRACE;
}

/**
 * Decide whether cached proof is still accepted.
 */
function wpagent_companion_license_is_valid(): bool {
	if ( wpagent_companion_license_bypassed() ) {
		return true;
	}

	return wpagent_companion_license_state_is_valid(
		wpagent_companion_license_state(),
		time(),
		wpagent_companion_license_site_id()
	);
}

/**
 * Public status payload. Secrets and customer data are deliberately omitted.
 *
 * @return array<string,mixed>
 */
function wpagent_companion_license_public_status(): array {
	if ( wpagent_companion_license_bypassed() ) {
		return array(
			'licensed'          => true,
			'tier'              => 'paid',
			'companion_access'  => true,
			'status'            => 'development_bypass',
			'checked_at'        => null,
			'expires_at'        => null,
			'limit_activations' => null,
		);
	}

	$state      = wpagent_companion_license_state();
	$checked_at = (int) ( $state['checked_at'] ?? 0 );
	$licensed = wpagent_companion_license_is_valid();
	return array(
		'licensed'          => $licensed,
		'tier'              => $licensed ? 'paid' : 'free',
		'companion_access'  => true,
		'status'            => $licensed ? (string) ( $state['status'] ?? 'valid' ) : 'free',
		'paid_license_status' => (string) ( $state['status'] ?? 'not_activated' ),
		'checked_at'        => $checked_at > 0 ? gmdate( 'c', $checked_at ) : null,
		'expires_at'        => $state['expires_at'] ?? null,
		'limit_activations' => isset( $state['limit_activations'] ) ? (int) $state['limit_activations'] : null,
		'offline_grace_until' => $checked_at > 0 ? gmdate( 'c', $checked_at + WPAGENT_LICENSE_OFFLINE_GRACE ) : null,
	);
}

/**
 * Call one of Polar's unauthenticated customer-portal license endpoints.
 *
 * @param string              $action activate, validate or deactivate.
 * @param array<string,mixed> $body Request body.
 * @return array{code:int,body:array<string,mixed>|null}|WP_Error
 */
function wpagent_companion_license_request( string $action, array $body ) {
	$mock = apply_filters( 'wpagent_companion_license_http_response', null, $action, $body );
	if ( null !== $mock ) {
		return $mock;
	}

	$encoded_body = wp_json_encode( $body );
	if ( ! is_string( $encoded_body ) ) {
		return new WP_Error( 'wpagent_license_bad_request', __( 'Die Lizenzanfrage konnte nicht erstellt werden.', 'wp-agent-companion' ) );
	}

	$response = wp_safe_remote_post(
		WPAGENT_LICENSE_API_BASE . '/' . $action,
		array(
			'timeout'             => 12,
			'redirection'         => 0,
			'limit_response_size' => WPAGENT_LICENSE_RESPONSE_LIMIT,
			'headers'             => array( 'Content-Type' => 'application/json' ),
			'body'                => $encoded_body,
		)
	);
	if ( is_wp_error( $response ) ) {
		return new WP_Error( 'wpagent_license_network', __( 'Die Polar-Lizenzprüfung ist vorübergehend nicht erreichbar.', 'wp-agent-companion' ) );
	}

	$code = wp_remote_retrieve_response_code( $response );
	$raw  = wp_remote_retrieve_body( $response );
	if ( strlen( $raw ) >= WPAGENT_LICENSE_RESPONSE_LIMIT ) {
		return new WP_Error( 'wpagent_license_bad_response', __( 'Polar hat eine ungültige Lizenzantwort geliefert.', 'wp-agent-companion' ) );
	}
	$content_type = wp_remote_retrieve_header( $response, 'content-type' );
	if ( '' !== $raw && is_string( $content_type ) && '' !== $content_type ) {
		$media_type = strtolower( trim( explode( ';', $content_type, 2 )[0] ) );
		if ( 'application/json' !== $media_type && ! str_ends_with( $media_type, '+json' ) ) {
			return new WP_Error( 'wpagent_license_bad_response', __( 'Polar hat eine ungültige Lizenzantwort geliefert.', 'wp-agent-companion' ) );
		}
	}
	$data = '' === $raw ? null : json_decode( $raw, true );
	if ( '' !== $raw && ! is_array( $data ) ) {
		return new WP_Error( 'wpagent_license_bad_response', __( 'Polar hat eine ungültige Lizenzantwort geliefert.', 'wp-agent-companion' ) );
	}
	return array(
		'code' => (int) $code,
		'body' => is_array( $data ) ? $data : null,
	);
}

/**
 * Turn a successful Polar license object into the minimal local state.
 *
 * @param string              $key           Full license key.
 * @param string              $activation_id Polar activation UUID.
 * @param array<string,mixed> $license       Polar license object.
 * @return array<string,mixed>|WP_Error
 */
function wpagent_companion_license_build_state( string $key, string $activation_id, array $license ) {
	if ( WPAGENT_LICENSE_ORGANIZATION_ID !== ( $license['organization_id'] ?? '' ) ) {
		return new WP_Error( 'wpagent_license_wrong_organization', __( 'Der Lizenzschlüssel gehört nicht zu WPAgently.', 'wp-agent-companion' ) );
	}
	$allowed_benefits = apply_filters( 'wpagent_companion_license_benefit_ids', WPAGENT_LICENSE_BENEFIT_IDS );
	if ( ! is_array( $allowed_benefits ) || ! in_array( (string) ( $license['benefit_id'] ?? '' ), $allowed_benefits, true ) ) {
		return new WP_Error( 'wpagent_license_wrong_benefit', __( 'Der Lizenzschlüssel gehört nicht zu einem WPAgently-Tarif.', 'wp-agent-companion' ) );
	}
	if ( 'granted' !== ( $license['status'] ?? '' ) ) {
		return new WP_Error( 'wpagent_license_not_granted', __( 'Der Lizenzschlüssel ist nicht aktiv.', 'wp-agent-companion' ) );
	}

	$expires_at = $license['expires_at'] ?? null;
	if ( null !== $expires_at ) {
		if ( ! is_string( $expires_at ) || false === strtotime( $expires_at ) ) {
			return new WP_Error( 'wpagent_license_bad_response', __( 'Polar hat eine ungültige Lizenzantwort geliefert.', 'wp-agent-companion' ) );
		}
		if ( strtotime( $expires_at ) <= time() ) {
			return new WP_Error( 'wpagent_license_expired', __( 'Der Lizenzschlüssel ist abgelaufen.', 'wp-agent-companion' ) );
		}
	}

	return array(
		'key'               => $key,
		'activation_id'     => $activation_id,
		'status'            => 'valid',
		'checked_at'        => time(),
		'expires_at'        => is_string( $expires_at ) ? $expires_at : null,
		'benefit_id'        => (string) ( $license['benefit_id'] ?? '' ),
		'limit_activations' => isset( $license['limit_activations'] ) ? (int) $license['limit_activations'] : null,
		'site_id'           => wpagent_companion_license_site_id(),
	);
}

/**
 * Undo a remote activation and restore the exact local option snapshots.
 *
 * @param array<string,array{exists:bool,value:mixed}> $snapshots Local options before activation.
 * @return WP_Error
 */
function wpagent_companion_license_abort_activation( string $key, string $activation_id, array $snapshots, WP_Error $cause ): WP_Error {
	$local_restored = true;
	foreach ( $snapshots as $option => $snapshot ) {
		if ( ! wpagent_companion_license_option_restore( $option, $snapshot ) ) {
			$local_restored = false;
		}
	}
	if ( ! $local_restored ) {
		wpagent_companion_license_option_delete( WPAGENT_LICENSE_OPTION );
	}

	$response = wpagent_companion_license_request(
		'deactivate',
		array(
			'key'             => $key,
			'organization_id' => WPAGENT_LICENSE_ORGANIZATION_ID,
			'activation_id'   => $activation_id,
		)
	);
	$remote_released = ! is_wp_error( $response ) && in_array( $response['code'], array( 204, 404, 422 ), true );
	if ( $local_restored && $remote_released ) {
		return $cause;
	}

	return new WP_Error(
		'wpagent_license_compensation_failed',
		__( 'Die unvollständige Lizenzaktivierung konnte nicht vollständig zurückgesetzt werden. Bitte versuche die Deaktivierung erneut oder kontaktiere den Support.', 'wp-agent-companion' ),
		array(
			'cause'           => $cause->get_error_code(),
			'local_restored'  => $local_restored,
			'remote_released' => $remote_released,
		)
	);
}

/**
 * Activate a license for this exact WordPress site.
 *
 * @return array<string,mixed>|WP_Error
 */
function wpagent_companion_license_activate( string $key ) {
	$key = trim( $key );
	if ( strlen( $key ) < 8 || strlen( $key ) > 500 || preg_match( '/[\x00-\x1F\x7F]/', $key ) ) {
		return new WP_Error( 'wpagent_license_bad_key', __( 'Der Lizenzschlüssel hat kein gültiges Format.', 'wp-agent-companion' ) );
	}
	$lock = wpagent_companion_license_acquire_lock();
	if ( is_wp_error( $lock ) ) {
		return $lock;
	}
	try {
		return wpagent_companion_license_activate_locked( $key );
	} finally {
		wpagent_companion_license_release_lock( $lock );
	}
}

/** @return array<string,mixed>|WP_Error Expects the license mutation lock. */
function wpagent_companion_license_activate_locked( string $key ) {
	$existing = wpagent_companion_license_state();
	if ( ! empty( $existing['key'] ) && ! empty( $existing['activation_id'] ) ) {
		if ( hash_equals( (string) $existing['key'], $key ) ) {
			return wpagent_companion_license_refresh_locked();
		}
		return new WP_Error(
			'wpagent_license_activation_exists',
			__( 'Deaktiviere zuerst die bestehende Website-Aktivierung, bevor du einen anderen Lizenzschlüssel verwendest.', 'wp-agent-companion' )
		);
	}
	if ( empty( $existing['key'] ) && ! empty( $existing['activation_id'] ) ) {
		$state_snapshot = wpagent_companion_license_option_snapshot( WPAGENT_LICENSE_OPTION );
		$key_hash = is_string( $existing['key_hash'] ?? null ) ? $existing['key_hash'] : '';
		if ( 64 !== strlen( $key_hash ) || ! hash_equals( $key_hash, hash( 'sha256', $key ) ) ) {
			return new WP_Error(
				'wpagent_license_recovery_key_mismatch',
				__( 'Der gespeicherte Lizenzschlüssel kann nach einer Salt-Änderung nicht mehr entschlüsselt werden. Gib denselben Lizenzschlüssel erneut ein, um die bestehende Aktivierung sicher wiederherzustellen.', 'wp-agent-companion' )
			);
		}
		$response = wpagent_companion_license_request(
			'validate',
			array(
				'key'             => $key,
				'organization_id' => WPAGENT_LICENSE_ORGANIZATION_ID,
				'activation_id'   => $existing['activation_id'],
				'conditions'      => array( 'site_url' => wpagent_companion_license_site_id() ),
			)
		);
		if ( is_wp_error( $response ) ) {
			return $response;
		}
		if ( 200 !== $response['code'] || ! is_array( $response['body'] ) ) {
			return new WP_Error( 'wpagent_license_recovery_failed', __( 'Die bestehende Polar-Aktivierung konnte nicht wiederhergestellt werden.', 'wp-agent-companion' ) );
		}
		$activation = $response['body']['activation'] ?? null;
		if ( ! is_array( $activation ) || ! hash_equals( (string) $existing['activation_id'], (string) ( $activation['id'] ?? '' ) ) ) {
			return new WP_Error( 'wpagent_license_recovery_failed', __( 'Die bestehende Polar-Aktivierung konnte nicht eindeutig bestätigt werden.', 'wp-agent-companion' ) );
		}
		$recovered = wpagent_companion_license_build_state( $key, (string) $existing['activation_id'], $response['body'] );
		if ( is_wp_error( $recovered ) ) {
			return $recovered;
		}
		$saved = wpagent_companion_license_save( $recovered );
		if ( is_wp_error( $saved ) ) {
			$restored = wpagent_companion_license_option_restore( WPAGENT_LICENSE_OPTION, $state_snapshot );
			$saved->add_data(
				array(
					'previous_state_restored' => $restored,
					'failed_closed'           => $restored ? false : wpagent_companion_license_option_delete( WPAGENT_LICENSE_OPTION ),
				)
			);
			return $saved;
		}
		return $recovered;
	}

	$site_id  = wpagent_companion_license_site_id();
	$response = wpagent_companion_license_request(
		'activate',
		array(
			'key'             => $key,
			'organization_id' => WPAGENT_LICENSE_ORGANIZATION_ID,
			'label'           => $site_id,
			'conditions'      => array( 'site_url' => $site_id ),
			'meta'            => array( 'site_url' => $site_id, 'product' => 'wpagently' ),
		)
	);
	if ( is_wp_error( $response ) ) {
		return $response;
	}
	if ( 200 !== $response['code'] || ! is_array( $response['body'] ) ) {
		return new WP_Error( 'wpagent_license_activate_failed', __( 'Polar hat den Lizenzschlüssel abgelehnt.', 'wp-agent-companion' ) );
	}

	$snapshots = array(
		WPAGENT_LICENSE_OPTION                  => wpagent_companion_license_option_snapshot( WPAGENT_LICENSE_OPTION ),
		WPAGENT_COMPANION_OPT_BOUND_SITE       => wpagent_companion_license_option_snapshot( WPAGENT_COMPANION_OPT_BOUND_SITE ),
		WPAGENT_COMPANION_OPT_ENABLED          => wpagent_companion_license_option_snapshot( WPAGENT_COMPANION_OPT_ENABLED ),
	);
	$raw_activation_id = $response['body']['id'] ?? null;
	$activation_id     = is_string( $raw_activation_id ) ? $raw_activation_id : '';
	$license           = $response['body']['license_key'] ?? null;
	if ( ! wp_is_uuid( $activation_id ) || ! is_array( $license ) ) {
		$error = new WP_Error( 'wpagent_license_bad_response', __( 'Polar hat eine ungültige Lizenzantwort geliefert.', 'wp-agent-companion' ) );
		if ( '' !== $activation_id && strlen( $activation_id ) <= 500 && ! preg_match( '/[\x00-\x1F\x7F]/', $activation_id ) ) {
			return wpagent_companion_license_abort_activation( $key, $activation_id, $snapshots, $error );
		}
		return $error;
	}

	$state     = wpagent_companion_license_build_state( $key, $activation_id, $license );
	if ( is_wp_error( $state ) ) {
		return wpagent_companion_license_abort_activation( $key, $activation_id, $snapshots, $state );
	}

	$persist_error = wpagent_companion_license_persist_error();
	if ( ! wpagent_companion_license_option_write( WPAGENT_COMPANION_OPT_BOUND_SITE, wpagent_companion_license_site_id() )
		|| ! wpagent_companion_license_option_write( WPAGENT_COMPANION_OPT_ENABLED, '1' ) ) {
		return wpagent_companion_license_abort_activation( $key, $activation_id, $snapshots, $persist_error );
	}
	$saved = wpagent_companion_license_save( $state );
	if ( is_wp_error( $saved ) ) {
		return wpagent_companion_license_abort_activation( $key, $activation_id, $snapshots, $saved );
	}

	return $state;
}

/**
 * Refresh the stored license proof. Network errors preserve the previous proof.
 *
 * @return array<string,mixed>|WP_Error
 */
function wpagent_companion_license_refresh() {
	$lock = wpagent_companion_license_acquire_lock();
	if ( is_wp_error( $lock ) ) {
		return $lock;
	}
	try {
		return wpagent_companion_license_refresh_locked();
	} finally {
		wpagent_companion_license_release_lock( $lock );
	}
}

/** @return array<string,mixed>|WP_Error Expects the license mutation lock. */
function wpagent_companion_license_refresh_locked() {
	$state          = wpagent_companion_license_state();
	$state_snapshot = wpagent_companion_license_option_snapshot( WPAGENT_LICENSE_OPTION );
	if ( empty( $state['key'] ) || empty( $state['activation_id'] ) ) {
		return new WP_Error( 'wpagent_license_missing', __( 'Es ist noch keine WPAgently-Lizenz aktiviert.', 'wp-agent-companion' ) );
	}

	$response = wpagent_companion_license_request(
		'validate',
		array(
			'key'             => $state['key'],
			'organization_id' => WPAGENT_LICENSE_ORGANIZATION_ID,
			'activation_id'   => $state['activation_id'],
			'conditions'      => array( 'site_url' => wpagent_companion_license_site_id() ),
		)
	);
	if ( is_wp_error( $response ) ) {
		return $response;
	}
	if ( in_array( $response['code'], array( 404, 422 ), true ) ) {
		$state['status']     = 'invalid';
		$state['checked_at'] = time();
		$saved               = wpagent_companion_license_save( $state );
		if ( is_wp_error( $saved ) ) {
			$saved->add_data( array( 'failed_closed' => wpagent_companion_license_option_delete( WPAGENT_LICENSE_OPTION ) ) );
			return $saved;
		}
		return new WP_Error( 'wpagent_license_invalid', __( 'Die WPAgently-Lizenz ist nicht mehr gültig.', 'wp-agent-companion' ) );
	}
	if ( 200 !== $response['code'] || ! is_array( $response['body'] ) ) {
		return new WP_Error(
			'wpagent_license_temporarily_unavailable',
			__( 'Die Polar-Lizenzprüfung ist vorübergehend nicht verfügbar. Der zuletzt bestätigte Status bleibt bis zum Ende der Offline-Frist gültig.', 'wp-agent-companion' )
		);
	}

	$activation = $response['body']['activation'] ?? null;
	if ( ! is_array( $activation ) || ( $activation['id'] ?? '' ) !== $state['activation_id'] ) {
		$state['status']     = 'invalid';
		$state['checked_at'] = time();
		$saved               = wpagent_companion_license_save( $state );
		if ( is_wp_error( $saved ) ) {
			$saved->add_data( array( 'failed_closed' => wpagent_companion_license_option_delete( WPAGENT_LICENSE_OPTION ) ) );
			return $saved;
		}
		return new WP_Error( 'wpagent_license_activation_mismatch', __( 'Die Polar-Aktivierung passt nicht zu dieser Website.', 'wp-agent-companion' ) );
	}

	$updated = wpagent_companion_license_build_state( $state['key'], $state['activation_id'], $response['body'] );
	if ( is_wp_error( $updated ) ) {
		$state['status']     = 'invalid';
		$state['checked_at'] = time();
		$saved               = wpagent_companion_license_save( $state );
		if ( is_wp_error( $saved ) ) {
			$saved->add_data( array( 'failed_closed' => wpagent_companion_license_option_delete( WPAGENT_LICENSE_OPTION ) ) );
			return $saved;
		}
		return $updated;
	}
	$saved = wpagent_companion_license_save( $updated );
	if ( is_wp_error( $saved ) ) {
		$restored = wpagent_companion_license_option_restore( WPAGENT_LICENSE_OPTION, $state_snapshot );
		$saved->add_data(
			array(
				'previous_state_restored' => $restored,
				'failed_closed'           => $restored ? false : wpagent_companion_license_option_delete( WPAGENT_LICENSE_OPTION ),
			)
		);
		return $saved;
	}
	return $updated;
}

/**
 * Release the Polar activation and remove local license material.
 *
 * @return true|WP_Error
 */
function wpagent_companion_license_deactivate() {
	$lock = wpagent_companion_license_acquire_lock();
	if ( is_wp_error( $lock ) ) {
		return $lock;
	}
	try {
		return wpagent_companion_license_deactivate_locked();
	} finally {
		wpagent_companion_license_release_lock( $lock );
	}
}

/** @return true|WP_Error Expects the license mutation lock. */
function wpagent_companion_license_deactivate_locked() {
	$state = wpagent_companion_license_state();
	if ( array() === $state ) {
		return wpagent_companion_license_option_delete( WPAGENT_LICENSE_OPTION )
			? true
			: wpagent_companion_license_persist_error();
	}
	$has_key = isset( $state['key'] )
		&& is_string( $state['key'] )
		&& strlen( $state['key'] ) >= 8
		&& strlen( $state['key'] ) <= 500
		&& ! preg_match( '/[\x00-\x1F\x7F]/', $state['key'] );
	$has_activation_id = isset( $state['activation_id'] )
		&& is_string( $state['activation_id'] )
		&& wp_is_uuid( $state['activation_id'] );
	if ( ! $has_key || ! $has_activation_id ) {
		return new WP_Error(
			'wpagent_license_partial_state',
			__( 'Die lokale Lizenzaktivierung ist unvollständig. Die Daten wurden zur sicheren manuellen Wiederherstellung beibehalten.', 'wp-agent-companion' ),
			array( 'status' => 409 )
		);
	}

	$response = wpagent_companion_license_request(
		'deactivate',
		array(
			'key'             => $state['key'],
			'organization_id' => WPAGENT_LICENSE_ORGANIZATION_ID,
			'activation_id'   => $state['activation_id'],
		)
	);
	if ( is_wp_error( $response ) ) {
		return $response;
	}
	if ( in_array( $response['code'], array( 404, 422 ), true ) ) {
		// The activation was already removed or invalidated remotely. Keeping the
		// local key would trap the site and prevent activating a replacement.
		return wpagent_companion_license_option_delete( WPAGENT_LICENSE_OPTION )
			? true
			: wpagent_companion_license_persist_error();
	}
	if ( 204 !== $response['code'] ) {
		return new WP_Error( 'wpagent_license_deactivate_failed', __( 'Polar konnte die Website-Aktivierung nicht freigeben.', 'wp-agent-companion' ) );
	}

	return wpagent_companion_license_option_delete( WPAGENT_LICENSE_OPTION )
		? true
		: wpagent_companion_license_persist_error();
}

/** Daily cron callback. */
function wpagent_companion_license_daily_check(): void {
	if ( ! wpagent_companion_license_bypassed() && wpagent_companion_license_state() ) {
		wpagent_companion_license_refresh();
	}
}
add_action( WPAGENT_LICENSE_CRON_HOOK, 'wpagent_companion_license_daily_check' );
