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

/**
 * Class Podcastmaker_License
 *
 * Handles license tier detection (Basic/Pro/Business) and online validation.
 */
class Podcastmaker_License
{
	/**
	 * In-request cache for verified license payloads.
	 *
	 * @var array<string, array|false>
	 */
	private static $runtime_license_cache = array();

	/**
	 * In-request cache for verification errors.
	 *
	 * @var array<string, string>
	 */
	private static $runtime_license_error_cache = array();

	/**
	 * Normalize a license key value before verification.
	 *
	 * @param string $license_key Raw license key value.
	 * @return string
	 */
	private static function normalize_license_key($license_key)
	{
		return trim((string) $license_key);
	}

	/**
	 * Normalize the active plan from the remote payload and license key prefix.
	 *
	 * @param mixed  $payload_plan Raw plan value from the verification payload.
	 * @return string
	 */
	private static function normalize_license_plan($payload_plan)
	{
		$plan = sanitize_key((string) $payload_plan);
		if ($plan === 'biz') {
			$plan = 'business';
		}

		return in_array($plan, array('basic', 'pro', 'business'), true) ? $plan : '';
	}

	/**
	 * Build the remote verification endpoint.
	 *
	 * @return string
	 */
	private static function get_verification_endpoint()
	{
		$default_endpoint = 'https://quikvox-ai.com/license/verify';
		$endpoint = apply_filters('podcastmaker_license_verify_endpoint', $default_endpoint);

		return esc_url_raw((string) $endpoint);
	}

	/**
	 * Build the remote verification timeout.
	 *
	 * @return int
	 */
	private static function get_verification_timeout()
	{
		return max(3, (int) apply_filters('podcastmaker_license_verify_timeout', 10));
	}

	/**
	 * Get the option name used to persist the license state snapshot.
	 *
	 * @return string
	 */
	private static function get_license_state_option_name()
	{
		return 'podcastmaker_license_state';
	}

	/**
	 * Build the default license state payload.
	 *
	 * @return array<string, mixed>
	 */
	private static function get_default_license_state()
	{
		return array(
			'license_key' => '',
			'status' => 'unknown',
			'plan' => 'basic',
			'checked_at' => '',
			'next_check_at' => '',
			'expires_at' => '',
			'failed_check_count' => 0,
			'grace_until' => '',
			'message' => '',
			'last_error' => '',
			'is_expired' => false,
			'expires' => '',
			'issued' => '',
			'verified_at' => '',
		);
	}

	/**
	 * Normalize a saved license state payload.
	 *
	 * @param mixed $state Raw state option value.
	 * @return array<string, mixed>
	 */
	private static function normalize_license_state($state)
	{
		$normalized_state = self::get_default_license_state();
		if (!is_array($state)) {
			return $normalized_state;
		}

		$license_key = isset($state['license_key']) ? self::normalize_license_key($state['license_key']) : '';
		$status = isset($state['status']) ? sanitize_key((string) $state['status']) : 'unknown';
		if (!in_array($status, array('active', 'inactive', 'expired', 'invalid', 'unknown'), true)) {
			$status = 'unknown';
		}

		$plan = isset($state['plan']) ? sanitize_key((string) $state['plan']) : 'basic';
		if (!in_array($plan, array('basic', 'pro', 'business'), true)) {
			$plan = 'basic';
		}

		$checked_at = isset($state['checked_at']) ? sanitize_text_field((string) $state['checked_at']) : '';
		$next_check_at = isset($state['next_check_at']) ? sanitize_text_field((string) $state['next_check_at']) : '';
		$expires_at = isset($state['expires_at']) ? sanitize_text_field((string) $state['expires_at']) : '';
		$failed_check_count = isset($state['failed_check_count']) ? absint($state['failed_check_count']) : 0;
		$grace_until = isset($state['grace_until']) ? sanitize_text_field((string) $state['grace_until']) : '';
		$message = isset($state['message']) ? sanitize_text_field((string) $state['message']) : '';
		$last_error = isset($state['last_error']) ? sanitize_text_field((string) $state['last_error']) : '';

		$normalized_state['license_key'] = $license_key;
		$normalized_state['status'] = $status;
		$normalized_state['plan'] = $plan;
		$normalized_state['checked_at'] = $checked_at;
		$normalized_state['next_check_at'] = $next_check_at;
		$normalized_state['expires_at'] = $expires_at;
		$normalized_state['failed_check_count'] = $failed_check_count;
		$normalized_state['grace_until'] = $grace_until;
		$normalized_state['message'] = $message;
		$normalized_state['last_error'] = $last_error;
		$normalized_state['is_expired'] = $status === 'expired';
		$normalized_state['expires'] = $expires_at;
		$normalized_state['issued'] = $checked_at;
		$normalized_state['verified_at'] = $checked_at;

		return $normalized_state;
	}

