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

namespace Hafenstudios\Kurato\Db;

defined( 'ABSPATH' ) || exit;

/**
 * Ansprechpartner/Experten des Buchungsmoduls (hs_hosts, K5a).
 *
 * Ein Host traegt eine oeffentliche URL `/termin/{slug}` und buendelt seine
 * Terminarten und Verfuegbarkeit. Free-Umfang ist ein Host; das Limit liegt in
 * der Admin-UI (K5b) und ist per Filter anhebbar, nicht hier im Repository.
 *
 * Nicht final, damit Tests die DB-Methoden per Test-Double ueberschreiben koennen.
 */
class HostRepository {

	/**
	 * Alle Hosts, alphabetisch nach Label.
	 *
	 * @return array<int, array<string, mixed>>
	 */
	public function all(): array {
		global $wpdb;
		$table = Schema::table( 'hosts' );
		// 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", ARRAY_A );
		return array_map( array( $this, 'hydrate' ), $rows ?: array() );
	}

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

	/**
	 * Host per Slug (fuer die oeffentliche Buchungsseite `/termin/{slug}`).
	 *
	 * @param string $slug Host-Slug.
	 * @return array<string, mixed>|null
	 */
	public function get_by_slug( string $slug ): ?array {
		$slug = sanitize_title( $slug );
		if ( '' === $slug ) {
			return null;
		}
		global $wpdb;
		$table = Schema::table( 'hosts' );
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		$row = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$table} WHERE slug = %s", $slug ), ARRAY_A );
		return $row ? $this->hydrate( $row ) : null;
	}

	/**
	 * Legt einen Host an.
	 *
	 * @param array<string, mixed> $data label, role, photo_id, user_id, active.
	 * @return int Host-ID (0 bei Fehler).
	 */
	public function create( array $data ): int {
		$label = trim( sanitize_text_field( (string) ( $data['label'] ?? '' ) ) );
		if ( '' === $label ) {
			return 0;
		}
		$slug = $this->unique_slug( sanitize_title( '' !== (string) ( $data['slug'] ?? '' ) ? (string) $data['slug'] : $label ) );

		global $wpdb;
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$ok = $wpdb->insert(
			Schema::table( 'hosts' ),
			array(
				'label'      => substr( $label, 0, 190 ),
				'slug'       => $slug,
				'role'       => substr( sanitize_text_field( (string) ( $data['role'] ?? '' ) ), 0, 190 ),
				'photo_id'   => ! empty( $data['photo_id'] ) ? absint( $data['photo_id'] ) : null,
				'user_id'    => ! empty( $data['user_id'] ) ? absint( $data['user_id'] ) : null,
				'active'     => isset( $data['active'] ) && ! $data['active'] ? 0 : 1,
				'created_at' => current_time( 'mysql', true ),
			)
		);
		return $ok ? (int) $wpdb->insert_id : 0;
	}

	/**
	 * Aktualisiert einen Host (nur uebergebene Felder).
	 *
	 * @param int                  $id   Host-ID.
	 * @param array<string, mixed> $data Zu aendernde Felder.
	 * @return bool
	 */
	public function update( int $id, array $data ): bool {
		$row = array();
		if ( array_key_exists( 'label', $data ) ) {
			$row['label'] = substr( sanitize_text_field( (string) $data['label'] ), 0, 190 );
		}
		if ( array_key_exists( 'slug', $data ) ) {
			// Der Aufrufer (K5b-Admin) stellt die Eindeutigkeit ueber
			// BookingInput::unique_slug sicher; hier wird nur normalisiert.
			$slug = sanitize_title( (string) $data['slug'] );
			if ( '' !== $slug ) {
				$row['slug'] = $slug;
			}
		}
		if ( array_key_exists( 'role', $data ) ) {
			$row['role'] = substr( sanitize_text_field( (string) $data['role'] ), 0, 190 );
		}
		if ( array_key_exists( 'photo_id', $data ) ) {
			$row['photo_id'] = ! empty( $data['photo_id'] ) ? absint( $data['photo_id'] ) : null;
		}
		if ( array_key_exists( 'user_id', $data ) ) {
			$row['user_id'] = ! empty( $data['user_id'] ) ? absint( $data['user_id'] ) : null;
		}
		if ( array_key_exists( 'active', $data ) ) {
			$row['active'] = ! empty( $data['active'] ) ? 1 : 0;
		}
		if ( array() === $row ) {
			return false;
		}
		global $wpdb;
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return false !== $wpdb->update( Schema::table( 'hosts' ), $row, array( 'id' => $id ) );
	}

	/**
	 * Loescht einen Host samt Terminarten, Verfuegbarkeit und Buchungen.
	 *
	 * @param int $id Host-ID.
	 * @return bool
	 */
	public function delete( int $id ): bool {
		global $wpdb;
		// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$wpdb->delete( Schema::table( 'booking_types' ), array( 'host_id' => $id ) );
		$wpdb->delete( Schema::table( 'availability' ), array( 'host_id' => $id ) );
		$wpdb->delete( Schema::table( 'bookings' ), array( 'host_id' => $id ) );
		$deleted = $wpdb->delete( Schema::table( 'hosts' ), array( 'id' => $id ) );
		// phpcs:enable
		return (bool) $deleted;
	}

	/**
	 * Anzahl der Hosts (fuer das Free-Limit in der Admin-UI, K5b).
	 *
	 * @return int
	 */
	public function count_all(): int {
		global $wpdb;
		$table = Schema::table( 'hosts' );
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		return (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$table}" );
	}

	/**
	 * Erzeugt einen eindeutigen Slug (haengt bei Kollision -2, -3 an).
	 *
	 * @param string $slug Basis-Slug.
	 * @return string
	 */
	private function unique_slug( string $slug ): string {
		global $wpdb;
		$table = Schema::table( 'hosts' );
		$base  = '' !== $slug ? $slug : 'host';
		$try   = $base;
		$n     = 1;
		// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		while ( (int) $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$table} WHERE slug = %s", $try ) ) > 0 ) {
			$n++;
			$try = $base . '-' . $n;
		}
		// phpcs:enable
		return $try;
	}

	/**
	 * Normalisiert eine Zeile.
	 *
	 * @param array<string, mixed> $row Rohzeile.
	 * @return array<string, mixed>
	 */
	private function hydrate( array $row ): array {
		return array(
			'id'         => (int) $row['id'],
			'label'      => (string) $row['label'],
			'slug'       => (string) $row['slug'],
			'role'       => (string) ( $row['role'] ?? '' ),
			'photo_id'   => null !== $row['photo_id'] ? (int) $row['photo_id'] : null,
			'user_id'    => null !== $row['user_id'] ? (int) $row['user_id'] : null,
			'active'     => (bool) $row['active'],
			'created_at' => (string) ( $row['created_at'] ?? '' ),
		);
	}
}
