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

namespace Hafenstudios\Kurato\Rest;

use Hafenstudios\Core\Core;
use Hafenstudios\Core\Events\EventBus;
use Hafenstudios\Kurato\Db\ContactRepository;
use Hafenstudios\Kurato\Db\MailEventRepository;
use Hafenstudios\Kurato\Db\SubscriptionRepository;
use Hafenstudios\Kurato\Mail\LinkToken;

defined( 'ABSPATH' ) || exit;

/**
 * Oeffentlicher Abmelde-Endpoint (`kurato/v1/unsubscribe`, K4-Konzept Abschnitt 3).
 *
 * Kein Login: die Absicht sichert ausschliesslich der HMAC-signierte Token aus
 * dem Abmeldelink der Mail. GET (Klick im Newsletter) und POST (One-Click nach
 * RFC 8058 fuer den `List-Unsubscribe-Post`-Header) sind beide erlaubt. Rate
 * Limit pro IP wie beim Bestaetigungs-Endpoint.
 */
final class UnsubscribeController {

	public const REST_NAMESPACE = 'kurato/v1';

	private SubscriptionRepository $subscriptions;

	private LinkToken $tokens;

	private ContactRepository $contacts;

	private ?MailEventRepository $events;

	/**
	 * @param SubscriptionRepository   $subscriptions Abo-Repository.
	 * @param LinkToken                $tokens        Token-Dienst (zustandslos).
	 * @param ContactRepository        $contacts      Kontakt-Repository (Consent-Nachweis).
	 * @param MailEventRepository|null $events        Ereignis-Log (optional, K4e-1).
	 */
	public function __construct( SubscriptionRepository $subscriptions, LinkToken $tokens, ContactRepository $contacts, ?MailEventRepository $events = null ) {
		$this->subscriptions = $subscriptions;
		$this->tokens        = $tokens;
		$this->contacts      = $contacts;
		$this->events        = $events;
	}

	/**
	 * Registriert die Route.
	 *
	 * @return void
	 */
	public function register_routes(): void {
		register_rest_route(
			self::REST_NAMESPACE,
			'/unsubscribe',
			array(
				'methods'             => array( \WP_REST_Server::READABLE, \WP_REST_Server::CREATABLE ),
				'callback'            => array( $this, 'unsubscribe' ),
				'permission_callback' => '__return_true',
				'args'                => array(
					'token' => array( 'type' => 'string', 'required' => true ),
				),
			)
		);
	}

	/**
	 * Meldet einen Kontakt per Token ab (aus einer Liste oder aus allen Listen).
	 *
	 * @param \WP_REST_Request $request Request.
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function unsubscribe( \WP_REST_Request $request ) {
		$ip  = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '';
		$key = 'kurato_unsub_' . md5( $ip );

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

		$claim = $this->tokens->verify( (string) $request->get_param( 'token' ) );
		if ( is_wp_error( $claim ) ) {
			return $claim;
		}

		$contact_id = (int) $claim['contact_id'];
		$list_id    = (int) $claim['list_id'];

		if ( 0 === $list_id ) {
			$this->subscriptions->unsubscribe_all( $contact_id );
		} else {
			$this->subscriptions->unsubscribe( $list_id, $contact_id );
		}

		// Widerruf der Einwilligung gerichtsfest ins Consent-Log schreiben (K4c-1-Nachzug).
		$contact = $this->contacts->get( $contact_id );
		$email   = null !== $contact ? sanitize_email( (string) ( $contact['email'] ?? '' ) ) : '';
		if ( '' !== $email ) {
			Core::instance()->consent()->record(
				array(
					'source'        => 'kurato:unsubscribe',
					'subject_email' => $email,
					'purpose'       => 'newsletter',
					'consent_text'  => __( 'Widerruf der Einwilligung durch Abmeldung', 'kurato' ),
					'text_version'  => '1',
					'reference'     => 'unsubscribe',
				)
			);
		}

		// Ereignis-Log (K4e-1): Abmeldung ohne Kampagnenbezug (campaign_id 0 -> NULL).
		if ( null !== $this->events ) {
			$this->events->record( 0, $contact_id, 'unsubscribe', array( 'detail' => 0 === $list_id ? 'all' : 'list:' . $list_id ) );
		}

		EventBus::emit(
			'subscription_unsubscribed',
			array(
				'contact_id' => $contact_id,
				'list_id'    => $list_id,
				'scope'      => 0 === $list_id ? 'all' : 'list',
			)
		);

		$message = 0 === $list_id
			? __( 'Sie wurden von allen Newslettern abgemeldet. Vielen Dank, dass Sie dabei waren.', 'kurato' )
			: __( 'Sie wurden von diesem Newsletter abgemeldet. Vielen Dank, dass Sie dabei waren.', 'kurato' );

		// Abmeldelinks klickt immer ein Mensch: gepflegte Seite statt JSON (L-F3-Muster).
		if ( \Hafenstudios\Core\Http\BrowserResponse::is_browser_request() ) {
			\Hafenstudios\Core\Http\BrowserResponse::send_page(
				__( 'Abmeldung bestaetigt', 'kurato' ),
				$message
			);
		}

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