	/**
	 * Read the stored license state snapshot.
	 *
	 * @return array<string, mixed>
	 */
	private static function get_saved_license_state()
	{
		$state = get_option(self::get_license_state_option_name(), array());
		return self::normalize_license_state($state);
	}

	/**
	 * Persist the license state snapshot.
	 *
	 * @param array<string, mixed> $state The state snapshot to save.
	 * @return array<string, mixed>
	 */
	private static function save_license_state(array $state)
	{
		$normalized_state = self::normalize_license_state($state);
		update_option(self::get_license_state_option_name(), $normalized_state, false);
		return $normalized_state;
	}

	/**
	 * Determine whether a recheck is due for the stored state.
	 *
	 * @param array<string, mixed> $state The stored state snapshot.
	 * @return bool
	 */
	private static function is_license_recheck_due(array $state)
	{
		$next_check_at = isset($state['next_check_at']) ? (string) $state['next_check_at'] : '';
		if ($next_check_at === '') {
			return true;
		}

		$next_timestamp = strtotime($next_check_at);
		if ($next_timestamp === false) {
			return true;
		}

		return $next_timestamp <= current_time('timestamp');
	}

	/**
	 * Calculate the next check timestamp in the site timezone.
	 *
	 * @param int $from_timestamp Base timestamp.
	 * @return string
	 */
	private static function build_next_check_at($from_timestamp)
	{
		return wp_date('Y-m-d H:i:s', $from_timestamp + (30 * DAY_IN_SECONDS));
	}

	/**
	 * Calculate the grace period timestamp in the site timezone.
	 *
	 * @param int $from_timestamp Base timestamp.
	 * @return string
	 */
	private static function build_grace_until($from_timestamp)
	{
		return wp_date('Y-m-d H:i:s', $from_timestamp + (37 * DAY_IN_SECONDS));
	}

	/**
	 * Build a license state snapshot from a successful verification response.
	 *
	 * @param string $license_key The license key that was verified.
	 * @param array  $verification_info Normalized verification payload.
	 * @return array<string, mixed>
	 */
	private static function build_state_from_success($license_key, array $verification_info)
	{
		$checked_timestamp = current_time('timestamp');
		$next_check_at = self::build_next_check_at($checked_timestamp);
		$status = !empty($verification_info['is_expired']) ? 'expired' : 'active';
		$grace_until = !empty($verification_info['grace_until']) ? sanitize_text_field((string) $verification_info['grace_until']) : '';
		if ($grace_until === '') {
			$grace_until = $status === 'active' ? self::build_grace_until($checked_timestamp) : wp_date('Y-m-d H:i:s', $checked_timestamp);
		}

		$state = array(
			'license_key' => $license_key,
			'status' => $status,
			'plan' => isset($verification_info['plan']) ? sanitize_key((string) $verification_info['plan']) : 'basic',
			'checked_at' => wp_date('Y-m-d H:i:s', $checked_timestamp),
			'next_check_at' => $next_check_at,
			'expires_at' => isset($verification_info['expires']) ? sanitize_text_field((string) $verification_info['expires']) : '',
			'failed_check_count' => 0,
			'grace_until' => $grace_until,
			'message' => $status === 'expired'
				? __('License is expired.', 'quikvox-ai')
				: __('License is active.', 'quikvox-ai'),
			'last_error' => '',
		);

		return self::save_license_state($state);
	}

