<?php
/**
 * DRBU_Webhook — Freemius webhook endpoint.
 *
 * REST endpoint:  POST /wp-json/drbu/v1/freemius-webhook
 *
 * Freemius šalje webhook event-e na ovaj URL. Endpoint verifikuje HMAC
 * potpis (opcija `drbu_freemius_webhook_secret`, filter
 * `drbu_freemius_webhook_secret`), pa za svaki podržan event (vidi
 * supported_events()) ODMAH forsira pravu Freemius sinhronizaciju
 * (`Freemius::_sync_cron()` — ista logika koja inače čeka na SDK-ov
 * sopstveni ~24h cron), umesto da čeka sledeći admin page load da lokalni
 * keš uhvati korak sa stvarnim stanjem na Freemius serverima.
 *
 * KRITIČNA ISPRAVKA (webhook nikad nije radio, od prvog dana): potpis se
 * ranije čitao iz nepostojećeg header-a `X-Freemius-Signature`, a
 * `drbu_freemius_webhook_secret` je bio dokumentovan kao poseban "webhook
 * secret" koji se navodno podešava na nepostojećoj "dashboard.freemius.com
 * → Settings → Webhooks" stranici. Freemius-ov sopstveni primer koda
 * (github.com/Freemius/php-webhook-example) i zvanična dokumentacija
 * (freemius.com/help/documentation/saas/events-webhooks/) pokazuju da: (1)
 * potpis dolazi u header-u `X-Signature` (`HTTP_X_SIGNATURE`), i (2) HMAC
 * ključ je PROIZVODOV SOPSTVENI Secret Key (`sk_...`, isti koji ide u
 * `WP_FS__{slug}_SECRET_KEY` za sandbox režim) sa Freemius Developer
 * Dashboard-a → [ovaj proizvod] → Settings — ne neki posebno generisan
 * "webhook" secret. Pošto je stari header naziv bio pogrešan, svaki webhook
 * zahtev je UVEK padao na proveri potpisa i vraćao 403 "Missing signature
 * header" — endpoint nikad nije obradio nijedan pravi event, bez obzira na
 * `supported_events()` listu. Oba su ispravljena; `render_webhook_secret()`
 * (class-drbu-settings.php) sad tačno objašnjava odakle se uzima pravi
 * secret.
 *
 * DRUGA ISPRAVKA: prethodna `supported_events()` je sadržala četiri
 * izmišljena imena event-a koja NE POSTOJE u Freemius API-ju
 * ('subscription.activated', 'subscription.deactivated', 'subscription.renewal',
 * 'payment.failed') — čak i da je potpis prolazio, ti event-i nikad ne bi
 * stigli. Stvarna imena (potvrđena na gornjem doc linku) su sad ispravna;
 * konkretan povod cele ove izmene je bio `license.deleted` (ručno brisanje
 * licence sa Freemius Dashboard-a), koji uopšte nije bio na staroj listi.
 *
 * @package discount-rules-box-upsell
 */

defined( 'ABSPATH' ) || exit;

class DRBU_Webhook {

	const NAMESPACE = 'drbu/v1';
	const ROUTE     = '/freemius-webhook';

	/**
	 * Registruje REST rutu.
	 */
	public static function init() {
		add_action( 'rest_api_init', array( __CLASS__, 'register_routes' ) );
	}

	/**
	 * Event-i koji su relevantni za stanje licence na OVOM sajtu — svaki od
	 * njih znači "ponovo proveri da li ovaj install sme da koristi Pro".
	 *
	 * @return array
	 */
	public static function supported_events() {
		return array(
			// Licenca.
			'license.activated',
			'license.deactivated',
			'license.deleted',
			'license.cancelled',
			'license.expired',
			'license.updated',
			'license.extended',
			'license.shortened',
			// Plan/trial vezan za OVAJ install.
			'install.plan.changed',
			'install.plan.downgraded',
			'install.trial.cancelled',
			'install.trial.expired',
			'install.premium.activated',
			'install.premium.deactivated',
			// Pretplata/naplata.
			'subscription.created',
			'subscription.cancelled',
			'subscription.renewal.failed',
		);
	}

