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

namespace Hafenstudios\Kurato\Admin;

use Hafenstudios\Core\Core;
use Hafenstudios\Core\Mail\Message;
use Hafenstudios\Kurato\Db\QueueRepository;
use Hafenstudios\Kurato\Mail\DeliveryLog;
use Hafenstudios\Kurato\Mail\DnsCheckService;
use Hafenstudios\Kurato\Mail\DriverFactory;
use Hafenstudios\Kurato\Mail\Secrets;

defined( 'ABSPATH' ) || exit;

/**
 * Versand-Einstellungen (Etappe K4b): Absender, Transport-Auswahl, Anbieter-
 * Zugangsdaten (verschluesselt), Durchsatzrate, DNS-Ampel und Testversand.
 *
 * Serverseitig ueber admin-post (D3). API-Keys werden nie im Klartext angezeigt
 * (nur "gesetzt"/"nicht gesetzt") und ueber Secrets verschluesselt gespeichert;
 * ein leeres Feld behaelt den bestehenden Key.
 */
final class MailSettingsPage {

	public const SLUG        = 'kurato-mail';
	public const ACTION_SAVE = 'kurato_save_mail';
	public const ACTION_TEST = 'kurato_test_mail';

	private const DNS_TRANSIENT = 'kurato_dns_check';

	/**
	 * Transport-Optionen (Wert => Anzeigename).
	 *
	 * @return array<string, string>
	 */
	private function transports(): array {
		return array(
			'wp_mail'  => __( 'WordPress-Standard (nur fuer kleine Mengen)', 'kurato' ),
			'smtp'     => __( 'SMTP', 'kurato' ),
			'brevo'    => __( 'Brevo (EU)', 'kurato' ),
			'postmark' => __( 'Postmark', 'kurato' ),
			'mailgun'  => __( 'Mailgun', 'kurato' ),
			'smtp2go'  => __( 'SMTP2GO', 'kurato' ),
			'ses'      => __( 'Amazon SES', 'kurato' ),
		);
	}

	/**
	 * Registriert die admin-post-Handler.
	 *
	 * @return void
	 */
	public function register(): void {
		add_action( 'admin_post_' . self::ACTION_SAVE, array( $this, 'handle_save' ) );
		add_action( 'admin_post_' . self::ACTION_TEST, array( $this, 'handle_test' ) );
	}

	/**
	 * Settings-Speicher.
	 *
	 * @return \Hafenstudios\Core\Settings\Settings
	 */
	private function settings() {
		return Core::instance()->settings( 'kurato' );
	}

	/**
	 * Rendert die Seite.
	 *
	 * @return void
	 */
	public function render(): void {
		if ( ! current_user_can( 'hs_manage_settings' ) ) {
			wp_die( esc_html__( 'Sie haben keine Berechtigung, die Versand-Einstellungen zu aendern.', 'kurato' ) );
		}

		$s         = $this->settings();
		$transport = (string) $s->get( 'mail.transport', 'wp_mail' );

		echo '<div class="wrap hs-kurato-mail">';
		echo '<h1>' . esc_html__( 'Versand-Einstellungen', 'kurato' ) . '</h1>';

		$this->render_notice();

		echo '<form method="post" action="' . esc_url( admin_url( 'admin-post.php' ) ) . '">';
		echo '<input type="hidden" name="action" value="' . esc_attr( self::ACTION_SAVE ) . '" />';
		wp_nonce_field( self::ACTION_SAVE );

		echo '<h2>' . esc_html__( 'Absender', 'kurato' ) . '</h2>';
		echo '<table class="form-table"><tbody>';
		$this->text_row( 'from_name', __( 'Absendername', 'kurato' ), (string) $s->get( 'mail.sender.from_name', '' ) );
		$this->text_row( 'from_email', __( 'Absender-E-Mail', 'kurato' ), (string) $s->get( 'mail.sender.from_email', '' ), 'email' );
		echo '</tbody></table>';

		echo '<h2>' . esc_html__( 'Versandweg', 'kurato' ) . '</h2>';
		echo '<table class="form-table"><tbody>';
		echo '<tr><th scope="row">' . esc_html__( 'Anbieter', 'kurato' ) . '</th><td><select name="transport">';
		foreach ( $this->transports() as $value => $label ) {
			printf( '<option value="%1$s"%2$s>%3$s</option>', esc_attr( $value ), selected( $transport, $value, false ), esc_html( $label ) );
		}
		echo '</select><p class="description">' . esc_html__( 'Fuellen Sie unten die Zugangsdaten des gewaehlten Anbieters aus.', 'kurato' ) . '</p></td></tr>';
		$this->number_row( 'rate_per_hour', __( 'Mails pro Stunde (Drosselung)', 'kurato' ), (int) $s->get( 'mail.rate_per_hour', 200 ) );
		echo '</tbody></table>';

		$this->render_credentials( $s );

		$this->render_tracking_section( $s );
		$this->render_welcome_section( $s );

		echo '<p><button type="submit" class="button button-primary">' . esc_html__( 'Einstellungen speichern', 'kurato' ) . '</button></p>';
		echo '</form>';

		$this->render_dns_section( $s, $transport );
		$this->render_delivery_log_section();
		$this->render_webhook_section( $s );
		$this->render_test_section();

		echo '</div>';
	}