	/**
	 * Build a license state snapshot after a failed remote verification.
	 *
	 * @param string $license_key The license key that was checked.
	 * @param string $error_message The error message from the verification attempt.
	 * @param array  $previous_state The previously stored state.
	 * @return array<string, mixed>
	 */
	private static function build_state_from_failure($license_key, $error_message, array $previous_state)
	{
		$checked_timestamp = current_time('timestamp');
		$previous_status = isset($previous_state['status']) ? sanitize_key((string) $previous_state['status']) : 'unknown';
		$previous_grace_until = isset($previous_state['grace_until']) ? sanitize_text_field((string) $previous_state['grace_until']) : '';
		$previous_failed_check_count = isset($previous_state['failed_check_count']) ? absint($previous_state['failed_check_count']) : 0;
		$next_check_at = self::build_next_check_at($checked_timestamp);
		$error_message = sanitize_text_field((string) $error_message);

		$status = $previous_status;
		$message = $error_message;
		$grace_until = $previous_grace_until;

		if ($status === 'active') {
			if ($grace_until === '') {
				$grace_until = self::build_grace_until($checked_timestamp);
			}

			$grace_timestamp = strtotime($grace_until);
			if ($grace_timestamp !== false && $grace_timestamp >= $checked_timestamp) {
				if ($error_message !== '') {
					/* translators: %s is the grace period end datetime. */
					$message = sprintf(__('License verification failed. Pro features remain active until %s.', 'quikvox-ai'), $grace_until);
				}
				else {
					/* translators: %s is the grace period end datetime. */
					$message = sprintf(__('License verification failed. Pro features remain active until %s.', 'quikvox-ai'), $grace_until);
				}
				$status = 'active';
			}
			else {
				$status = 'inactive';
				$grace_until = $grace_until !== '' ? $grace_until : wp_date('Y-m-d H:i:s', $checked_timestamp);
				if ($error_message !== '') {
					/* translators: %s is the error message returned from the license server. */
					$message = sprintf(__('License verification failed and the grace period has expired. %s', 'quikvox-ai'), $error_message);
				}
				else {
					$message = __('License verification failed and the grace period has expired.', 'quikvox-ai');
				}
			}
		}
		elseif ($status === 'expired' || $status === 'invalid') {
			$message = $error_message !== '' ? $error_message : $message;
		}
		else {
			$status = 'unknown';
			$message = $error_message !== '' ? $error_message : __('License verification could not be completed.', 'quikvox-ai');
		}

		$state = array(
			'license_key' => $license_key,
			'status' => $status,
			'plan' => isset($previous_state['plan']) && in_array($previous_state['plan'], array('basic', 'pro', 'business'), true) ? $previous_state['plan'] : 'basic',
			'checked_at' => wp_date('Y-m-d H:i:s', $checked_timestamp),
			'next_check_at' => $next_check_at,
			'expires_at' => isset($previous_state['expires_at']) ? sanitize_text_field((string) $previous_state['expires_at']) : '',
			'failed_check_count' => $previous_failed_check_count + 1,
			'grace_until' => $grace_until,
			'message' => $message,
			'last_error' => $error_message,
		);

		return self::save_license_state($state);
	}

	/**
	 * Build a license state snapshot for a server-rejected license.
	 *
	 * @param string $license_key The license key that was checked.
	 * @param string $error_message The error message from the verification attempt.
	 * @return array<string, mixed>
	 */
	private static function build_state_from_rejected_license($license_key, $error_message)
	{
		$checked_timestamp = current_time('timestamp');
		$state = array(
			'license_key' => $license_key,
			'status' => 'invalid',
			'plan' => 'basic',
			'checked_at' => wp_date('Y-m-d H:i:s', $checked_timestamp),
			'next_check_at' => self::build_next_check_at($checked_timestamp),
			'expires_at' => '',
			'failed_check_count' => 0,
			'grace_until' => wp_date('Y-m-d H:i:s', $checked_timestamp),
			'message' => sanitize_text_field((string) $error_message),
			'last_error' => sanitize_text_field((string) $error_message),
		);

		return self::save_license_state($state);
	}

	/**
	 * Refresh the stored state if the next check date has passed.
	 *
	 * @return bool True when a refresh was attempted.
	 */
	public static function maybe_refresh_due_license_state()
	{
		$license_key = self::normalize_license_key(get_option('podcastmaker_license_key', ''));
		if ($license_key === '') {
			return false;
		}

		$state = self::get_saved_license_state();
		if (!self::is_license_recheck_due($state)) {
			return false;
		}

		self::verify_license($license_key, true);
		return true;
	}

