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

namespace Hafenstudios\Leadlotse\Forms;

use Hafenstudios\Leadlotse\Fields\ConditionalLogic;
use Hafenstudios\Leadlotse\Fields\FieldRegistry;
use Hafenstudios\Leadlotse\Fields\Types\HoneypotField;

defined( 'ABSPATH' ) || exit;

/**
 * Validierungs- und Normalisierungs-Schema fuer die Formular-Definition
 * (`_leadlotse_definition`, D4).
 *
 * Struktur (Version 1):
 * {
 *   "version": 1,
 *   "purpose": "contact",
 *   "language": "de",
 *   "fields": [ { id, type, label, required, help, placeholder, options,
 *                 default, max_length, display, capture, consent_text,
 *                 text_version, conditions, whatsapp_optin, whatsapp_optin_text } ],
 *   "settings": {
 *     "recipients": [ "mail@..." ],
 *     "notification": { "enabled": bool, "subject": "..." },
 *     "confirmation": { "enabled": bool, "subject": "...", "body": "..." },
 *     "doi": { "enabled": bool },
 *     "success_message": "...",
 *     "redirect_url": "...",
 *     "spam": { "min_seconds": int },
 *     "whatsapp": { "enabled": bool, "number": "...", "button_label": "...",
 *                   "prefill_text": "...", "hint_text": "..." }
 *   }
 * }
 *
 * Feld-IDs, die mit einem Unterstrich beginnen, sind reserviert (technischer
 * Namensraum fuer Eingaben wie `_wa_{feldid}`) und werden abgewiesen.
 */
final class DefinitionSchema {

	public const VERSION = 1;

	private FieldRegistry $registry;

	private ConditionalLogic $conditions;

	/**
	 * @param FieldRegistry    $registry   Feldtyp-Registry.
	 * @param ConditionalLogic $conditions Conditional-Logic-Service.
	 */
	public function __construct( FieldRegistry $registry, ConditionalLogic $conditions ) {
		$this->registry   = $registry;
		$this->conditions = $conditions;
	}

