<?php
/**
 * HTTP client for the Let's Code backoffice API.
 *
 * Handles plugin registration (API key generation), cookie lookup, and
 * cookie contribution. The API base URL defaults to the production endpoint
 * and can be overridden in wp-config.php for local development:
 *
 *   define( 'LCCB_API_URL', 'http://localhost:3002/api/v1' );
 *
 * External service: Let's Code backoffice API (https://backoffice.let-scode.it).
 * Used to: register plugin installations, look up cookie metadata, and submit
 * user-contributed cookie definitions. No personal data is transmitted.
 * Privacy policy: https://let-scode.it/privacy
 *
 * @package LetsCodeCookieBanner
 */

defined( 'ABSPATH' ) || exit;

/**
 * Communicates with the Let's Code backoffice REST API.
 */
class LCCB_API_Client {

	const API_KEY_OPTION         = 'lccb_api_key';
	const INSTALLATION_ID_OPTION = 'lccb_installation_id';
	const PLUGIN_SLUG            = 'lets-code-cookie-banner';

	/**
	 * Returns the API base URL (without trailing slash).
	 *
	 * @return string
	 */
	private static function api_base() {
		return defined( 'LCCB_API_URL' ) ? rtrim( LCCB_API_URL, '/' ) : 'https://backoffice.let-scode.it/api/v1';
	}

	/**
	 * Returns the stored API key, or an empty string if not set.
	 *
	 * @return string
	 */
	public static function get_api_key() {
		return (string) get_option( self::API_KEY_OPTION, '' );
	}

	/**
	 * Returns true if an API key is stored.
	 *
	 * @return bool
	 */
	public static function is_connected() {
		return '' !== self::get_api_key();
	}

	/**
	 * Returns the installation UUID used in the autoblocking script URL.
	 * If not yet stored, fetches it from the API and caches the result.
	 *
	 * @return string UUID or empty string on failure.
	 */
	public static function get_installation_id() {
		$id = (string) get_option( self::INSTALLATION_ID_OPTION, '' );
		if ( '' !== $id ) {
			return $id;
		}

		// Avoid hammering the API if a previous attempt already failed.
		if ( get_transient( 'lccb_installation_id_failed' ) ) {
			return '';
		}

		// /plugin/register is idempotent: returns the same api_key + installation_id for the same site.
		if ( ! self::register() ) {
			set_transient( 'lccb_installation_id_failed', 1, HOUR_IN_SECONDS );
			return '';
		}

		return (string) get_option( self::INSTALLATION_ID_OPTION, '' );
	}

	/**
	 * Registers this WordPress installation with the backoffice and stores the API key.
	 * The endpoint is idempotent: calling it again for the same site returns the existing key.
	 *
	 * @return bool True on success, false if the request failed or no key was returned.
	 */
	public static function register() {
		$body = wp_json_encode(
			array(
				'site_url'    => home_url( '/' ),
				'plugin_slug' => self::PLUGIN_SLUG,
			)
		);

		$response = wp_remote_post(
			self::api_base() . '/plugin/register',
			array(
				'body'    => $body,
				'headers' => array( 'Content-Type' => 'application/json' ),
				'timeout' => 10,
			)
		);

		if ( is_wp_error( $response ) ) {
			return false;
		}

		$code = (int) wp_remote_retrieve_response_code( $response );
		if ( $code < 200 || $code >= 300 ) {
			return false;
		}

		$data    = json_decode( wp_remote_retrieve_body( $response ), true );
		$api_key = isset( $data['api_key'] ) ? sanitize_text_field( (string) $data['api_key'] ) : '';

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

		update_option( self::API_KEY_OPTION, $api_key );

		$installation_id = isset( $data['installation_id'] ) ? sanitize_text_field( (string) $data['installation_id'] ) : '';
		if ( '' !== $installation_id ) {
			update_option( self::INSTALLATION_ID_OPTION, $installation_id );
		}

		return true;
	}

