<?php

declare(strict_types=1);

namespace FormConnector\Core\Updater;

use FormConnector\Integration\CareHQ\Signing;
use FormConnector\Features\License\License;
use FormConnector\Core\Utils\Debug;
use Throwable;
use YahnisElsts\PluginUpdateChecker\v5\PucFactory;

/**
 * Private plugin update integration.
 */
class PluginUpdater {

	private const AUTO_UPDATE_OPTION = 'fc_plugin_auto_update_enabled';
	private const NOTICE_TRANSIENT = 'fc_plugin_update_error';

	/**
	 * Register the update checker with WordPress.
	 *
	 * Wrapped in try/catch so a malformed update-server response or a missing
	 * dependency surfaces as a dismissible admin notice instead of breaking
	 * the entire admin screen.
	 */
	public static function register(): void {
		add_action('admin_notices', [self::class, 'maybe_render_admin_notice']);

		if (!class_exists(PucFactory::class)) {
			return;
		}

		$license = new License();
		if ($license->get_key() === '') {
			return;
		}

		try {
			$checker = PucFactory::buildUpdateChecker(
				self::endpoint_url(),
				FC_PLUGIN_FILE,
				self::plugin_slug()
			);

			$checker->addQueryArgFilter([self::class, 'add_query_args']);
			$checker->addHttpRequestArgFilter([self::class, 'add_request_headers']);
			$checker->addResultFilter([self::class, 'store_update_state']);

			add_filter('auto_update_plugin', [self::class, 'filter_auto_update'], 10, 2);
			add_filter('http_request_args', [self::class, 'add_download_headers'], 10, 2);

			// Clear any previously-stored failure once we've successfully registered.
			delete_transient(self::NOTICE_TRANSIENT);
		} catch (Throwable $e) {
			set_transient(
				self::NOTICE_TRANSIENT,
				wp_strip_all_tags($e->getMessage()),
				HOUR_IN_SECONDS
			);
			Debug::error('Plugin update checker failed to register', [
				'message' => $e->getMessage(),
			]);
		}
	}

	/**
	 * Render a dismissible admin notice when update-checker registration failed.
	 */
	public static function maybe_render_admin_notice(): void {
		if (!current_user_can('update_plugins')) {
			return;
		}

		$message = get_transient(self::NOTICE_TRANSIENT);
		if (!is_string($message) || $message === '') {
			return;
		}

		printf(
			'<div class="notice notice-warning is-dismissible"><p><strong>%s</strong> %s</p></div>',
			esc_html__('CareHQ Connector:', 'carehq-connector'),
			esc_html(sprintf(
				/* translators: %s: error message from the update checker */
				__('automatic update checks are temporarily unavailable (%s). The plugin itself continues to work normally.', 'carehq-connector'),
				$message
			))
		);
	}

	/**
	 * Add public context to update metadata requests.
	 */
	public static function add_query_args(array $query_args): array {
		$query_args['site_url'] = home_url();
		$query_args['plugin_version'] = FC_VERSION;
		$query_args['plugin_basename'] = FC_PLUGIN_BASENAME;
		$query_args['wp_version'] = get_bloginfo('version');
		$query_args['php_version'] = PHP_VERSION;
		$query_args['locale'] = get_locale();

		return $query_args;
	}

	/**
	 * Add license authentication to update metadata requests.
	 */
	public static function add_request_headers(array $request_args): array {
		$license = new License();
		$key = $license->get_key();

		if ($key === '') {
			return $request_args;
		}

		$request_args['headers'] = is_array($request_args['headers'] ?? null) ? $request_args['headers'] : [];
		$request_args['headers']['x-api-key'] = $key;
		$request_args['headers']['Accept'] = 'application/json';

		$secret = Signing::get_secret();
		if ($secret !== '') {
			$timestamp = (string) (int) round(microtime(true) * 1000);
			$nonce = wp_generate_uuid4();
			$payload = $timestamp . '.' . $nonce . '.';
			$request_args['headers']['x-carehq-signature'] = 'sha256=' . hash_hmac('sha256', $payload, $secret);
			$request_args['headers']['x-carehq-timestamp'] = $timestamp;
			$request_args['headers']['x-carehq-nonce'] = $nonce;
		}

		return $request_args;
	}

	/**
	 * Add license authentication to protected package downloads from our update host.
	 */
	public static function add_download_headers(array $request_args, string $url): array {
		$endpoint_host = wp_parse_url(self::endpoint_url(), PHP_URL_HOST);
		$request_host = wp_parse_url($url, PHP_URL_HOST);

		if ($endpoint_host === null || $endpoint_host !== $request_host) {
			return $request_args;
		}

		$license = new License();
		$key = $license->get_key();

		if ($key === '') {
			return $request_args;
		}

		$request_args['headers'] = is_array($request_args['headers'] ?? null) ? $request_args['headers'] : [];
		$request_args['headers']['x-api-key'] = $request_args['headers']['x-api-key'] ?? $key;

		return $request_args;
	}

	/**
	 * Persist update state for the existing admin notice and License screen.
	 */
	public static function store_update_state(mixed $plugin_info, mixed $http_response): mixed {
		if (!is_object($plugin_info) || empty($plugin_info->version)) {
			return $plugin_info;
		}

		$auto_update = isset($plugin_info->auto_update) ? (bool) $plugin_info->auto_update : null;
		if ($auto_update !== null) {
			update_option(self::AUTO_UPDATE_OPTION, $auto_update, false);
		}

		update_option(Signing::OPTION_PLUGIN_UPDATE, [
			'current' => FC_VERSION,
			'latest' => sanitize_text_field((string) $plugin_info->version),
			'minimum' => isset($plugin_info->minimum) ? sanitize_text_field((string) $plugin_info->minimum) : '',
			'updateAvailable' => version_compare((string) $plugin_info->version, FC_VERSION, '>'),
			'updateRequired' => isset($plugin_info->update_required) ? (bool) $plugin_info->update_required : false,
			'checkedAt' => gmdate('c'),
		], false);

		return $plugin_info;
	}

	/**
	 * Respect service-controlled background auto-update preference when provided.
	 */
	public static function filter_auto_update(mixed $update, object $item): mixed {
		if (($item->plugin ?? '') !== FC_PLUGIN_BASENAME) {
			return $update;
		}

		$enabled = get_option(self::AUTO_UPDATE_OPTION, null);

		return $enabled === null ? $update : (bool) $enabled;
	}

	/**
	 * Resolve the update metadata endpoint.
	 */
	private static function endpoint_url(): string {
		if (defined('FC_UPDATE_CHECK_ENDPOINT') && FC_UPDATE_CHECK_ENDPOINT !== '') {
			return FC_UPDATE_CHECK_ENDPOINT;
		}

		$base = defined('FC_LICENSE_API_ENDPOINT') ? FC_LICENSE_API_ENDPOINT : 'https://app.formconnector.co.uk';

		return rtrim($base, '/') . '/api/plugin/update-check';
	}

	/**
	 * Resolve the stable plugin slug used by WordPress update APIs.
	 */
	private static function plugin_slug(): string {
		$slug = dirname(FC_PLUGIN_BASENAME);

		return $slug === '.' ? sanitize_key(basename(FC_PLUGIN_BASENAME, '.php')) : sanitize_key($slug);
	}
}