	/**
	 * Build a transient key for cached license payloads.
	 *
	 * @param string $license_key License key used as cache seed.
	 * @return string
	 */
	private static function get_license_cache_key($license_key)
	{
		return 'podcastmaker_license_info_' . md5($license_key);
	}

	/**
	 * Build a transient key for cached license errors.
	 *
	 * @param string $license_key License key used as cache seed.
	 * @return string
	 */
	private static function get_license_error_cache_key($license_key)
	{
		return 'podcastmaker_license_error_' . md5($license_key);
	}

	/**
	 * Read a cached license payload from runtime cache or transient storage.
	 *
	 * @param string $license_key License key used as cache seed.
	 * @return array|false
	 */
	private static function get_cached_license_info($license_key)
	{
		$cache_key = self::get_license_cache_key($license_key);
		if (array_key_exists($cache_key, self::$runtime_license_cache)) {
			return self::$runtime_license_cache[$cache_key];
		}

		$cached_info = get_transient($cache_key);
		if (is_array($cached_info)) {
			self::$runtime_license_cache[$cache_key] = $cached_info;
			return $cached_info;
		}

		self::$runtime_license_cache[$cache_key] = false;
		return false;
	}

	/**
	 * Read a cached verification error message.
	 *
	 * @param string $license_key License key used as cache seed.
	 * @return string
	 */
	private static function get_cached_license_error($license_key)
	{
		$cache_key = self::get_license_error_cache_key($license_key);
		if (array_key_exists($cache_key, self::$runtime_license_error_cache)) {
			return self::$runtime_license_error_cache[$cache_key];
		}

		$cached_error = get_transient($cache_key);
		if (is_string($cached_error)) {
			self::$runtime_license_error_cache[$cache_key] = $cached_error;
			return $cached_error;
		}

		self::$runtime_license_error_cache[$cache_key] = '';
		return '';
	}

	/**
	 * Store a verified license payload in cache.
	 *
	 * @param string $license_key License key used as cache seed.
	 * @param array  $license_info Verified license payload.
	 * @return void
	 */
	private static function store_license_info($license_key, array $license_info)
	{
		$cache_key = self::get_license_cache_key($license_key);
		$error_cache_key = self::get_license_error_cache_key($license_key);
		$ttl = self::get_license_cache_ttl($license_info);

		set_transient($cache_key, $license_info, $ttl);
		delete_transient($error_cache_key);
		self::$runtime_license_cache[$cache_key] = $license_info;
		self::$runtime_license_error_cache[$error_cache_key] = '';
	}

	/**
	 * Store a verification error message in cache.
	 *
	 * @param string $license_key License key used as cache seed.
	 * @param string $message Error message returned by the remote service.
	 * @return void
	 */
	private static function store_license_error($license_key, $message)
	{
		$cache_key = self::get_license_cache_key($license_key);
		$error_cache_key = self::get_license_error_cache_key($license_key);
		$message = trim((string) $message);

		delete_transient($cache_key);
		set_transient($error_cache_key, $message, 15 * MINUTE_IN_SECONDS);
		self::$runtime_license_cache[$cache_key] = false;
		self::$runtime_license_error_cache[$error_cache_key] = $message;
	}

	/**
	 * Determine how long a verified payload should stay cached.
	 *
	 * @param array $license_info Verified license payload.
	 * @return int
	 */
	private static function get_license_cache_ttl(array $license_info)
	{
		$default_ttl = (int) apply_filters('podcastmaker_license_cache_ttl', 30 * MINUTE_IN_SECONDS);
		$default_ttl = max(5 * MINUTE_IN_SECONDS, $default_ttl);

		if (!empty($license_info['expires'])) {
			$expires_timestamp = strtotime($license_info['expires'] . ' 23:59:59');
			if ($expires_timestamp !== false) {
				$seconds_remaining = $expires_timestamp - time();
				if ($seconds_remaining > 0) {
					return max(5 * MINUTE_IN_SECONDS, min($default_ttl, $seconds_remaining));
				}
				return 5 * MINUTE_IN_SECONDS;
			}
		}

		return $default_ttl;
	}