	/**
	 * Zeigt die fertige Brevo-Webhook-URL zum Kopieren (K4e-1). Der Betreiber
	 * traegt sie in Brevo als Event-Webhook ein; das Secret in der URL sichert den
	 * Endpunkt gegen fremde Aufrufe.
	 *
	 * @param \Hafenstudios\Core\Settings\Settings $s Settings.
	 * @return void
	 */
	private function render_webhook_section( $s ): void {
		$secret = (string) $s->get( 'webhook.brevo_secret', '' );

		echo '<h2>' . esc_html__( 'Bounce- und Beschwerde-Webhook (Brevo)', 'kurato' ) . '</h2>';

		if ( '' === $secret ) {
			echo '<p class="description">' . esc_html__( 'Die Webhook-URL steht nach der naechsten Aktivierung des Plugins bereit.', 'kurato' ) . '</p>';
			return;
		}

		$url = add_query_arg( array( 'secret' => $secret ), rest_url( 'kurato/v1/webhooks/brevo' ) );

		echo '<p class="description">' . esc_html__( 'Tragen Sie diese Adresse in Ihrem Brevo-Konto als Event-Webhook ein (fuer harte und weiche Bounces, Beschwerden und Abmeldungen). So erkennt Kurato unzustellbare Adressen automatisch.', 'kurato' ) . '</p>';
		echo '<p><input type="text" readonly value="' . esc_attr( $url ) . '" class="large-text code" onfocus="this.select();" /></p>';
	}

	/**
	 * Tracking-Abschnitt: Site-Schalter fuer die Open-/Klick-Messung (Default aus).
	 *
	 * Ehrlicher Hinweis: Tracking ist einwilligungspflichtig und misst je Kontakt
	 * nur bei dessen Einwilligung; erhoben wird die je Kontakt ueber das Preference
	 * Center (K4-Konzept Abschnitt 7).
	 *
	 * @param \Hafenstudios\Core\Settings\Settings $s Settings.
	 * @return void
	 */
	private function render_tracking_section( $s ): void {
		$enabled = (bool) $s->get( 'tracking.enabled', false );

		echo '<h2>' . esc_html__( 'Oeffnungen und Klicks messen (Tracking)', 'kurato' ) . '</h2>';
		echo '<table class="form-table"><tbody>';
		echo '<tr><th scope="row">' . esc_html__( 'Tracking aktivieren', 'kurato' ) . '</th><td>';
		echo '<label><input type="checkbox" name="tracking_enabled" value="1" ' . checked( $enabled, true, false ) . ' /> ';
		echo esc_html__( 'Oeffnungen und Klicks in Newslettern messen', 'kurato' ) . '</label>';
		echo '<p class="description">' . esc_html__( 'Tracking ist einwilligungspflichtig und getrennt von der Newsletter-Einwilligung. Auch bei aktivem Schalter wird je Kontakt nur dann gemessen, wenn dieser Kontakt der Messung ausdruecklich zugestimmt hat (Einwilligung erteilt der Kontakt selbst im Preference Center). Ohne diese doppelte Einwilligung enthalten die Mails weder Zaehlpixel noch umgeschriebene Links. Es werden keine IP-Adressen gespeichert.', 'kurato' ) . '</p>';
		echo '</td></tr>';
		echo '</tbody></table>';
	}

