<?php
/**
 * @package Hafenstudios\Leadlotse
 */

namespace Hafenstudios\Leadlotse\Rest;

use Hafenstudios\Leadlotse\Doi\DoiService;
use Hafenstudios\Leadlotse\Submit\SubmitHandler;

defined( 'ABSPATH' ) || exit;

/**
 * Oeffentliche Endpunkte: `leadlotse/v1/submit` und `leadlotse/v1/confirm`.
 * Beide mit Rate Limiting; Submit zusaetzlich durch die Spam-Kette (D7) im
 * SubmitHandler geschuetzt. Der klassische POST-Fallback ohne JS laeuft ueber
 * admin-post.php (D8) und nutzt denselben Handler.
 */
final class PublicController {

	public const REST_NAMESPACE = 'leadlotse/v1';

	private SubmitHandler $handler;

	private DoiService $doi;

	/**
	 * @param SubmitHandler $handler Submit-Pipeline.
	 * @param DoiService    $doi     DOI-Flow.
	 */
	public function __construct( SubmitHandler $handler, DoiService $doi ) {
		$this->handler = $handler;
		$this->doi     = $doi;
	}

	/**
	 * Registriert die Routen.
	 *
	 * @return void
	 */
	public function register_routes(): void {
		register_rest_route(
			self::REST_NAMESPACE,
			'/submit',
			array(
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'submit' ),
				'permission_callback' => '__return_true',
				'args'                => array(
					'form_id' => array(
						'type'     => 'integer',
						'required' => true,
						'minimum'  => 1,
					),
				),
			)
		);

		register_rest_route(
			self::REST_NAMESPACE,
			'/confirm',
			array(
				'methods'             => \WP_REST_Server::READABLE,
				'callback'            => array( $this, 'confirm' ),
				'permission_callback' => '__return_true',
				'args'                => array(
					'token' => array(
						'type'              => 'string',
						'required'          => true,
						'sanitize_callback' => 'sanitize_text_field',
					),
				),
			)
		);
	}

	/**
	 * POST /submit - oeffentlicher Formular-Submit.
	 *
	 * @param \WP_REST_Request $request Request.
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function submit( \WP_REST_Request $request ) {
		$form_id = (int) $request->get_param( 'form_id' );

		$fields = $request->get_param( 'fields' );
		$fields = is_array( $fields ) ? $fields : array();

		// Spam-relevante Sonderfelder liegen auf der obersten Ebene.
		foreach ( array( 'll_ts', 'll_website_url' ) as $special ) {
			$value = $request->get_param( $special );
			if ( null !== $value && ! isset( $fields[ $special ] ) ) {
				$fields[ $special ] = $value;
			}
		}

		$context = array(
			'source_url'   => esc_url_raw( (string) $request->get_param( 'source_url' ) ),
			'utm_source'   => sanitize_text_field( (string) $request->get_param( 'utm_source' ) ),
			'utm_medium'   => sanitize_text_field( (string) $request->get_param( 'utm_medium' ) ),
			'utm_campaign' => sanitize_text_field( (string) $request->get_param( 'utm_campaign' ) ),
		);

		$result = $this->handler->handle( $form_id, $fields, $context );
		if ( is_wp_error( $result ) ) {
			return $result;
		}

		// Entry-ID nicht oeffentlich ausgeben.
		unset( $result['entry_id'] );

		return new \WP_REST_Response( $result, 200 );
	}

	/**
	 * GET /confirm - DOI-Bestaetigung ueber signierten Token (D9).
	 * Eigenes leichtes Rate Limit pro IP gegen Token-Raten.
	 *
	 * @param \WP_REST_Request $request Request.
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function confirm( \WP_REST_Request $request ) {
		$ip  = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '';
		$key = 'll_confirm_' . md5( $ip );

		$count = (int) get_transient( $key );
		if ( $count >= 10 ) {
			return new \WP_Error(
				'leadlotse_rate_limited',
				__( 'Es wurden zu viele Bestaetigungsversuche gesendet. Bitte versuchen Sie es in einigen Minuten erneut.', 'leadlotse' ),
				array( 'status' => 429 )
			);
		}
		set_transient( $key, $count + 1, 10 * MINUTE_IN_SECONDS );

		$entry = $this->doi->confirm( (string) $request->get_param( 'token' ) );
		if ( is_wp_error( $entry ) ) {
			// Menschen kommen aus einer E-Mail und duerfen keine JSON-Fehlermeldung
			// sehen (L-F3). Maschinen bekommen unveraendert den WP_Error.
			if ( \Hafenstudios\Core\Http\BrowserResponse::is_browser_request() ) {
				\Hafenstudios\Core\Http\BrowserResponse::send_page(
					__( 'Bestaetigung nicht moeglich', 'leadlotse' ),
					$entry->get_error_message(),
					false,
					400
				);
			}
			return $entry;
		}

		/**
		 * Flotten-Event: Anmeldung bestaetigt (Kurato uebernimmt in Phase 2 die
		 * eigentliche Listen-/Abo-Verwaltung, D9). Nur beim ERSTEN Bestaetigen
		 * feuern, nicht bei idempotenten Wiederholungsklicks (Review-Backlog).
		 *
		 * @param array<string, mixed> $payload Entry-Daten.
		 */
		if ( ! empty( $entry['_newly_confirmed'] ) ) {
			\Hafenstudios\Core\Events\EventBus::emit(
				'lead_confirmed',
				array(
					'entry_id' => (int) $entry['id'],
					'form_id'  => (int) $entry['form_id'],
					'email'    => (string) $entry['email'],
					'purpose'  => (string) $entry['purpose'],
				)
			);
		}

		$message = __( 'Vielen Dank. Ihre Anmeldung ist bestaetigt.', 'leadlotse' );

		// Klick aus der Bestaetigungsmail: gepflegte Seite statt JSON (L-F3).
		if ( \Hafenstudios\Core\Http\BrowserResponse::is_browser_request() ) {
			\Hafenstudios\Core\Http\BrowserResponse::send_page(
				__( 'Anmeldung bestaetigt', 'leadlotse' ),
				$message
			);
		}

		return new \WP_REST_Response(
			array(
				'confirmed' => true,
				'message'   => $message,
			),
			200
		);
	}
}