	/**
	 * Normalize a remote payload into the internal license information format.
	 *
	 * @param array  $response_body Raw decoded JSON response.
	 * @param string $license_key License key that was verified.
	 * @return array|false
	 */
	private static function normalize_verification_response(array $response_body, $license_key)
	{
		$payload = $response_body;
		if (isset($response_body['data']) && is_array($response_body['data'])) {
			$payload = $response_body['data'];
		}
		elseif (isset($response_body['license']) && is_array($response_body['license'])) {
			$payload = $response_body['license'];
		}

		$is_valid = false;
		if (isset($response_body['success'])) {
			$is_valid = (bool) $response_body['success'];
		}
		elseif (isset($response_body['valid'])) {
			$is_valid = (bool) $response_body['valid'];
		}
		elseif (isset($response_body['is_valid'])) {
			$is_valid = (bool) $response_body['is_valid'];
		}
		elseif (isset($response_body['status'])) {
			$status = strtolower((string) $response_body['status']);
			$is_valid = in_array($status, array('valid', 'active', 'ok', 'success'), true);
		}
		else {
			$is_valid = isset($payload['plan']) || isset($payload['tier']);
		}

		if (!$is_valid) {
			return false;
		}

		$plan = self::normalize_license_plan(
			isset($payload['plan']) ? $payload['plan'] : (isset($payload['tier']) ? $payload['tier'] : (isset($payload['license_type']) ? $payload['license_type'] : '')),
		);

		if ($plan === '') {
			return false;
		}

		$email = '';
		if (isset($payload['email'])) {
			$email = sanitize_email((string) $payload['email']);
		}

		$expires = '';
		if (isset($payload['expires'])) {
			$expires = sanitize_text_field((string) $payload['expires']);
		}
		elseif (isset($payload['expires_at'])) {
			$expires = sanitize_text_field((string) $payload['expires_at']);
		}

		$issued = '';
		if (isset($payload['issued'])) {
			$issued = sanitize_text_field((string) $payload['issued']);
		}
		elseif (isset($payload['issued_at'])) {
			$issued = sanitize_text_field((string) $payload['issued_at']);
		}

		$additional = '';
		if (isset($payload['additional'])) {
			if (is_scalar($payload['additional'])) {
				$additional = sanitize_text_field((string) $payload['additional']);
			}
			else {
				$additional = wp_json_encode($payload['additional']);
			}
		}

		$is_expired = false;
		if ($expires !== '') {
			$expires_timestamp = strtotime($expires . ' 23:59:59');
			if ($expires_timestamp !== false) {
				$is_expired = $expires_timestamp < time();
			}
		}

		if ($issued !== '') {
			$issued_timestamp = strtotime($issued . ' 00:00:00');
			if ($issued_timestamp !== false && $issued_timestamp > time()) {
				$is_expired = true;
			}
		}

		return array(
			'plan' => $plan,
			'email' => $email,
			'expires' => $expires,
			'issued' => $issued,
			'additional' => $additional,
			'is_expired' => $is_expired,
			'verified_at' => gmdate('Y-m-d H:i:s'),
		);
	}

	/**
	 * Extract a human-readable error message from a verification response.
	 *
	 * @param array $response_body Raw decoded JSON response.
	 * @return string
	 */
	private static function extract_error_message(array $response_body)
	{
		$message_fields = array('message', 'error', 'error_message', 'reason', 'detail');
		foreach ($message_fields as $field) {
			if (!empty($response_body[$field]) && is_scalar($response_body[$field])) {
				return sanitize_text_field((string) $response_body[$field]);
			}
		}

		return __('License verification failed.', 'quikvox-ai');
	}