	/**
	 * Willkommensmail-Abschnitt: eine Mail bei Abo-Bestaetigung (Default aus),
	 * Inhalt ist ein hs_mail-Post (K4-Konzept Abschnitt 10 Punkt 5).
	 *
	 * @param \Hafenstudios\Core\Settings\Settings $s Settings.
	 * @return void
	 */
	private function render_welcome_section( $s ): void {
		$enabled = (bool) $s->get( 'welcome.enabled', false );
		$post_id = (int) $s->get( 'welcome.post_id', 0 );

		echo '<h2>' . esc_html__( 'Willkommensmail', 'kurato' ) . '</h2>';
		echo '<table class="form-table"><tbody>';

		echo '<tr><th scope="row">' . esc_html__( 'Willkommensmail senden', 'kurato' ) . '</th><td>';
		echo '<label><input type="checkbox" name="welcome_enabled" value="1" ' . checked( $enabled, true, false ) . ' /> ';
		echo esc_html__( 'Bei jeder bestaetigten Newsletter-Anmeldung einmalig eine Willkommensmail senden', 'kurato' ) . '</label>';
		echo '<p class="description">' . esc_html__( 'Eine einzelne Mail direkt nach der Bestaetigung, ohne Serie. Jeder Kontakt erhaelt sie je Liste nur einmal.', 'kurato' ) . '</p>';
		echo '</td></tr>';

		echo '<tr><th scope="row"><label for="hs-welcome_post_id">' . esc_html__( 'Inhalt der Willkommensmail', 'kurato' ) . '</label></th><td>';
		$posts = $this->mail_posts();
		if ( array() === $posts ) {
			echo '<p class="description">' . esc_html__( 'Legen Sie zuerst unter "Kampagnen" eine Mail als Inhalt an; sie erscheint dann hier zur Auswahl.', 'kurato' ) . '</p>';
		} else {
			echo '<select id="hs-welcome_post_id" name="welcome_post_id">';
			echo '<option value="0">' . esc_html__( '- keine -', 'kurato' ) . '</option>';
			foreach ( $posts as $pid => $title ) {
				printf( '<option value="%1$d"%2$s>%3$s</option>', (int) $pid, selected( $post_id, (int) $pid, false ), esc_html( $title ) );
			}
			echo '</select>';
		}
		echo '</td></tr>';

		echo '</tbody></table>';
	}

	/**
	 * Verfuegbare hs_mail-Posts (Betreff => Titel) fuer die Willkommensmail-Auswahl.
	 *
	 * @return array<int, string> Post-ID => Titel.
	 */
	private function mail_posts(): array {
		$out   = array();
		$posts = get_posts(
			array(
				'post_type'        => 'hs_mail',
				'post_status'      => array( 'draft', 'publish', 'pending', 'private' ),
				'numberposts'      => 100,
				'orderby'          => 'modified',
				'order'            => 'DESC',
				'suppress_filters' => false,
			)
		);
		foreach ( (array) $posts as $post ) {
			$id    = (int) ( $post->ID ?? 0 );
			$title = (string) ( $post->post_title ?? '' );
			if ( $id <= 0 ) {
				continue;
			}
			$out[ $id ] = '' !== trim( $title ) ? $title : sprintf( /* translators: %d: Post-ID */ __( '(ohne Betreff, #%d)', 'kurato' ), $id );
		}
		return $out;
	}

