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

namespace Hafenstudios\Kurato\Privacy;

use Hafenstudios\Core\Events\EventBus;
use Hafenstudios\Kurato\Db\BookingRepository;
use Hafenstudios\Kurato\Db\BookingTypeRepository;
use Hafenstudios\Kurato\Db\ContactRepository;
use Hafenstudios\Kurato\Db\MailEventRepository;
use Hafenstudios\Kurato\Db\QueueRepository;
use Hafenstudios\Kurato\Db\SubscriptionRepository;
use Hafenstudios\Kurato\Db\TagRepository;
use Hafenstudios\Kurato\Db\TaskRepository;

defined( 'ABSPATH' ) || exit;

/**
 * Bindet die Kurato-Kontakte an die Privacy-Werkzeuge des Core (WP-Exporter und
 * -Eraser laufen ueber die Flotten-Filter `privacy_export` / `privacy_erase`).
 *
 * Kontakte sind die persoenlichsten Daten der Flotte; ihr vollstaendiger Export
 * und die Loeschung (inklusive Tags, Wiedervorlagen, Stufen-Verlauf, Abos sowie
 * Versand- und Mail-Ereignissen) sind Pflicht fuer den DSGVO-first-Anspruch
 * (BAUPLAN 29). Die Loeschung der Abo-, Queue- und Ereignis-Zeilen erfolgt
 * kaskadierend in ContactRepository::delete(); hier wird sie nur exportiert.
 * Buchungen (hs_bookings) haengen NICHT an der Kontakt-Kaskade und werden deshalb
 * eigens exportiert und bei der Loeschung ueber BookingRepository::delete_by_contact
 * entfernt (K5c: Loeschung des Kontakts loescht seine Buchungen hart - ein
 * vergangener Termin ist ohne Personenbezug wertlos; die Retention raeumt
 * verwaiste Alt-Termine ohnehin).
 */
final class PrivacyBridge {

	private ContactRepository $contacts;

	private TagRepository $tags;

	private TaskRepository $tasks;

	private SubscriptionRepository $subscriptions;

	private QueueRepository $queue;

	private MailEventRepository $events;

	private BookingRepository $bookings;

	private BookingTypeRepository $booking_types;

	/**
	 * @param ContactRepository      $contacts      Kontakt-Repository.
	 * @param TagRepository          $tags          Tag-Repository.
	 * @param TaskRepository         $tasks         Task-Repository.
	 * @param SubscriptionRepository $subscriptions Abo-Repository.
	 * @param QueueRepository        $queue         Queue-Repository.
	 * @param MailEventRepository    $events        Ereignis-Log.
	 * @param BookingRepository      $bookings      Buchungs-Repository (K5c).
	 * @param BookingTypeRepository  $booking_types Terminarten-Repository (K5c).
	 */
	public function __construct( ContactRepository $contacts, TagRepository $tags, TaskRepository $tasks, SubscriptionRepository $subscriptions, QueueRepository $queue, MailEventRepository $events, BookingRepository $bookings, BookingTypeRepository $booking_types ) {
		$this->contacts      = $contacts;
		$this->tags          = $tags;
		$this->tasks         = $tasks;
		$this->subscriptions = $subscriptions;
		$this->queue         = $queue;
		$this->events        = $events;
		$this->bookings      = $bookings;
		$this->booking_types = $booking_types;
	}

	/**
	 * Registriert die Filter.
	 *
	 * @return void
	 */
	public function register(): void {
		EventBus::add_filter( 'privacy_export', array( $this, 'export' ), 10, 2 );
		EventBus::add_filter( 'privacy_erase', array( $this, 'erase' ), 10, 2 );
	}