	/**
	 * Verify a license key against the remote license server.
	 *
	 * @param string  $license_key License key to verify.
	 * @param boolean $force_refresh Skip cached values and always contact the server.
	 * @return array|false
	 */
	public static function verify_license($license_key, $force_refresh = false)
	{
		$license_key = self::normalize_license_key($license_key);
		if ($license_key === '') {
			self::save_license_state(array(
				'license_key' => '',
				'status' => 'unknown',
				'plan' => 'basic',
				'checked_at' => wp_date('Y-m-d H:i:s', current_time('timestamp')),
				'next_check_at' => '',
				'expires_at' => '',
				'failed_check_count' => 0,
				'grace_until' => '',
				'message' => __('No license key is configured.', 'quikvox-ai'),
				'last_error' => '',
			));
			return false;
		}

		$previous_state = self::get_saved_license_state();

		if (!$force_refresh) {
			$cached_info = self::get_cached_license_info($license_key);
			if ($cached_info !== false) {
				self::save_license_state(self::build_state_from_success($license_key, $cached_info));
				return $cached_info;
			}
		}

		$endpoint = self::get_verification_endpoint();
		if ($endpoint === '') {
			self::store_license_error($license_key, __('License verification endpoint is not configured.', 'quikvox-ai'));
			return false;
		}

		$request = wp_remote_post(
			$endpoint,
			array(
				'timeout' => self::get_verification_timeout(),
				'headers' => array(
					'Accept' => 'application/json',
				),
				'body' => array(
					'license_key' => $license_key,
					'site_url' => site_url('/'),
					'home_url' => home_url('/'),
					'plugin_version' => defined('PODCASTMAKER_VERSION') ? PODCASTMAKER_VERSION : '',
				),
			)
		);

		if (is_wp_error($request)) {
			self::store_license_error($license_key, $request->get_error_message());
			self::build_state_from_failure($license_key, $request->get_error_message(), $previous_state);
			return false;
		}

		$status_code = (int) wp_remote_retrieve_response_code($request);
		$response_body = wp_remote_retrieve_body($request);
		if ($status_code < 200 || $status_code >= 300 || $response_body === '') {
			$error_message = __('License verification request failed.', 'quikvox-ai');
			self::store_license_error($license_key, $error_message);
			self::build_state_from_failure($license_key, $error_message, $previous_state);
			return false;
		}

		$decoded_body = json_decode($response_body, true);
		if (!is_array($decoded_body) || json_last_error() !== JSON_ERROR_NONE) {
			$error_message = __('License verification response is not valid JSON.', 'quikvox-ai');
			self::store_license_error($license_key, $error_message);
			self::build_state_from_failure($license_key, $error_message, $previous_state);
			return false;
		}

		$normalized_info = self::normalize_verification_response($decoded_body, $license_key);
		if ($normalized_info === false) {
			$error_message = self::extract_error_message($decoded_body);
			self::store_license_error($license_key, $error_message);
			self::build_state_from_rejected_license($license_key, $error_message);
			return false;
		}

		self::store_license_info($license_key, $normalized_info);
		self::build_state_from_success($license_key, $normalized_info);
		return $normalized_info;
	}

	/**
	 * Decode the license key and extract information.
	 *
	 * This method is kept for backward compatibility and now delegates to the
	 * remote verification flow.
	 *
	 * @param string $license_key License key to verify.
	 * @return array|false Returns array with plan, email, expires, issued, additional or false if invalid.
	 */
	public static function decode_license($license_key)
	{
		return self::get_license_state();
	}

	/**
	 * Read the stored license state.
	 *
	 * @return array<string, mixed>
	 */
	public static function get_license_state()
	{
		$state = self::get_saved_license_state();
		$license_key = self::normalize_license_key(get_option('podcastmaker_license_key', ''));
		$state['license_key'] = $license_key;

		return self::normalize_license_state($state);
	}

	/**
	 * Sanitize and verify the stored license key option.
	 *
	 * @param string $value Raw option value from the settings form.
	 * @return string
	 */
	public static function sanitize_license_key($value)
	{
		$license_key = self::normalize_license_key($value);

		if ($license_key === '') {
			self::save_license_state(array(
				'license_key' => '',
				'status' => 'unknown',
				'plan' => 'basic',
				'checked_at' => wp_date('Y-m-d H:i:s', current_time('timestamp')),
				'next_check_at' => '',
				'expires_at' => '',
				'failed_check_count' => 0,
				'grace_until' => '',
				'message' => __('No license key is configured.', 'quikvox-ai'),
				'last_error' => '',
			));
			return '';
		}

		$existing_key = self::normalize_license_key(get_option('podcastmaker_license_key', ''));
		if ($license_key === $existing_key) {
			return $license_key;
		}

		$license_info = self::verify_license($license_key, true);
		if ($license_info === false) {
			$error_message = self::get_cached_license_error($license_key);
			if ($error_message === '') {
				$error_message = __('License verification failed.', 'quikvox-ai');
			}

			add_settings_error(
				'podcastmaker_license_key',
				'podcastmaker_license_invalid',
				esc_html($error_message),
				'error'
			);
			return $license_key;
		}

		if (!empty($license_info['is_expired'])) {
			add_settings_error(
				'podcastmaker_license_key',
				'podcastmaker_license_expired',
				esc_html__('License key saved, but it is expired.', 'quikvox-ai'),
				'updated'
			);
		}
		else {
			add_settings_error(
				'podcastmaker_license_key',
				'podcastmaker_license_verified',
				esc_html__('License verified successfully.', 'quikvox-ai'),
				'updated'
			);
		}

		return $license_key;
	}

