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

namespace Hafenstudios\Kurato\Db;

use Hafenstudios\Kurato\Campaign\SegmentDefinition;

defined( 'ABSPATH' ) || exit;

/**
 * Gespeicherte Filter (hs_segments, K4-Konzept Abschnitt 4).
 *
 * Ein Segment ist ein Label plus ein Filter-JSON (definition). Die Auswertung zu
 * Kontakt-IDs uebernimmt der SegmentQuery-Service; dieses Repository haelt nur die
 * Persistenz. Das definition-JSON wird beim Schreiben durch SegmentDefinition
 * sanitisiert (nur die drei v1-Felder Tag/Stufe/Liste bleiben erhalten).
 *
 * Nicht final, damit Tests die DB-Methoden per Test-Double ueberschreiben koennen.
 */
class SegmentRepository {

	/**
	 * Alle Segmente, alphabetisch nach Label.
	 *
	 * @return array<int, array<string, mixed>>
	 */
	public function all(): array {
		global $wpdb;
		$table = Schema::table( 'segments' );
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		$rows = $wpdb->get_results( "SELECT * FROM {$table} ORDER BY label ASC, id ASC", ARRAY_A );
		return array_map( array( $this, 'hydrate' ), $rows ?: array() );
	}

	/**
	 * Ein Segment.
	 *
	 * @param int $id Segment-ID.
	 * @return array<string, mixed>|null
	 */
	public function get( int $id ): ?array {
		global $wpdb;
		$table = Schema::table( 'segments' );
		// 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 ? $this->hydrate( $row ) : null;
	}

	/**
	 * Legt ein Segment an.
	 *
	 * @param string $label      Anzeigename.
	 * @param mixed  $definition Rohe Filter-Definition (wird sanitisiert).
	 * @return int Segment-ID (0 bei Fehler oder leerem Label).
	 */
	public function create( string $label, $definition ): int {
		$label = trim( sanitize_text_field( $label ) );
		if ( '' === $label ) {
			return 0;
		}

		global $wpdb;
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$ok = $wpdb->insert(
			Schema::table( 'segments' ),
			array(
				'label'      => substr( $label, 0, 190 ),
				'definition' => SegmentDefinition::encode( $definition ),
				'created_at' => current_time( 'mysql', true ),
			)
		);
		return $ok ? (int) $wpdb->insert_id : 0;
	}

	/**
	 * Aktualisiert Label und/oder Definition eines Segments.
	 *
	 * @param int                  $id   Segment-ID.
	 * @param array<string, mixed> $data Zu aendernde Felder (label, definition).
	 * @return bool
	 */
	public function update( int $id, array $data ): bool {
		$row = array();
		if ( array_key_exists( 'label', $data ) ) {
			$label = trim( sanitize_text_field( (string) $data['label'] ) );
			if ( '' !== $label ) {
				$row['label'] = substr( $label, 0, 190 );
			}
		}
		if ( array_key_exists( 'definition', $data ) ) {
			$row['definition'] = SegmentDefinition::encode( $data['definition'] );
		}
		if ( array() === $row ) {
			return false;
		}
		$row['updated_at'] = current_time( 'mysql', true );

		global $wpdb;
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return false !== $wpdb->update( Schema::table( 'segments' ), $row, array( 'id' => $id ) );
	}

	/**
	 * Loescht ein Segment.
	 *
	 * @param int $id Segment-ID.
	 * @return bool
	 */
	public function delete( int $id ): bool {
		global $wpdb;
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return (bool) $wpdb->delete( Schema::table( 'segments' ), array( 'id' => $id ) );
	}

	/**
	 * Normalisiert eine Zeile. Das Definition-JSON wird zur sanitisierten
	 * Struktur dekodiert (definition = array), das Roh-JSON bleibt als
	 * definition_json erhalten.
	 *
	 * @param array<string, mixed> $row Rohzeile.
	 * @return array<string, mixed>
	 */
	private function hydrate( array $row ): array {
		$json = (string) ( $row['definition'] ?? '' );
		return array(
			'id'         => (int) $row['id'],
			'label'      => (string) $row['label'],
			'definition' => SegmentDefinition::decode( $json ),
			'created_at' => (string) ( $row['created_at'] ?? '' ),
			'updated_at' => null !== ( $row['updated_at'] ?? null ) ? (string) $row['updated_at'] : '',
		);
	}
}
