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

namespace Hafenstudios\Core\Consent;

use Hafenstudios\Core\Db\Migrator;

defined( 'ABSPATH' ) || exit;

/**
 * Gerichtsfestes Consent-Log der Flotte (`hs_consents`).
 *
 * Haelt fuer jede Einwilligung: Quelle, verwendeten Einwilligungstext samt
 * Version, Zeitpunkt, anonymisierte IP und User-Agent als Nachweis. Leadlotse und
 * (spaeter) Kurato schreiben hier hinein und speichern nur die Referenz-ID.
 */
final class ConsentLog {

	public const DB_VERSION = '1.0.0';

	/**
	 * Vollstaendiger Tabellenname.
	 *
	 * @return string
	 */
	public static function table(): string {
		global $wpdb;
		return $wpdb->prefix . 'hs_consents';
	}

	/**
	 * Registriert die Migration und stellt die Tabelle sicher.
	 *
	 * @return void
	 */
	public function boot(): void {
		// Tabelle wird bei Plugin-Aktivierung ueber install() erstellt; hier nichts.
	}

	/**
	 * Legt die Tabelle an (Aufruf bei Plugin-Aktivierung).
	 *
	 * @return void
	 */
	public static function install(): void {
		$migrator = new Migrator( 'core', self::DB_VERSION );
		$table    = self::table();

		$migrator->register(
			'1.0.0',
			static function ( $wpdb, $charset_collate ) use ( $table ) {
				$sql = "CREATE TABLE {$table} (
					id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
					source VARCHAR(60) NOT NULL DEFAULT '',
					subject_email VARCHAR(190) NOT NULL DEFAULT '',
					purpose VARCHAR(60) NOT NULL DEFAULT '',
					consent_text LONGTEXT NULL,
					text_version VARCHAR(40) NOT NULL DEFAULT '',
					reference VARCHAR(190) NOT NULL DEFAULT '',
					ip_hash VARCHAR(64) NOT NULL DEFAULT '',
					user_agent VARCHAR(255) NOT NULL DEFAULT '',
					created_at DATETIME NOT NULL,
					PRIMARY KEY (id),
					KEY subject_email (subject_email),
					KEY source (source),
					KEY created_at (created_at)
				) {$charset_collate};";
				dbDelta( $sql );
			}
		);

		$migrator->run();
	}

	/**
	 * Protokolliert eine Einwilligung und liefert die Datensatz-ID.
	 *
	 * @param array{
	 *   source?:string, subject_email?:string, purpose?:string,
	 *   consent_text?:string, text_version?:string, reference?:string
	 * } $data Einwilligungsdaten.
	 * @return int Consent-ID (0 bei Fehler).
	 */
	public function record( array $data ): int {
		global $wpdb;

		$row = array(
			'source'        => substr( (string) ( $data['source'] ?? '' ), 0, 60 ),
			'subject_email' => sanitize_email( (string) ( $data['subject_email'] ?? '' ) ),
			'purpose'       => substr( (string) ( $data['purpose'] ?? '' ), 0, 60 ),
			'consent_text'  => (string) ( $data['consent_text'] ?? '' ),
			'text_version'  => substr( (string) ( $data['text_version'] ?? '' ), 0, 40 ),
			'reference'     => substr( (string) ( $data['reference'] ?? '' ), 0, 190 ),
			'ip_hash'       => $this->hash_ip(),
			'user_agent'    => substr( (string) ( $_SERVER['HTTP_USER_AGENT'] ?? '' ), 0, 255 ), // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
			'created_at'    => current_time( 'mysql', true ),
		);

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$ok = $wpdb->insert( self::table(), $row );

		return $ok ? (int) $wpdb->insert_id : 0;
	}

	/**
	 * Liefert einen Consent-Datensatz.
	 *
	 * @param int $id Consent-ID.
	 * @return array<string, mixed>|null
	 */
	public function get( int $id ): ?array {
		global $wpdb;
		$table = self::table();
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		$row = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$table} WHERE id = %d", $id ), ARRAY_A );
		return $row ?: null;
	}

	/**
	 * Findet Consent-Datensaetze zu einer E-Mail (fuer Privacy-Export/Loeschung).
	 *
	 * @param string $email E-Mail-Adresse.
	 * @return array<int, array<string, mixed>>
	 */
	public function find_by_email( string $email ): array {
		global $wpdb;
		$table = self::table();
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		$rows = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table} WHERE subject_email = %s ORDER BY created_at DESC", sanitize_email( $email ) ), ARRAY_A );
		return $rows ?: array();
	}

	/**
	 * Loescht alle Consent-Datensaetze zu einer E-Mail (Recht auf Vergessen).
	 *
	 * @param string $email E-Mail-Adresse.
	 * @return int Anzahl geloeschter Zeilen.
	 */
	public function delete_by_email( string $email ): int {
		global $wpdb;
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return (int) $wpdb->delete( self::table(), array( 'subject_email' => sanitize_email( $email ) ) );
	}

	/**
	 * Anonymisierter IP-Nachweis: gehashte, um das letzte Oktett gekuerzte IP.
	 * IP-Anonymisierung ist Default (BAUPLAN Abschnitt 29).
	 *
	 * @return string
	 */
	private function hash_ip(): string {
		$ip = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '';
		if ( '' === $ip ) {
			return '';
		}
		// Letztes Oktett (IPv4) bzw. Suffix (IPv6) verwerfen, dann salted hash.
		if ( false !== strpos( $ip, '.' ) ) {
			$parts = explode( '.', $ip );
			if ( count( $parts ) === 4 ) {
				$parts[3] = '0';
				$ip       = implode( '.', $parts );
			}
		} elseif ( false !== strpos( $ip, ':' ) ) {
			$parts = explode( ':', $ip );
			$ip    = implode( ':', array_slice( $parts, 0, 4 ) ) . '::';
		}
		return hash_hmac( 'sha256', $ip, (string) wp_salt( 'nonce' ) );
	}
}