	/**
	 * Get the latest verified license information.
	 *
	 * @return array|false
	 */
	public static function get_license_info()
	{
		return self::get_license_state();
	}

	/**
	 * Get a human-readable status object for the current license.
	 *
	 * @return array{state:string,message:string,info:array|false}
	 */
	public static function get_license_status()
	{
		$state = self::get_license_state();
		$license_key = isset($state['license_key']) ? self::normalize_license_key($state['license_key']) : '';
		if ($license_key === '') {
			return array(
				'state' => 'unknown',
				'message' => __('No license key is configured.', 'quikvox-ai'),
				'info' => $state,
			);
		}

		$status = isset($state['status']) ? sanitize_key((string) $state['status']) : 'unknown';
		switch ($status) {
			case 'active':
				return array(
					'state' => 'active',
					'message' => !empty($state['message']) ? $state['message'] : __('The saved license key is active.', 'quikvox-ai'),
					'info' => $state,
				);
			case 'expired':
				return array(
					'state' => 'expired',
					'message' => !empty($state['message']) ? $state['message'] : __('The saved license key is expired.', 'quikvox-ai'),
					'info' => $state,
				);
			case 'invalid':
				return array(
					'state' => 'invalid',
					'message' => !empty($state['message']) ? $state['message'] : __('The saved license key is invalid.', 'quikvox-ai'),
					'info' => $state,
				);
			case 'inactive':
				return array(
					'state' => 'inactive',
					'message' => !empty($state['message']) ? $state['message'] : __('The saved license key is inactive.', 'quikvox-ai'),
					'info' => $state,
				);
			default:
				return array(
					'state' => 'unknown',
					'message' => !empty($state['message']) ? $state['message'] : __('The saved license key status is unknown.', 'quikvox-ai'),
					'info' => $state,
				);
		}
	}

	/**
	 * Check if the current installation is Pro or higher.
	 *
	 * @return bool
	 */
	public static function is_pro()
	{
		$state = self::get_license_state();
		return isset($state['status'], $state['plan']) && $state['status'] === 'active' && in_array($state['plan'], array('pro', 'business'), true);
	}

	/**
	 * Check if the current installation is Business.
	 *
	 * @return bool
	 */
	public static function is_business()
	{
		$state = self::get_license_state();
		return isset($state['status'], $state['plan']) && $state['status'] === 'active' && $state['plan'] === 'business';
	}

	/**
	 * Check if Vertex AI can be used (Business only).
	 *
	 * @return bool
	 */
	public static function can_use_vertex()
	{
		return self::is_business();
	}

	/**
	 * Check if global stats can be viewed (Business only).
	 *
	 * @return bool
	 */
	public static function can_view_global_stats()
	{
		return self::is_business();
	}

	/**
	 * Check if the user can view logged user names.
	 * Only Business plan (Admin) can see real names.
	 *
	 * @return bool
	 */
	public static function can_view_user_names()
	{
		return self::is_business();
	}

	/**
	 * Get the current active plan.
	 *
	 * @return string
	 */
	public static function get_plan()
	{
		$state = self::get_license_state();
		if (isset($state['status'], $state['plan']) && $state['status'] === 'active' && in_array($state['plan'], array('basic', 'pro', 'business'), true)) {
			return $state['plan'];
		}

		return 'basic';
	}

	/**
	 * Helper to get the version label.
	 *
	 * @return string
	 */
	public static function get_version_type()
	{
		$plan = self::get_plan();
		switch ($plan) {
			case 'business':
				return 'Business';
			case 'pro':
				return 'Pro';
			default:
				return 'Basic';
		}
	}
}