	/**
	 * Zugangsdaten-Felder je Anbieter.
	 *
	 * @param \Hafenstudios\Core\Settings\Settings $s Settings.
	 * @return void
	 */
	private function render_credentials( $s ): void {
		echo '<h2>' . esc_html__( 'Zugangsdaten', 'kurato' ) . '</h2>';
		echo '<table class="form-table"><tbody>';

		$this->secret_row( 'brevo_api_key', __( 'Brevo: API-Key', 'kurato' ), (string) $s->get( 'mail.brevo.api_key', '' ) );
		$this->secret_row( 'postmark_token', __( 'Postmark: Server-Token', 'kurato' ), (string) $s->get( 'mail.postmark.token', '' ) );
		$this->secret_row( 'mailgun_api_key', __( 'Mailgun: API-Key', 'kurato' ), (string) $s->get( 'mail.mailgun.api_key', '' ) );
		$this->text_row( 'mailgun_domain', __( 'Mailgun: Sende-Domain', 'kurato' ), (string) $s->get( 'mail.mailgun.domain', '' ) );
		$this->select_row( 'mailgun_region', __( 'Mailgun: Region', 'kurato' ), (string) $s->get( 'mail.mailgun.region', 'us' ), array( 'us' => 'US', 'eu' => 'EU' ) );
		$this->secret_row( 'smtp2go_api_key', __( 'SMTP2GO: API-Key', 'kurato' ), (string) $s->get( 'mail.smtp2go.api_key', '' ) );
		$this->text_row( 'ses_access_key', __( 'SES: Access Key ID', 'kurato' ), (string) $s->get( 'mail.ses.access_key', '' ) );
		$this->secret_row( 'ses_secret_key', __( 'SES: Secret Access Key', 'kurato' ), (string) $s->get( 'mail.ses.secret_key', '' ) );
		$this->text_row( 'ses_region', __( 'SES: Region', 'kurato' ), (string) $s->get( 'mail.ses.region', 'eu-central-1' ) );

		echo '</tbody></table>';
	}

	/**
	 * DNS-Ampel fuer die Absender-Domain.
	 *
	 * @param \Hafenstudios\Core\Settings\Settings $s         Settings.
	 * @param string                               $transport Transport.
	 * @return void
	 */
	private function render_dns_section( $s, string $transport ): void {
		$from = (string) $s->get( 'mail.sender.from_email', '' );
		if ( '' === $from || false === strpos( $from, '@' ) ) {
			return;
		}
		$domain = strtolower( substr( strrchr( $from, '@' ), 1 ) );

		echo '<h2>' . esc_html__( 'Zustellbarkeit (SPF, DKIM, DMARC)', 'kurato' ) . '</h2>';

		$check = $this->cached_dns_check( $domain, $transport );

		echo '<table class="wp-list-table widefat striped" style="max-width:720px;"><tbody>';
		foreach ( array( 'spf' => 'SPF', 'dkim' => 'DKIM', 'dmarc' => 'DMARC' ) as $key => $label ) {
			$item = $check[ $key ];
			echo '<tr><td style="width:80px;"><strong>' . esc_html( $label ) . '</strong></td>';
			echo '<td>' . $this->status_badge( (string) $item['status'] ) . '</td>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- status_badge escaped.
			echo '<td>' . esc_html( (string) $item['message'] ) . '</td></tr>';
		}
		echo '</tbody></table>';

		if ( ! $check['can_send'] ) {
			echo '<p class="notice notice-warning" style="padding:8px 12px;">' . esc_html__( 'Kampagnenversand ist gesperrt, bis SPF und DKIM stimmen. Testmails und Bestaetigungsmails bleiben moeglich.', 'kurato' ) . '</p>';
		}
		if ( ! $check['checkable'] ) {
			echo '<p class="description">' . esc_html__( 'Die Eintraege konnten auf diesem Server nicht automatisch geprueft werden.', 'kurato' ) . '</p>';
			echo '<p><label><input type="checkbox" disabled ' . checked( (bool) $s->get( 'dns.doh_enabled', false ), true, false ) . ' /> ' . esc_html__( 'DNS-over-HTTPS ist unten aktivierbar, um die Pruefung dennoch durchzufuehren.', 'kurato' ) . '</label></p>';
		}

		// Erneut pruefen + DoH-Schalter.
		echo '<form method="post" action="' . esc_url( admin_url( 'admin-post.php' ) ) . '" style="margin-top:8px;">';
		echo '<input type="hidden" name="action" value="' . esc_attr( self::ACTION_SAVE ) . '" />';
		echo '<input type="hidden" name="recheck_dns" value="1" />';
		wp_nonce_field( self::ACTION_SAVE );
		echo '<label><input type="checkbox" name="doh_enabled" value="1" ' . checked( (bool) $s->get( 'dns.doh_enabled', false ), true, false ) . ' /> ';
		echo esc_html__( 'DNS-over-HTTPS erlauben (fragt einmalig einen externen DNS-Dienst, wenn die lokale Pruefung blockiert ist)', 'kurato' ) . '</label> ';
		echo '<button type="submit" class="button">' . esc_html__( 'Erneut pruefen', 'kurato' ) . '</button>';
		echo '</form>';
	}