	/**
	 * Validiert und normalisiert eine eingereichte Definition.
	 *
	 * @param mixed $raw Rohdaten (Array aus JSON).
	 * @return array<string, mixed>|\WP_Error Normalisierte Definition oder Fehler.
	 */
	public function sanitize( $raw ) {
		if ( ! is_array( $raw ) ) {
			return new \WP_Error(
				'leadlotse_invalid_definition',
				__( 'Die Formular-Definition ist kein gueltiges Objekt. Bitte laden Sie den Builder neu und speichern Sie erneut.', 'leadlotse' ),
				array( 'status' => 400 )
			);
		}

		$fields    = array();
		$field_ids = array();

		foreach ( (array) ( $raw['fields'] ?? array() ) as $field ) {
			if ( ! is_array( $field ) ) {
				continue;
			}

			$type = sanitize_key( (string) ( $field['type'] ?? '' ) );
			if ( 'honeypot' === $type ) {
				// Honeypot wird automatisch verwaltet, nie aus der Definition uebernommen.
				continue;
			}
			if ( ! $this->registry->has( $type ) ) {
				return new \WP_Error(
					'leadlotse_unknown_field_type',
					sprintf(
						/* translators: %s: Feldtyp */
						__( 'Der Feldtyp "%s" ist nicht verfuegbar. Bitte entfernen Sie das Feld oder aktualisieren Sie das Plugin.', 'leadlotse' ),
						$type
					),
					array( 'status' => 400 )
				);
			}

			$id = sanitize_key( (string) ( $field['id'] ?? '' ) );
			if ( '' === $id || isset( $field_ids[ $id ] ) || HoneypotField::FIELD_ID === $id || 0 === strpos( $id, '_' ) ) {
				return new \WP_Error(
					'leadlotse_invalid_field_id',
					__( 'Ein Feld hat eine fehlende oder doppelte ID. Bitte benennen Sie die Feld-IDs eindeutig. IDs duerfen nicht mit einem Unterstrich beginnen.', 'leadlotse' ),
					array( 'status' => 400 )
				);
			}
			$field_ids[ $id ] = true;

			$clean = array(
				'id'          => $id,
				'type'        => $type,
				'label'       => sanitize_text_field( (string) ( $field['label'] ?? '' ) ),
				'required'    => ! empty( $field['required'] ),
				'help'        => sanitize_text_field( (string) ( $field['help'] ?? '' ) ),
				'placeholder' => sanitize_text_field( (string) ( $field['placeholder'] ?? '' ) ),
				'default'     => sanitize_text_field( (string) ( $field['default'] ?? '' ) ),
				'conditions'  => $this->conditions->sanitize( $field['conditions'] ?? null ),
			);

			if ( isset( $field['max_length'] ) ) {
				$clean['max_length'] = absint( $field['max_length'] );
			}
			if ( 'select' === $type ) {
				$clean['options'] = $this->sanitize_options( $field['options'] ?? array() );
				$clean['display'] = 'radio' === (string) ( $field['display'] ?? '' ) ? 'radio' : 'dropdown';
			}
			if ( 'hidden' === $type ) {
				$clean['capture'] = sanitize_key( (string) ( $field['capture'] ?? '' ) );
			}
			if ( 'number' === $type ) {
				if ( isset( $field['min'] ) ) {
					$clean['min'] = absint( $field['min'] );
				}
				if ( isset( $field['max'] ) ) {
					$clean['max'] = absint( $field['max'] );
				}
			}
			if ( 'phone' === $type ) {
				if ( isset( $field['whatsapp_optin'] ) ) {
					$clean['whatsapp_optin'] = ! empty( $field['whatsapp_optin'] );
				}
				if ( isset( $field['whatsapp_optin_text'] ) ) {
					$clean['whatsapp_optin_text'] = sanitize_text_field( (string) $field['whatsapp_optin_text'] );
				}
			}
			if ( 'consent' === $type ) {
				$clean['required'] = true;
				// Einen Datenschutz-Link zulassen (der Renderer erlaubt <a> ebenfalls).
				// Sonst wuerde der im Einwilligungstext versprochene Link verschwinden.
				$clean['consent_text'] = wp_kses(
					(string) ( $field['consent_text'] ?? '' ),
					array( 'a' => array( 'href' => array(), 'target' => array(), 'rel' => array() ) )
				);
				$clean['text_version'] = sanitize_text_field( (string) ( $field['text_version'] ?? '1' ) );
			}

			$fields[] = $clean;
		}

		// Conditional-Regeln duerfen nur auf existierende Felder zeigen.
		foreach ( $fields as $i => $field ) {
			foreach ( (array) ( $field['conditions']['rules'] ?? array() ) as $j => $rule ) {
				if ( ! isset( $field_ids[ $rule['field'] ] ) || $rule['field'] === $field['id'] ) {
					unset( $fields[ $i ]['conditions']['rules'][ $j ] );
				}
			}
			$fields[ $i ]['conditions']['rules'] = array_values( $fields[ $i ]['conditions']['rules'] );
			if ( array() === $fields[ $i ]['conditions']['rules'] ) {
				$fields[ $i ]['conditions']['enabled'] = false;
			}
		}

		$settings = is_array( $raw['settings'] ?? null ) ? $raw['settings'] : array();

		return array(
			'version'  => self::VERSION,
			'purpose'  => sanitize_key( (string) ( $raw['purpose'] ?? 'contact' ) ),
			'language' => 'en' === (string) ( $raw['language'] ?? 'de' ) ? 'en' : 'de',
			'fields'   => $fields,
			'settings' => $this->sanitize_settings( $settings ),
		);
	}

	/**
	 * Bereinigt die Options-Liste eines Auswahl-Felds.
	 *
	 * @param mixed $options Rohdaten.
	 * @return array<int, array{value:string,label:string}>
	 */
	private function sanitize_options( $options ): array {
		$clean = array();
		foreach ( (array) $options as $option ) {
			if ( is_array( $option ) ) {
				$value = sanitize_text_field( (string) ( $option['value'] ?? ( $option['label'] ?? '' ) ) );
				$label = sanitize_text_field( (string) ( $option['label'] ?? $value ) );
			} else {
				$value = sanitize_text_field( (string) $option );
				$label = $value;
			}
			if ( '' !== $value ) {
				$clean[] = array(
					'value' => $value,
					'label' => $label,
				);
			}
		}
		return $clean;
	}

