<?php
/**
 * Consent REST controller.
 *
 * @package Carticy\Conversions\Rest
 */

declare(strict_types=1);

namespace Carticy\Conversions\Rest;

use Carticy\Conversions\Consent\ConsentPlatformDetector;
use Carticy\Conversions\Consent\GenericConsentPresetRegistry;
use Carticy\Conversions\Domain\ConsentResolver;
use Carticy\Conversions\Settings\ConsentPostureSettings;
use Carticy\Conversions\Storage\OrderMetaStore;

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

/**
 * Backs the Settings · Consent and the per-order consent audit surfaces.
 *
 * Posture writes are server-validated and never silently default to
 * `not_required`; the per-order audit is strictly read-only and exposes the
 * resolved decision together with the contributing-signal trail so the gate's
 * behavior can be proven.
 */
final class ConsentController extends AbstractRestController {

	/**
	 * Posture/intake settings.
	 *
	 * @var ConsentPostureSettings
	 */
	private ConsentPostureSettings $posture;

	/**
	 * Generic preset registry (data-driven CMP coverage).
	 *
	 * @var GenericConsentPresetRegistry
	 */
	private GenericConsentPresetRegistry $presets;

	/**
	 * Order meta store (for the per-order audit).
	 *
	 * @var OrderMetaStore
	 */
	private OrderMetaStore $store;

	/**
	 * Active consent-platform detector.
	 *
	 * @var ConsentPlatformDetector
	 */
	private ConsentPlatformDetector $detector;

	/**
	 * Constructor.
	 *
	 * @param ConsentPostureSettings       $posture Posture/intake settings.
	 * @param GenericConsentPresetRegistry $presets Generic preset registry.
	 * @param OrderMetaStore               $store Order meta store.
	 * @param ConsentPlatformDetector|null $detector Active consent-platform detector.
	 */
	public function __construct( ConsentPostureSettings $posture, GenericConsentPresetRegistry $presets, OrderMetaStore $store, ?ConsentPlatformDetector $detector = null ) {
		$this->posture  = $posture;
		$this->presets  = $presets;
		$this->store    = $store;
		$this->detector = $detector ?? new ConsentPlatformDetector();
	}

	/**
	 * Declared routes.
	 *
	 * @return array<int, array<string, mixed>>
	 */
	public function routes(): array {
		return array(
			array(
				'methods'  => 'GET',
				'route'    => '/consent',
				'callback' => array( $this, 'get_consent' ),
			),
			array(
				'methods'  => 'POST',
				'route'    => '/consent',
				'callback' => array( $this, 'update_consent' ),
			),
			array(
				'methods'  => 'GET',
				'route'    => '/consent/audit/(?P<order_id>\d+)',
				'callback' => array( $this, 'get_audit' ),
				'args'     => array(
					'order_id' => array(
						'required'          => true,
						'sanitize_callback' => 'absint',
					),
				),
			),
			array(
				'methods'  => 'GET',
				'route'    => '/consent/no-cmp-guidance',
				'callback' => array( $this, 'get_no_cmp_guidance' ),
			),
		);
	}

	/**
	 * Read the consent posture, attestation, gating, and detected presets.
	 *
	 * @return \WP_REST_Response
	 */
	public function get_consent(): \WP_REST_Response {
		return $this->ok(
			array(
				'posture'     => $this->posture->posture(),
				'postures'    => array(
					ConsentResolver::POSTURE_CMP_DRIVEN,
					ConsentResolver::POSTURE_REQUIRED,
					ConsentResolver::POSTURE_NOT_REQUIRED,
				),
				'attestation' => $this->posture->attestation(),
				'config'      => $this->posture->consent_config(),
				'detected'    => $this->detector->detect(),
				'presets'     => $this->preset_summaries(),
				'schema'      => array(
					'services'     => array( 'ga4', 'google-ads', 'meta' ),
					'serviceModes' => array( 'auto', 'require_explicit', 'off' ),
				),
			)
		);
	}