	/**
	 * Zustell-Protokoll (K-M1): letzter erfolgreicher Versand und die letzten
	 * Fehlversande (Core-Mailer-Ereignisse plus fehlgeschlagene Queue-Zeilen).
	 * Datensparsam: maskierte Empfaenger, keine Mail-Inhalte, keine IP.
	 *
	 * @return void
	 */
	private function render_delivery_log_section(): void {
		echo '<h2>' . esc_html__( 'Zustell-Protokoll', 'kurato' ) . '</h2>';

		$last_ok = DeliveryLog::last_ok();
		if ( null !== $last_ok ) {
			echo '<p>' . esc_html(
				sprintf(
					/* translators: 1: Zeitpunkt (UTC), 2: maskierter Empfaenger */
					__( 'Letzter erfolgreicher Versand: %1$s UTC an %2$s.', 'kurato' ),
					(string) ( $last_ok['time'] ?? '' ),
					(string) ( $last_ok['to'] ?? '' )
				)
			) . '</p>';
		}

		$failures       = DeliveryLog::failures();
		$queue_failures = ( new QueueRepository() )->recent_failures( 20 );

		if ( array() === $failures && array() === $queue_failures ) {
			echo '<p class="description">' . esc_html__( 'Bisher sind keine Fehlversande aufgetreten. Alle uebergebenen Mails wurden vom Versandweg angenommen.', 'kurato' ) . '</p>';
			return;
		}

		if ( array() !== $failures ) {
			echo '<h3>' . esc_html__( 'Letzte Fehlversande (Transaktionsmails)', 'kurato' ) . '</h3>';
			echo '<table class="wp-list-table widefat striped" style="max-width:820px;"><thead><tr>';
			echo '<th>' . esc_html__( 'Zeit (UTC)', 'kurato' ) . '</th>';
			echo '<th>' . esc_html__( 'Empfaenger', 'kurato' ) . '</th>';
			echo '<th>' . esc_html__( 'Betreff', 'kurato' ) . '</th>';
			echo '<th>' . esc_html__( 'Versandweg', 'kurato' ) . '</th>';
			echo '</tr></thead><tbody>';
			foreach ( $failures as $row ) {
				echo '<tr>';
				echo '<td>' . esc_html( (string) ( $row['time'] ?? '' ) ) . '</td>';
				echo '<td>' . esc_html( (string) ( $row['to'] ?? '' ) ) . '</td>';
				echo '<td>' . esc_html( (string) ( $row['subject'] ?? '' ) ) . '</td>';
				echo '<td>' . esc_html( (string) ( $row['driver'] ?? '' ) ) . '</td>';
				echo '</tr>';
			}
			echo '</tbody></table>';
		}

		if ( array() !== $queue_failures ) {
			echo '<h3>' . esc_html__( 'Letzte Fehlversande (Kampagnen-Queue)', 'kurato' ) . '</h3>';
			echo '<table class="wp-list-table widefat striped" style="max-width:820px;"><thead><tr>';
			echo '<th>' . esc_html__( 'Empfaenger', 'kurato' ) . '</th>';
			echo '<th>' . esc_html__( 'Versuche', 'kurato' ) . '</th>';
			echo '<th>' . esc_html__( 'Fehlermeldung', 'kurato' ) . '</th>';
			echo '</tr></thead><tbody>';
			foreach ( $queue_failures as $row ) {
				echo '<tr>';
				echo '<td>' . esc_html( DeliveryLog::mask_email( (string) ( $row['to_email'] ?? '' ) ) ) . '</td>';
				echo '<td>' . esc_html( (string) (int) ( $row['attempts'] ?? 0 ) ) . '</td>';
				echo '<td>' . esc_html( (string) ( $row['last_error'] ?? '' ) ) . '</td>';
				echo '</tr>';
			}
			echo '</tbody></table>';
		}
	}

