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

namespace Hafenstudios\Kurato\Mail;

use Hafenstudios\Core\Core;
use Hafenstudios\Kurato\Db\ContactRepository;
use Hafenstudios\Kurato\Db\MailEventRepository;
use Hafenstudios\Kurato\Db\SubscriptionRepository;

defined( 'ABSPATH' ) || exit;

/**
 * Wendet ein normalisiertes Zustell-Ereignis auf einen Kontakt an (K4-Konzept
 * Abschnitt 9, Minimal-Hygiene v1).
 *
 * Wirkung je Typ (jeweils zusaetzlich ein Eintrag in hs_mail_events):
 *   bounce_hard  -> email_status = suppressed_bounce
 *   complaint    -> email_status = suppressed_complaint UND alle Abos abgemeldet
 *                   (0,3-Prozent-Regel von Google/Yahoo)
 *   bounce_soft  -> NUR Ereignis (Zaehler/Schwelle sind 1.1)
 *   unsubscribe  -> alle Abos abgemeldet
 * Ist die Adresse keinem Kontakt zuzuordnen, passiert nichts (Noop).
 *
 * Die Queue ueberspringt supprimierte Kontakte bereits beim Versand
 * (CampaignMailBuilder prueft email_status), daher genuegt hier das Setzen des
 * Status. Nicht final, damit Tests den Dienst per Double kapseln koennen.
 */
class SuppressionService {

	private ContactRepository $contacts;

	private SubscriptionRepository $subscriptions;

	private MailEventRepository $events;

	/**
	 * @param ContactRepository      $contacts      Kontakt-Repository.
	 * @param SubscriptionRepository $subscriptions Abo-Repository.
	 * @param MailEventRepository    $events        Ereignis-Log.
	 */
	public function __construct( ContactRepository $contacts, SubscriptionRepository $subscriptions, MailEventRepository $events ) {
		$this->contacts      = $contacts;
		$this->subscriptions = $subscriptions;
		$this->events        = $events;
	}

	/**
	 * Wendet ein normalisiertes Ereignis an.
	 *
	 * @param string $type   Interner Ereignistyp (bounce_hard|bounce_soft|complaint|unsubscribe).
	 * @param string $email  Betroffene E-Mail-Adresse.
	 * @param string $reason Optionaler Grund (fuer das Ereignis-Detail).
	 * @return string Ergebnis-Code (fuer Logging/Tests).
	 */
	public function apply( string $type, string $email, string $reason = '' ): string {
		$contact = $this->contacts->find_by_email( $email );
		if ( null === $contact ) {
			return 'unknown_contact';
		}
		$contact_id = (int) ( $contact['id'] ?? 0 );
		if ( $contact_id <= 0 ) {
			return 'unknown_contact';
		}

		$meta = array( 'detail' => substr( $reason, 0, 255 ) );

		switch ( $type ) {
			case 'bounce_hard':
				$this->contacts->set_email_status( $contact_id, 'suppressed_bounce' );
				$this->events->record( 0, $contact_id, 'bounce_hard', $meta );
				return 'suppressed_bounce';

			case 'complaint':
				$this->contacts->set_email_status( $contact_id, 'suppressed_complaint' );
				$this->subscriptions->unsubscribe_all( $contact_id );
				$this->events->record( 0, $contact_id, 'complaint', $meta );
				$this->record_revocation(
					$contact,
					__( 'Widerruf der Einwilligung durch Spam-Beschwerde beim Versanddienst.', 'kurato' ),
					'complaint'
				);
				return 'suppressed_complaint';

			case 'bounce_soft':
				// v1: nur protokollieren. Schwellwert-Suppression ist 1.1.
				$this->events->record( 0, $contact_id, 'bounce_soft', $meta );
				return 'soft_logged';

			case 'unsubscribe':
				$this->subscriptions->unsubscribe_all( $contact_id );
				$this->events->record( 0, $contact_id, 'unsubscribe', $meta );
				$this->record_revocation(
					$contact,
					__( 'Widerruf der Einwilligung durch Abmeldung ueber den Versanddienst.', 'kurato' ),
					'unsubscribe'
				);
				return 'unsubscribed';

			default:
				return 'ignored';
		}
	}

	/**
	 * Schreibt den Widerruf der Newsletter-Einwilligung ins Core-Consent-Log
	 * (gleiche Linie wie UnsubscribeController und PreferenceCenter: jeder
	 * Widerruf ist gerichtsfest nachweisbar, egal ueber welchen Kanal).
	 *
	 * @param array<string, mixed> $contact      Kontakt-Datensatz.
	 * @param string               $consent_text Klartext des Widerrufs.
	 * @param string               $reference    Referenz (complaint|unsubscribe).
	 * @return void
	 */
	private function record_revocation( array $contact, string $consent_text, string $reference ): void {
		$email = sanitize_email( (string) ( $contact['email'] ?? '' ) );
		if ( '' === $email ) {
			return;
		}
		$this->record_consent(
			array(
				'source'        => 'kurato:webhook',
				'subject_email' => $email,
				'purpose'       => 'newsletter',
				'consent_text'  => $consent_text,
				'text_version'  => '1',
				'reference'     => $reference,
			)
		);
	}

	/**
	 * Schreibt einen Consent-Nachweis. Eigene Methode als Test-Seam (Muster
	 * PreferenceCenter): Tests ueberschreiben sie, um den Aufruf ohne echtes
	 * wpdb zu pruefen.
	 *
	 * @param array<string, mixed> $data Consent-Daten (Core-ConsentLog-Format).
	 * @return int Consent-ID (0 bei Fehler).
	 */
	protected function record_consent( array $data ): int {
		return Core::instance()->consent()->record( $data );
	}
}