	/**
	 * Write posture + attestation + per-service gating + preset selection.
	 *
	 * @param \WP_REST_Request $request REST request.
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function update_consent( \WP_REST_Request $request ) {
		$body = $this->body( $request );
		if ( array() === $body ) {
			return $this->fail( 'carticy_invalid_body', __( 'Request body must be a non-empty JSON object.', 'carticy-conversions-for-woocommerce' ), 400 );
		}

		// Posture is only touched when explicitly present: an absent posture must
		// not silently flip the store to `not_required`.
		if ( array_key_exists( 'posture', $body ) ) {
			$this->posture->apply_posture( (string) $body['posture'] );
		}

		if ( array_key_exists( 'config', $body ) && is_array( $body['config'] ) ) {
			$this->posture->apply_consent_config( $body['config'] );
		}

		return $this->get_consent();
	}

	/**
	 * Read-only per-order consent audit: resolved decision + contributing sources.
	 *
	 * @param \WP_REST_Request $request REST request.
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function get_audit( \WP_REST_Request $request ) {
		$order_id = (int) $request->get_param( 'order_id' );
		if ( $order_id <= 0 || ! function_exists( 'wc_get_order' ) ) {
			return $this->fail( 'carticy_invalid_order', __( 'A valid order id is required.', 'carticy-conversions-for-woocommerce' ), 400 );
		}

		$order = wc_get_order( $order_id );
		if ( ! is_object( $order ) ) {
			return $this->fail( 'carticy_order_not_found', __( 'Order not found.', 'carticy-conversions-for-woocommerce' ), 404 );
		}

		$record = $this->store->get_consent( $order );

		return $this->ok(
			array(
				'orderId' => $order_id,
				'state'   => $record->state(),
				'sources' => $record->sources(),
			)
		);
	}

	/**
	 * Supported-CMP install guidance for the no-platform branch.
	 *
	 * This is the real destination behind the "Connect a consent platform"
	 * affordance: a list of supported CMP plugins to install - NOT an in-app
	 * OAuth/connect (CMPs are separate plugins) and not a dead link.
	 *
	 * @return \WP_REST_Response
	 */
	public function get_no_cmp_guidance(): \WP_REST_Response {
		return $this->ok(
			array(
				'message'     => __( 'Consent platforms are separate plugins. Install one of these supported options, then return here - Carticy detects it automatically.', 'carticy-conversions-for-woocommerce' ),
				'platforms'   => $this->supported_cmps(),
				'attestation' => array(
					'posture' => ConsentResolver::POSTURE_NOT_REQUIRED,
					'note'    => __( 'If your store operates outside an opt-in consent regime, you can record a merchant attestation instead of installing a platform.', 'carticy-conversions-for-woocommerce' ),
				),
			)
		);
	}

	/**
	 * Compact, secret-free summary of the generic presets/adapters.
	 *
	 * @return array<int, array<string, string>>
	 */
	private function preset_summaries(): array {
		$summaries = array();
		foreach ( $this->presets->presets() as $preset ) {
			$summaries[] = array(
				'source'  => (string) ( $preset['source'] ?? '' ),
				'version' => (string) ( $preset['source_version'] ?? '' ),
				'channel' => 'cookie',
			);
		}
		foreach ( $this->presets->browser_adapters() as $adapter ) {
			$summaries[] = array(
				'source'  => (string) ( $adapter['source'] ?? '' ),
				'version' => (string) ( $adapter['sourceVersion'] ?? '' ),
				'channel' => 'js-global',
			);
		}

		return $summaries;
	}

	/**
	 * Supported CMP plugins for guided install.
	 *
	 * @return array<int, array<string, string>>
	 */
	private function supported_cmps(): array {
		return array(
			array(
				'name'        => 'Complianz',
				'slug'        => 'complianz-gdpr',
				'install_url' => self::plugin_install_url( 'complianz-gdpr' ),
			),
			array(
				'name'        => 'CookieYes',
				'slug'        => 'cookie-law-info',
				'install_url' => self::plugin_install_url( 'cookie-law-info' ),
			),
			array(
				'name'        => 'Real Cookie Banner',
				'slug'        => 'real-cookie-banner',
				'install_url' => self::plugin_install_url( 'real-cookie-banner' ),
			),
			array(
				'name'        => 'WPConsent',
				'slug'        => 'wpconsent-cookies-banner-privacy-suite',
				'install_url' => self::plugin_install_url( 'wpconsent-cookies-banner-privacy-suite' ),
			),
		);
	}

	/**
	 * Build the wp-admin install link for a plugin slug.
	 *
	 * @param string $slug Plugin directory slug.
	 * @return string
	 */
	private static function plugin_install_url( string $slug ): string {
		if ( ! function_exists( 'self_admin_url' ) ) {
			return '';
		}

		$url = self_admin_url( 'plugin-install.php?tab=plugin-information&plugin=' . rawurlencode( $slug ) );

		return function_exists( 'esc_url_raw' ) ? esc_url_raw( $url ) : $url;
	}
}