	/**
	 * Testversand-Bereich.
	 *
	 * @return void
	 */
	private function render_test_section(): void {
		$to = (string) $this->settings()->get( 'mail.sender.from_email', '' );
		if ( '' === $to ) {
			$to = (string) get_option( 'admin_email', '' );
		}

		echo '<h2>' . esc_html__( 'Testmail senden', 'kurato' ) . '</h2>';
		echo '<form method="post" action="' . esc_url( admin_url( 'admin-post.php' ) ) . '">';
		echo '<input type="hidden" name="action" value="' . esc_attr( self::ACTION_TEST ) . '" />';
		wp_nonce_field( self::ACTION_TEST );
		echo '<p><label>' . esc_html__( 'Empfaenger', 'kurato' ) . ' <input type="email" name="test_to" value="' . esc_attr( $to ) . '" class="regular-text" required /></label> ';
		echo '<button type="submit" class="button">' . esc_html__( 'Testmail senden', 'kurato' ) . '</button></p>';
		echo '<p class="description">' . esc_html__( 'Sendet eine Testmail ueber den aktuell gespeicherten Versandweg.', 'kurato' ) . '</p>';
		echo '</form>';
	}

	/**
	 * Speichert die Einstellungen (admin-post).
	 *
	 * @return void
	 */
	public function handle_save(): void {
		if ( ! current_user_can( 'hs_manage_settings' ) ) {
			wp_die( esc_html__( 'Keine Berechtigung.', 'kurato' ) );
		}
		check_admin_referer( self::ACTION_SAVE );

		$s = $this->settings();

		// Nur beim Voll-Speichern (nicht beim reinen DNS-Recheck) alle Felder schreiben.
		if ( empty( $_POST['recheck_dns'] ) ) {
			$s->set( 'mail.sender.from_name', sanitize_text_field( wp_unslash( $_POST['from_name'] ?? '' ) ) );
			$s->set( 'mail.sender.from_email', sanitize_email( wp_unslash( $_POST['from_email'] ?? '' ) ) );
			$s->set( 'mail.transport', $this->clean_transport( (string) ( $_POST['transport'] ?? 'wp_mail' ) ) );
			$s->set( 'mail.rate_per_hour', max( 1, min( 100000, absint( $_POST['rate_per_hour'] ?? 200 ) ) ) );

			$s->set( 'mail.mailgun.domain', sanitize_text_field( wp_unslash( $_POST['mailgun_domain'] ?? '' ) ) );
			$s->set( 'mail.mailgun.region', 'eu' === ( $_POST['mailgun_region'] ?? 'us' ) ? 'eu' : 'us' );
			$s->set( 'mail.ses.access_key', sanitize_text_field( wp_unslash( $_POST['ses_access_key'] ?? '' ) ) );
			$s->set( 'mail.ses.region', sanitize_text_field( wp_unslash( $_POST['ses_region'] ?? '' ) ) );

			$this->save_secret( 'mail.brevo.api_key', 'brevo_api_key' );
			$this->save_secret( 'mail.postmark.token', 'postmark_token' );
			$this->save_secret( 'mail.mailgun.api_key', 'mailgun_api_key' );
			$this->save_secret( 'mail.smtp2go.api_key', 'smtp2go_api_key' );
			$this->save_secret( 'mail.ses.secret_key', 'ses_secret_key' );

			// Tracking (Site-Schalter, Default aus) und Willkommensmail.
			$s->set( 'tracking.enabled', ! empty( $_POST['tracking_enabled'] ) );
			$s->set( 'welcome.enabled', ! empty( $_POST['welcome_enabled'] ) );
			$s->set( 'welcome.post_id', absint( $_POST['welcome_post_id'] ?? 0 ) );
		}

		$s->set( 'dns.doh_enabled', ! empty( $_POST['doh_enabled'] ) );
		delete_transient( self::DNS_TRANSIENT ); // frische Pruefung erzwingen

		$this->redirect( 'saved' );
	}

