<?php
/**
 * BaseCloud Boost — Cloudflare CDN Integration
 *
 * Hooks into the BCB purge action system so that whenever the local
 * page cache is cleared, the corresponding Cloudflare edge cache is
 * cleared automatically via the Cloudflare Cache Purge API.
 *
 * Setup requirements (entered in WP Admin → BaseCloud Boost → CDN):
 *   - Cloudflare Zone ID  (Dashboard → your domain → Overview, bottom-right)
 *   - API Token           (My Profile → API Tokens → Create Token → Cache Purge)
 *
 * @package BaseCloud\Boost\CDN
 */

declare( strict_types=1 );

namespace BaseCloud\Boost\CDN;

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

/**
 * Cloudflare CDN integration — registers purge hooks and exposes API helpers.
 */
class CDN_Integration {

	private const CF_API_BASE = 'https://api.cloudflare.com/client/v4/zones/';

	/**
	 * Register hooks that mirror BCB's local purge events to Cloudflare.
	 *
	 * @return void
	 */
	public function boot(): void {
		// Full site purge.
		add_action( 'BCBOOST_after_purge_all',  [ $this, 'purge_all' ] );

		// Single URL purge.
		add_action( 'BCBOOST_after_purge_url',  [ $this, 'purge_url' ] );

		// Smart post purge (receives the full list of affected URLs).
		add_action( 'BCBOOST_after_purge_post', [ $this, 'on_purge_post' ], 10, 2 );

		// Taxonomy term purge.
		add_action( 'BCBOOST_after_purge_term', [ $this, 'on_purge_term' ], 10, 2 );
	}

	/**
	 * Purge every cached asset in the Cloudflare zone.
	 *
	 * @return bool
	 */
	public function purge_all(): bool {
		if ( ! $this->is_configured() ) {
			return false;
		}

		$response = $this->api_request( 'purge_cache', [ 'purge_everything' => true ] );
		return (bool) ( $response['success'] ?? false );
	}

	/**
	 * Purge a single URL from Cloudflare's edge cache.
	 *
	 * @param string $url Full URL to purge.
	 * @return bool
	 */
	public function purge_url( string $url ): bool {
		return $this->purge_urls( [ $url ] );
	}

	/**
	 * Purge multiple URLs in batches of 30 (Cloudflare API limit per request).
	 *
	 * @param string[] $urls List of full URLs.
	 * @return bool True if all batches succeeded.
	 */
	public function purge_urls( array $urls ): bool {
		if ( ! $this->is_configured() || empty( $urls ) ) {
			return false;
		}

		$urls    = array_values( array_unique( array_filter( $urls ) ) );
		$success = true;

		foreach ( array_chunk( $urls, 30 ) as $chunk ) {
			$response = $this->api_request( 'purge_cache', [ 'files' => $chunk ] );
			if ( ! ( $response['success'] ?? false ) ) {
				$success = false;
			}
		}

		return $success;
	}

	/**
	 * Called after a post purge — receives the full URL list.
	 *
	 * @param int      $post_id Post ID (unused, URLs already resolved).
	 * @param string[] $urls    URLs that were purged locally.
	 * @return void
	 */
	public function on_purge_post( int $post_id, array $urls ): void {
		$this->purge_urls( $urls );
	}

	/**
	 * Called after a taxonomy term purge.
	 *
	 * @param int    $term_id  Term ID.
	 * @param string $taxonomy Taxonomy slug.
	 * @return void
	 */
	public function on_purge_term( int $term_id, string $taxonomy ): void {
		$term_link = get_term_link( $term_id, $taxonomy );
		if ( ! is_wp_error( $term_link ) ) {
			$this->purge_urls( [ $term_link, home_url( '/' ) ] );
		}
	}

	/**
	 * Test the configured credentials by fetching zone info from Cloudflare.
	 *
	 * @return array{success: bool, message: string}
	 */
	public function test_connection(): array {
		$zone_id = BCBOOST_get_option( 'cloudflare_zone_id', '' );
		$token   = BCBOOST_get_option( 'cloudflare_api_token', '' );

		if ( empty( $zone_id ) || empty( $token ) ) {
			return [ 'success' => false, 'message' => 'Zone ID and API Token are both required.' ];
		}

		$response = wp_remote_get(
			self::CF_API_BASE . sanitize_text_field( $zone_id ),
			[
				'headers' => [
					'Authorization' => 'Bearer ' . $token,
					'Content-Type'  => 'application/json',
				],
				'timeout' => 10,
			]
		);

		if ( is_wp_error( $response ) ) {
			return [ 'success' => false, 'message' => $response->get_error_message() ];
		}

		$body = (array) json_decode( wp_remote_retrieve_body( $response ), true );

		if ( ! empty( $body['success'] ) ) {
			$zone_name = $body['result']['name'] ?? $zone_id;
			return [ 'success' => true, 'message' => 'Connected. Zone: ' . $zone_name ];
		}

		$error = $body['errors'][0]['message'] ?? 'Unknown Cloudflare error.';
		return [ 'success' => false, 'message' => 'Cloudflare: ' . $error ];
	}

	// ─── Private ──────────────────────────────────────────────────────────────

	/**
	 * Whether valid credentials are saved.
	 *
	 * @return bool
	 */
	private function is_configured(): bool {
		return ! empty( BCBOOST_get_option( 'cloudflare_zone_id', '' ) ) &&
		       ! empty( BCBOOST_get_option( 'cloudflare_api_token', '' ) );
	}

	/**
	 * Send a POST request to the Cloudflare API.
	 *
	 * @param string  $endpoint Relative endpoint (e.g. "purge_cache").
	 * @param array   $body     Request body data.
	 * @return array Decoded JSON response.
	 */
	private function api_request( string $endpoint, array $body ): array {
		$zone_id = sanitize_text_field( BCBOOST_get_option( 'cloudflare_zone_id', '' ) );
		$token   = BCBOOST_get_option( 'cloudflare_api_token', '' );

		$response = wp_remote_post(
			self::CF_API_BASE . $zone_id . '/' . $endpoint,
			[
				'headers' => [
					'Authorization' => 'Bearer ' . $token,
					'Content-Type'  => 'application/json',
				],
				'body'    => wp_json_encode( $body ),
				'timeout' => 15,
			]
		);

		if ( is_wp_error( $response ) ) {
			if ( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) {
				error_log( '[BaseCloud Boost] Cloudflare API error: ' . $response->get_error_message() );
			}
			return [ 'success' => false ];
		}

		return (array) json_decode( wp_remote_retrieve_body( $response ), true );
	}
}