	/**
	 * Export-Items fuer eine E-Mail.
	 *
	 * @param array<int, array<string, mixed>> $items Bisherige Items.
	 * @param string                           $email E-Mail.
	 * @return array<int, array<string, mixed>>
	 */
	public function export( array $items, string $email ): array {
		foreach ( $this->contacts->ids_by_email( $email ) as $id ) {
			$contact = $this->contacts->get( $id );
			if ( null === $contact ) {
				continue;
			}

			$data = array(
				array( 'name' => __( 'Name', 'kurato' ), 'value' => (string) $contact['display_name'] ),
				array( 'name' => __( 'E-Mail', 'kurato' ), 'value' => (string) $contact['email'] ),
				array( 'name' => __( 'Telefon', 'kurato' ), 'value' => (string) $contact['phone'] ),
				array( 'name' => __( 'Quelle', 'kurato' ), 'value' => (string) $contact['source'] ),
				array( 'name' => __( 'Hinzugefuegt am', 'kurato' ), 'value' => (string) $contact['created_at'] ),
			);

			$tag_labels = array_map(
				static function ( array $tag ): string {
					return (string) $tag['label'];
				},
				$this->tags->tags_for( $id )
			);
			if ( array() !== $tag_labels ) {
				$data[] = array( 'name' => __( 'Tags', 'kurato' ), 'value' => implode( ', ', $tag_labels ) );
			}

			foreach ( $this->tasks->for_contact( $id ) as $task ) {
				$data[] = array(
					'name'  => __( 'Wiedervorlage', 'kurato' ),
					'value' => (string) $task['title'] . ( '' !== $task['due_date'] ? ' (' . $task['due_date'] . ')' : '' ),
				);
			}

			// CP5: Notizen am Kontakt gehoeren in die Auskunft (sie koennen
			// personenbezogene Angaben enthalten). Geloescht werden sie mit dem
			// Kontakt (contact_meta haengt an der Kontakt-Loeschung).
			foreach ( $this->contacts->notes( $id ) as $note ) {
				$data[] = array(
					'name'  => __( 'Notiz', 'kurato' ),
					'value' => (string) $note['note'] . ( '' !== (string) $note['created_at'] ? ' (' . $note['created_at'] . ')' : '' ),
				);
			}

			foreach ( $this->contacts->stage_history( $id ) as $step ) {
				$data[] = array(
					'name'  => __( 'Stufenwechsel', 'kurato' ),
					'value' => ( '' !== $step['from_label'] ? $step['from_label'] . ' -> ' : '' ) . $step['to_label'] . ' (' . $step['changed_at'] . ')',
				);
			}

			foreach ( $this->subscriptions->for_contact( $id ) as $sub ) {
				$when = '' !== (string) ( $sub['confirmed_at'] ?? '' ) ? $sub['confirmed_at'] : (string) ( $sub['subscribed_at'] ?? '' );
				$data[] = array(
					'name'  => __( 'Newsletter-Abo', 'kurato' ),
					'value' => (string) ( $sub['list_label'] ?? '' ) . ' (' . (string) $sub['status'] . ( '' !== $when ? ', ' . $when : '' ) . ')',
				);
			}

			foreach ( $this->queue->for_contact( $id ) as $row ) {
				$campaign = (int) ( $row['campaign_id'] ?? 0 );
				$data[]   = array(
					'name'  => __( 'Versand', 'kurato' ),
					/* translators: 1: Kampagnen-ID, 2: Status */
					'value' => sprintf( __( 'Kampagne %1$d: %2$s', 'kurato' ), $campaign, (string) ( $row['status'] ?? '' ) ) . ( '' !== (string) ( $row['sent_at'] ?? '' ) ? ' (' . $row['sent_at'] . ')' : '' ),
				);
			}

			foreach ( $this->events->find_by_contact( $id ) as $event ) {
				$data[] = array(
					'name'  => __( 'Mail-Ereignis', 'kurato' ),
					'value' => (string) $event['type'] . ( '' !== (string) $event['detail'] ? ' (' . $event['detail'] . ')' : '' ) . ' - ' . (string) $event['created_at'],
				);
			}

			$type_labels = array();
			foreach ( $this->bookings->for_contact( $id ) as $booking ) {
				$type_id = (int) $booking['type_id'];
				if ( ! array_key_exists( $type_id, $type_labels ) ) {
					$type                   = $this->booking_types->get( $type_id );
					$type_labels[ $type_id ] = null !== $type ? (string) $type['label'] : __( '(entfernte Terminart)', 'kurato' );
				}
				$data[] = array(
					'name'  => __( 'Termin', 'kurato' ),
					'value' => $type_labels[ $type_id ] . ' - ' . (string) $booking['start_utc'] . ' bis ' . (string) $booking['end_utc'] . ' (' . (string) $booking['status'] . ')',
				);
			}

			$items[] = array(
				'group_id'    => 'hs_contacts',
				'group_label' => __( 'Kontakte (Kurato)', 'kurato' ),
				'item_id'     => 'contact-' . $id,
				'data'        => $data,
			);
		}
		return $items;
	}

	/**
	 * Loescht die Kontakte zu einer E-Mail.
	 *
	 * @param array<string, mixed> $result Bisheriges Loeschergebnis.
	 * @param string               $email  E-Mail.
	 * @return array<string, mixed>
	 */
	public function erase( array $result, string $email ): array {
		// Buchungen haengen nicht an der Kontakt-Kaskade: erst je Kontakt loeschen,
		// dann den Kontakt selbst.
		$removed = false;
		foreach ( $this->contacts->ids_by_email( $email ) as $id ) {
			$this->bookings->delete_by_contact( $id );
			$removed = true;
		}
		if ( $this->contacts->delete_by_email( $email ) > 0 ) {
			$removed = true;
		}
		if ( $removed ) {
			$result['items_removed'] = true;
		}
		return $result;
	}
}