	/**
	 * Sendet eine Testmail (admin-post).
	 *
	 * @return void
	 */
	public function handle_test(): void {
		if ( ! current_user_can( 'hs_manage_settings' ) ) {
			wp_die( esc_html__( 'Keine Berechtigung.', 'kurato' ) );
		}
		check_admin_referer( self::ACTION_TEST );

		$to = sanitize_email( wp_unslash( $_POST['test_to'] ?? '' ) );
		if ( '' === $to ) {
			$this->redirect( 'test_failed' );
		}

		$s       = $this->settings();
		$message = new Message(
			$to,
			__( 'Kurato Testmail', 'kurato' ),
			__( 'Das ist eine Testmail von Kurato. Wenn Sie diese Nachricht sehen, funktioniert Ihr Versandweg.', 'kurato' )
		);
		$message->is_html    = false;
		$message->from_name  = (string) $s->get( 'mail.sender.from_name', '' );
		$message->from_email = (string) $s->get( 'mail.sender.from_email', '' );
		if ( '' === $message->from_email ) {
			$message->from_email = (string) get_option( 'admin_email', '' );
		}

		$ok = ( new DriverFactory() )->current()->send( $message );
		$this->redirect( $ok ? 'test_sent' : 'test_failed' );
	}

	// --- Helfer ------------------------------------------------------------

	/**
	 * Verschluesselt und speichert einen Key, sofern das Feld ausgefuellt ist.
	 *
	 * @param string $path  Settings-Pfad.
	 * @param string $field POST-Feldname.
	 * @return void
	 */
	private function save_secret( string $path, string $field ): void {
		$value = trim( (string) wp_unslash( $_POST[ $field ] ?? '' ) );
		if ( '' === $value ) {
			return; // leer = bestehenden Key behalten
		}
		$this->settings()->set( $path, Secrets::encrypt( $value ) );
	}

	/**
	 * Validiert einen Transport-Wert.
	 *
	 * @param string $value Rohwert.
	 * @return string
	 */
	private function clean_transport( string $value ): string {
		return array_key_exists( $value, $this->transports() ) ? $value : 'wp_mail';
	}

	/**
	 * DNS-Check mit 12-Stunden-Cache.
	 *
	 * @param string $domain    Domain.
	 * @param string $transport Transport.
	 * @return array<string, mixed>
	 */
	private function cached_dns_check( string $domain, string $transport ): array {
		$cache = get_transient( self::DNS_TRANSIENT );
		if ( is_array( $cache ) && ( $cache['domain'] ?? '' ) === $domain && ( $cache['transport'] ?? '' ) === $transport ) {
			return $cache['result'];
		}
		$service = new DnsCheckService( (bool) $this->settings()->get( 'dns.doh_enabled', false ) );
		$result  = $service->check( $domain, $transport );
		set_transient( self::DNS_TRANSIENT, array( 'domain' => $domain, 'transport' => $transport, 'result' => $result ), 12 * HOUR_IN_SECONDS );
		return $result;
	}

