<?php

declare(strict_types=1);

namespace FormConnector\Integration\Health;

use FormConnector\Integration\CareHQ\Signing;
use FormConnector\Features\License\License;

/**
 * CareHQ Health Ping
 *
 * Registers this WordPress site with CareHQ and keeps it marked as healthy by
 * sending periodic signed health pings. The first successful ping returns a
 * per-site signing secret which we persist for all subsequent signed requests.
 *
 * @see two-way.md
 */
class HealthPing {

	/** Cron hook used to send the recurring ping. */
	public const CRON_HOOK = 'fc_carehq_health_ping';

	/** Cron schedule for recurring pings. */
	public const CRON_SCHEDULE = 'hourly';

	/** REST path for the CareHQ health ping endpoint. */
	private const ENDPOINT_PATH = '/api/health/ping';

	/** REST namespace + route exposed by this plugin to receive webhooks. */
	private const WEBHOOK_NAMESPACE = 'carehq/v1';
	private const WEBHOOK_ROUTE     = '/webhook';

	/**
	 * Bind WordPress hooks. Safe to call multiple times.
	 */
	public static function register_hooks(): void {
		add_action(self::CRON_HOOK, [self::class, 'send_scheduled_ping']);
	}

	/**
	 * Schedule the recurring cron event if not already scheduled.
	 */
	public static function schedule_cron(): void {
		if (!wp_next_scheduled(self::CRON_HOOK)) {
			wp_schedule_event(time() + 60, self::CRON_SCHEDULE, self::CRON_HOOK);
		}
	}

	/**
	 * Remove the recurring cron event.
	 */
	public static function unschedule_cron(): void {
		wp_clear_scheduled_hook(self::CRON_HOOK);
	}

	/**
	 * Cron callback. Wraps {@see send_ping()} so failures don't bubble out of cron.
	 */
	public static function send_scheduled_ping(): void {
		try {
			self::send_ping();
		} catch (\Throwable $e) {
			error_log('[FormConnector] Scheduled health ping failed: ' . $e->getMessage());
		}
	}

	/**
	 * Send a registration/health ping to CareHQ.
	 *
	 * On the first successful response CareHQ returns `data.signingSecret` —
	 * we persist it so that future requests can be signed.
	 *
	 * @return array{success:bool,message?:string,data?:array}
	 */
	public static function send_ping(): array {
		$license = new License();
		$api_key = $license->get_key();

		if ($api_key === '') {
			return [
				'success' => false,
				'message' => __('Cannot send health ping: no API key configured.', 'form-connector'),
			];
		}

		$body = self::build_body();
		$url  = self::endpoint_url();

		$response = wp_remote_post($url, [
			'timeout' => 15,
			'headers' => Signing::build_signed_headers($body, $api_key),
			'body'    => $body,
		]);

		if (is_wp_error($response)) {
			update_option(Signing::OPTION_HEALTH_STATUS, 'unreachable', false);
			error_log('[FormConnector] Health ping network error: ' . $response->get_error_message());
			return [
				'success' => false,
				'message' => $response->get_error_message(),
			];
		}

		$code = wp_remote_retrieve_response_code($response);
		$raw  = wp_remote_retrieve_body($response);
		$data = json_decode($raw, true);
		if (!is_array($data)) {
			$data = [];
		}

		if ($code < 200 || $code >= 300) {
			update_option(Signing::OPTION_HEALTH_STATUS, 'error', false);
			error_log(sprintf('[FormConnector] Health ping failed (HTTP %d): %s', $code, $raw));
			return [
				'success' => false,
				'message' => $data['message'] ?? sprintf(__('Health ping failed (HTTP %d).', 'form-connector'), $code),
			];
		}

		// Persist signing secret on first registration.
		$secret = $data['data']['signingSecret'] ?? null;
		if (is_string($secret) && $secret !== '') {
			Signing::store_secret($secret);
		}

		update_option(Signing::OPTION_LAST_PING, gmdate('c'), false);
		update_option(Signing::OPTION_HEALTH_STATUS, $data['data']['healthStatus'] ?? 'ok', false);
		self::store_plugin_version_status($data['data']['pluginVersion'] ?? null);

		return [
			'success' => true,
			'data'    => $data['data'] ?? [],
		];
	}

	/**
	 * Persist plugin-version status returned by CareHQ.
	 */
	private static function store_plugin_version_status(mixed $plugin_version): void {
		if (!is_array($plugin_version)) {
			return;
		}

		$status = [
			'current'         => sanitize_text_field((string) ($plugin_version['current'] ?? (defined('FC_VERSION') ? FC_VERSION : '0.0.0'))),
			'latest'          => sanitize_text_field((string) ($plugin_version['latest'] ?? '')),
			'minimum'         => sanitize_text_field((string) ($plugin_version['minimum'] ?? '')),
			'updateAvailable' => (bool) ($plugin_version['updateAvailable'] ?? false),
			'updateRequired'  => (bool) ($plugin_version['updateRequired'] ?? false),
			'checkedAt'       => gmdate('c'),
		];

		if (!$status['updateAvailable'] && !$status['updateRequired']) {
			delete_option(Signing::OPTION_PLUGIN_UPDATE);
			return;
		}

		update_option(Signing::OPTION_PLUGIN_UPDATE, $status, false);
	}

	/**
	 * Build the JSON body sent with each health ping.
	 */
	private static function build_body(): string {
		$last_sync = get_option(Signing::OPTION_LAST_SYNC, null);

		$payload = [
			'siteUrl'          => home_url(),
			'webhookUrl'       => self::webhook_url(),
			'pluginVersion'    => defined('FC_VERSION') ? FC_VERSION : '0.0.0',
			'phpVersion'       => PHP_VERSION,
			'wordpressVersion' => get_bloginfo('version'),
			'lastSyncTime'     => $last_sync ?: null,
			'metadata'         => [
				'multisite' => is_multisite(),
				'locale'    => get_locale(),
			],
		];

		return wp_json_encode($payload);
	}

	/**
	 * Resolve the absolute CareHQ health-ping URL.
	 */
	private static function endpoint_url(): string {
		$base = defined('FC_LICENSE_API_ENDPOINT') ? FC_LICENSE_API_ENDPOINT : 'https://app.formconnector.co.uk';
		return rtrim($base, '/') . self::ENDPOINT_PATH;
	}

	/**
	 * Resolve the public webhook URL CareHQ should use to call back into this site.
	 */
	public static function webhook_url(): string {
		return rest_url(self::WEBHOOK_NAMESPACE . self::WEBHOOK_ROUTE);
	}
}