	/**
	 * Looks up cookie metadata by name in the global cookie database.
	 *
	 * @param string[] $names Cookie names to look up (max 50).
	 * @return array|WP_Error Associative array with 'found' and 'not_found' keys, or WP_Error on failure.
	 */
	public static function lookup( array $names ) {
		$api_key = self::get_api_key();
		if ( '' === $api_key ) {
			return new WP_Error(
				'lccb_no_api_key',
				__( 'API key not found. Reactivate the plugin to generate a key.', 'lets-code-cookie-banner' )
			);
		}

		$body = wp_json_encode( array( 'cookies' => array_values( $names ) ) );

		$response = wp_remote_post(
			self::api_base() . '/cookies/lookup',
			array(
				'body'    => $body,
				'headers' => array(
					'Content-Type' => 'application/json',
					'X-Plugin-Key' => $api_key,
				),
				'timeout' => 15,
			)
		);

		if ( is_wp_error( $response ) ) {
			return new WP_Error(
				'lccb_api_offline',
				__( 'The service is temporarily unavailable. Please try again later or contact support@let-scode.it', 'lets-code-cookie-banner' )
			);
		}

		$code = (int) wp_remote_retrieve_response_code( $response );
		if ( 200 !== $code ) {
			return new WP_Error(
				'lccb_api_error',
				__( 'The service is temporarily unavailable. Please try again later or contact support@let-scode.it', 'lets-code-cookie-banner' )
			);
		}

		$data = json_decode( wp_remote_retrieve_body( $response ), true );
		if ( ! isset( $data['found'] ) || ! is_array( $data['found'] ) ) {
			return new WP_Error( 'lccb_api_invalid', __( 'Invalid API response.', 'lets-code-cookie-banner' ) );
		}

		return $data;
	}

	/**
	 * Submits a list of third-party script URLs to the global database.
	 *
	 * Each item must have 'script_url' (string) and 'domain' (string).
	 * No personal data is transmitted — only normalized URLs (no query string).
	 *
	 * @param array[] $scripts Array of arrays with 'script_url' and 'domain' keys.
	 * @return array|WP_Error Response data or WP_Error on failure.
	 */
	public static function contribute_scripts( array $scripts ) {
		$api_key = self::get_api_key();
		if ( '' === $api_key ) {
			return new WP_Error(
				'lccb_no_api_key',
				__( 'API key not found. Reactivate the plugin to generate a key.', 'lets-code-cookie-banner' )
			);
		}

		$body = wp_json_encode( array( 'scripts' => array_values( $scripts ) ) );

		$response = wp_remote_post(
			self::api_base() . '/cookies/scripts/contribute',
			array(
				'body'    => $body,
				'headers' => array(
					'Content-Type' => 'application/json',
					'X-Plugin-Key' => $api_key,
				),
				'timeout' => 15,
			)
		);

		if ( is_wp_error( $response ) ) {
			return new WP_Error(
				'lccb_api_offline',
				__( 'The service is temporarily unavailable. Please try again later or contact support@let-scode.it', 'lets-code-cookie-banner' )
			);
		}

		$code = (int) wp_remote_retrieve_response_code( $response );
		if ( 202 !== $code ) {
			return new WP_Error(
				'lccb_api_error',
				__( 'An error occurred while sending the script contribution.', 'lets-code-cookie-banner' )
			);
		}

		$data = json_decode( wp_remote_retrieve_body( $response ), true );
		return is_array( $data ) ? $data : array();
	}

	/**
	 * Submits one or more cookies to the global database for review.
	 *
	 * @param array[] $cookies Array of cookie data arrays (name, category, vendor, duration, description).
	 * @return array|WP_Error Response data or WP_Error on failure.
	 */
	public static function contribute( array $cookies ) {
		$api_key = self::get_api_key();
		if ( '' === $api_key ) {
			return new WP_Error(
				'lccb_no_api_key',
				__( 'API key not found. Reactivate the plugin to generate a key.', 'lets-code-cookie-banner' )
			);
		}

		$body = wp_json_encode(
			array(
				'site_url' => home_url( '/' ),
				'cookies'  => array_values( $cookies ),
			)
		);

		$response = wp_remote_post(
			self::api_base() . '/cookies/contribute',
			array(
				'body'    => $body,
				'headers' => array(
					'Content-Type' => 'application/json',
					'X-Plugin-Key' => $api_key,
				),
				'timeout' => 15,
			)
		);

		if ( is_wp_error( $response ) ) {
			return new WP_Error(
				'lccb_api_offline',
				__( 'The service is temporarily unavailable. Please try again later or contact support@let-scode.it', 'lets-code-cookie-banner' )
			);
		}

		$code = (int) wp_remote_retrieve_response_code( $response );
		if ( 202 !== $code ) {
			return new WP_Error(
				'lccb_api_error',
				__( 'An error occurred while sending the contribution.', 'lets-code-cookie-banner' )
			);
		}

		$data = json_decode( wp_remote_retrieve_body( $response ), true );
		return is_array( $data ) ? $data : array();
	}
}