	/**
	 * Statusfarbiges Badge.
	 *
	 * @param string $status Status.
	 * @return string
	 */
	private function status_badge( string $status ): string {
		$labels = array(
			DnsCheckService::PASS    => __( 'In Ordnung', 'kurato' ),
			DnsCheckService::WARN    => __( 'Hinweis', 'kurato' ),
			DnsCheckService::FAIL    => __( 'Fehlt', 'kurato' ),
			DnsCheckService::UNKNOWN => __( 'Nicht pruefbar', 'kurato' ),
		);
		$classes = array(
			DnsCheckService::PASS    => 'hs-shield--green',
			DnsCheckService::WARN    => 'hs-shield--amber',
			DnsCheckService::FAIL    => 'hs-shield--amber',
			DnsCheckService::UNKNOWN => 'hs-shield--grey',
		);
		$label = $labels[ $status ] ?? $status;
		$class = $classes[ $status ] ?? 'hs-shield--grey';
		return '<span class="hs-shield ' . esc_attr( $class ) . '">' . esc_html( $label ) . '</span>';
	}

	private function render_notice(): void {
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nur Anzeige nach Redirect.
		$msg = isset( $_GET['msg'] ) ? sanitize_key( wp_unslash( $_GET['msg'] ) ) : '';
		$map = array(
			'saved'       => array( 'success', __( 'Einstellungen gespeichert.', 'kurato' ) ),
			'test_sent'   => array( 'success', __( 'Testmail wurde uebergeben. Bitte pruefen Sie Ihr Postfach.', 'kurato' ) ),
			'test_failed' => array( 'error', __( 'Die Testmail konnte nicht gesendet werden. Bitte pruefen Sie die Zugangsdaten.', 'kurato' ) ),
		);
		if ( isset( $map[ $msg ] ) ) {
			printf( '<div class="notice notice-%1$s is-dismissible"><p>%2$s</p></div>', esc_attr( $map[ $msg ][0] ), esc_html( $map[ $msg ][1] ) );
		}
	}

	private function text_row( string $name, string $label, string $value, string $type = 'text' ): void {
		echo '<tr><th scope="row"><label for="hs-' . esc_attr( $name ) . '">' . esc_html( $label ) . '</label></th>';
		echo '<td><input type="' . esc_attr( $type ) . '" id="hs-' . esc_attr( $name ) . '" name="' . esc_attr( $name ) . '" value="' . esc_attr( $value ) . '" class="regular-text" /></td></tr>';
	}

	private function number_row( string $name, string $label, int $value ): void {
		echo '<tr><th scope="row"><label for="hs-' . esc_attr( $name ) . '">' . esc_html( $label ) . '</label></th>';
		echo '<td><input type="number" min="1" id="hs-' . esc_attr( $name ) . '" name="' . esc_attr( $name ) . '" value="' . esc_attr( (string) $value ) . '" /></td></tr>';
	}

	private function select_row( string $name, string $label, string $value, array $options ): void {
		echo '<tr><th scope="row">' . esc_html( $label ) . '</th><td><select name="' . esc_attr( $name ) . '">';
		foreach ( $options as $val => $text ) {
			printf( '<option value="%1$s"%2$s>%3$s</option>', esc_attr( $val ), selected( $value, $val, false ), esc_html( $text ) );
		}
		echo '</select></td></tr>';
	}

	private function secret_row( string $name, string $label, string $stored ): void {
		$is_set      = '' !== $stored;
		$placeholder = $is_set ? __( 'gesetzt (leer lassen zum Behalten)', 'kurato' ) : __( 'nicht gesetzt', 'kurato' );
		echo '<tr><th scope="row"><label for="hs-' . esc_attr( $name ) . '">' . esc_html( $label ) . '</label></th>';
		echo '<td><input type="password" autocomplete="new-password" id="hs-' . esc_attr( $name ) . '" name="' . esc_attr( $name ) . '" value="" placeholder="' . esc_attr( $placeholder ) . '" class="regular-text" /></td></tr>';
	}

	private function redirect( string $msg ): void {
		wp_safe_redirect( add_query_arg( array( 'page' => self::SLUG, 'msg' => $msg ), admin_url( 'admin.php' ) ) );
		exit;
	}
}