	/**
	 * Registruje REST rutu.
	 */
	public static function register_routes() {
		register_rest_route(
			self::NAMESPACE,
			self::ROUTE,
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( __CLASS__, 'handle' ),
				'permission_callback' => '__return_true',
			)
		);
	}

	/**
	 * Obrađuje webhook zahtev.
	 *
	 * @param WP_REST_Request $request REST zahtev.
	 * @return WP_REST_Response|WP_Error
	 */
	public static function handle( $request ) {
		$secret = self::get_secret();
		if ( '' === $secret ) {
			return new WP_Error(
				'drbu_webhook_not_configured',
				__( 'Webhook secret is not configured.', 'drbu-discount-rules-box-for-woocommerce' ),
				array( 'status' => 403 )
			);
		}

		$signature = $request->get_header( 'x-signature' );
		$body      = $request->get_body();
		if ( ! is_string( $signature ) || '' === $signature ) {
			return new WP_Error(
				'drbu_webhook_missing_signature',
				__( 'Missing signature header.', 'drbu-discount-rules-box-for-woocommerce' ),
				array( 'status' => 403 )
			);
		}

		$expected = hash_hmac( 'sha256', (string) $body, $secret );
		if ( ! hash_equals( $expected, $signature ) ) {
			return new WP_Error(
				'drbu_webhook_invalid_signature',
				__( 'Invalid signature.', 'drbu-discount-rules-box-for-woocommerce' ),
				array( 'status' => 403 )
			);
		}

		$payload = json_decode( (string) $body, true );
		if ( ! is_array( $payload ) ) {
			return new WP_Error(
				'drbu_webhook_invalid_payload',
				__( 'Invalid JSON payload.', 'drbu-discount-rules-box-for-woocommerce' ),
				array( 'status' => 400 )
			);
		}

		// Freemius šalje event ime samo u payload-u, kao "type" (potvrđeno u
		// zvaničnom Freemius/php-webhook-example: `$fs_event->type`) — nema
		// posebnog header-a za ovo.
		$event = isset( $payload['type'] ) ? strtolower( (string) $payload['type'] ) : '';

		if ( in_array( $event, self::supported_events(), true ) ) {
			// Pravi fetch svežeg licenca/plan stanja sa Freemius servera —
			// ista sinhrona logika koju SDK inače sam pokreće preko svog
			// ~24h wp-cron job-a (Freemius::_sync_cron() je javna metoda,
			// bezbedna za pozivanje van admin konteksta jer je upravo
			// napravljena da radi unattended, iz cron-a). Kad ona detektuje
			// pravu promenu stanja, SDK sam ispaljuje 'after_license_change',
			// na koji je DRBU_Freemius_Hooks već ispravno zakačen — pa
			// korisnik dobija i tačnu, transition-specific admin poruku
			// (aktivirano/otkazano/isteklo/...) bez da ovaj fajl mora sam da
			// je pogađa iz sirovog imena webhook event-a.
			if ( function_exists( 'drbu_fs' ) && drbu_fs() && method_exists( drbu_fs(), '_sync_cron' ) ) {
				drbu_fs()->_sync_cron();
			}

			if ( function_exists( 'drbu_license' ) ) {
				drbu_license()->refresh_cache();
			}

			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
				// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
				error_log( sprintf( '[DRBU] Webhook received: %s — forced Freemius sync.', $event ) );
			}
		}

		return new WP_REST_Response(
			array(
				'status' => 'ok',
				'event'  => $event,
			),
			200
		);
	}

	/**
	 * Webhook secret iz podešavanja ili filtera.
	 *
	 * Čuva se u `drbu_options` (sekcija License), a filter omogućava override
	 * (npr. iz konfiguracije ili env-a).
	 *
	 * @return string
	 */
	private static function get_secret() {
		$secret = '';
		if ( class_exists( 'DRBU_Settings' ) ) {
			$secret = (string) DRBU_Settings::instance()->get( 'drbu_freemius_webhook_secret', '' );
		}
		if ( '' === $secret ) {
			$secret = (string) get_option( 'drbu_freemius_webhook_secret', '' );
		}
		return (string) apply_filters( 'drbu_freemius_webhook_secret', $secret );
	}
}