	/**
	 * Bereinigt die Formular-Einstellungen.
	 *
	 * @param array<string, mixed> $settings Rohdaten.
	 * @return array<string, mixed>
	 */
	private function sanitize_settings( array $settings ): array {
		$recipients = array();
		foreach ( (array) ( $settings['recipients'] ?? array() ) as $recipient ) {
			$email = sanitize_email( (string) $recipient );
			if ( '' !== $email && is_email( $email ) ) {
				$recipients[] = $email;
			}
		}

		$notification = is_array( $settings['notification'] ?? null ) ? $settings['notification'] : array();
		$confirmation = is_array( $settings['confirmation'] ?? null ) ? $settings['confirmation'] : array();
		$doi          = is_array( $settings['doi'] ?? null ) ? $settings['doi'] : array();
		$spam         = is_array( $settings['spam'] ?? null ) ? $settings['spam'] : array();
		$whatsapp     = is_array( $settings['whatsapp'] ?? null ) ? $settings['whatsapp'] : array();
		$identity     = is_array( $settings['identity'] ?? null ) ? $settings['identity'] : array();
		$event        = is_array( $settings['event'] ?? null ) ? $settings['event'] : array();

		return array(
			'recipients'      => $recipients,
			'notification'    => array(
				'enabled' => ! isset( $notification['enabled'] ) || ! empty( $notification['enabled'] ),
				'subject' => sanitize_text_field( (string) ( $notification['subject'] ?? '' ) ),
			),
			'confirmation'    => array(
				'enabled' => ! empty( $confirmation['enabled'] ),
				'subject' => sanitize_text_field( (string) ( $confirmation['subject'] ?? '' ) ),
				'body'    => sanitize_textarea_field( (string) ( $confirmation['body'] ?? '' ) ),
			),
			'doi'             => array(
				'enabled' => ! empty( $doi['enabled'] ),
			),
			'success_message' => sanitize_textarea_field( (string) ( $settings['success_message'] ?? '' ) ),
			'redirect_url'    => esc_url_raw( (string) ( $settings['redirect_url'] ?? '' ) ),
			'spam'            => array(
				'min_seconds' => min( 60, max( 0, absint( $spam['min_seconds'] ?? 3 ) ) ),
			),
			'whatsapp'        => array(
				'enabled'      => ! empty( $whatsapp['enabled'] ),
				'number'       => sanitize_text_field( (string) ( $whatsapp['number'] ?? '' ) ),
				'button_label' => sanitize_text_field( (string) ( $whatsapp['button_label'] ?? '' ) ),
				'prefill_text' => sanitize_textarea_field( (string) ( $whatsapp['prefill_text'] ?? '' ) ),
				'hint_text'    => sanitize_textarea_field( (string) ( $whatsapp['hint_text'] ?? '' ) ),
			),
			'identity'        => array(
				// Ausfuellhilfe-Knoepfe: Default an, wenn der Schluessel fehlt.
				'enabled' => ! isset( $identity['enabled'] ) || ! empty( $identity['enabled'] ),
			),
			'event'           => array(
				// Veranstaltungs-Anmeldung (L-R1): Titel/Datum/Ort erscheinen in
				// Mails und auf dem Ticket; Kapazitaet 0 = unbegrenzt.
				'enabled'  => ! empty( $event['enabled'] ),
				'title'    => sanitize_text_field( (string) ( $event['title'] ?? '' ) ),
				'date'     => sanitize_text_field( (string) ( $event['date'] ?? '' ) ),
				'location' => sanitize_text_field( (string) ( $event['location'] ?? '' ) ),
				'url'      => esc_url_raw( (string) ( $event['url'] ?? '' ) ),
				'capacity' => absint( $event['capacity'] ?? 0 ),
			),
		);
	}
}
