<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
// Justification: This class manages a plugin-owned custom table (wp_ccgal_favorites) that has no post-meta or options-table representation, so direct queries are by design. Caching is intentionally avoided because favorite toggles and reads occur within the same request lifecycle. The interpolated table name is derived from $wpdb->prefix plus a constant suffix — safe by construction. Per-row values pass through $wpdb->prepare() with %d/%s placeholders.

class CCGAL_Favorites {

	const COOKIE_NAME = 'wpg_visitor';
	const COOKIE_TTL  = YEAR_IN_SECONDS;

	public static function init() {
		add_action( 'before_delete_post', array( __CLASS__, 'on_post_delete' ), 10, 2 );
	}

	/**
	 * Prüft, ob für eine Galerie überhaupt Cookies gesetzt werden dürfen.
	 * Wird vor dem on-demand-Setzen des Besucher-Cookies geprüft.
	 */
	private static function cookies_allowed_for( $gallery_id ) {
		$gallery = CCGAL_CPT::get_gallery_config( $gallery_id );
		return $gallery ? ! empty( $gallery['cookies_enabled'] ) : false;
	}

	public static function on_post_delete( $post_id, $post = null ) {
		if ( ! $post ) {
			$post = get_post( $post_id );
		}
		if ( ! $post || CCGAL_CPT::POST_TYPE !== $post->post_type ) {
			return;
		}
		self::delete_all_for_gallery( $post_id );
		do_action( 'ccgal_gallery_deleted', $post_id );
	}

	public static function delete_all_for_gallery( $gallery_id ) {
		global $wpdb;
		$gallery_id = absint( $gallery_id );
		if ( ! $gallery_id ) {
			return 0;
		}
		$table = $wpdb->prefix . 'ccgal_favorites';
		return (int) $wpdb->delete( $table, array( 'gallery_id' => $gallery_id ), array( '%d' ) );
	}

	public static function delete_all_for_visitor_in_gallery( $gallery_id, $cookie_id ) {
		global $wpdb;
		$gallery_id = absint( $gallery_id );
		$cookie_id  = (string) $cookie_id;
		if ( ! $gallery_id || '' === $cookie_id ) {
			return 0;
		}
		$table = $wpdb->prefix . 'ccgal_favorites';
		return (int) $wpdb->delete( $table, array( 'gallery_id' => $gallery_id, 'cookie_id' => $cookie_id ), array( '%d', '%s' ) );
	}

	public static function ensure_cookie() {
		if ( is_admin() || ( defined( 'DOING_CRON' ) && DOING_CRON ) ) {
			return;
		}
		if ( ! empty( $_COOKIE[ self::COOKIE_NAME ] ) ) {
			return;
		}
		if ( headers_sent() ) {
			return;
		}

		$id = self::generate_cookie_id();
		setcookie(
			self::COOKIE_NAME,
			$id,
			array(
				'expires'  => time() + self::COOKIE_TTL,
				'path'     => COOKIEPATH ?: '/',
				'domain'   => COOKIE_DOMAIN ?: '',
				'secure'   => is_ssl(),
				'httponly' => true,
				'samesite' => 'Lax',
			)
		);
		$_COOKIE[ self::COOKIE_NAME ] = $id;
	}

	private static function generate_cookie_id() {
		if ( function_exists( 'random_bytes' ) ) {
			return bin2hex( random_bytes( 16 ) );
		}
		return wp_generate_password( 32, false, false );
	}

	public static function get_cookie_id() {
		if ( ! empty( $_COOKIE[ self::COOKIE_NAME ] ) ) {
			// Visitor cookie IDs are always hex (bin2hex of random_bytes, or wp_generate_password without special chars).
			// preg_replace with a hex-only whitelist below IS the sanitizer here; sanitize_text_field would not enforce the format.
			// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- preg_replace whitelist on the same line is the sanitizer, see the two comment lines above.
			$raw = preg_replace( '/[^a-f0-9]/', '', strtolower( (string) wp_unslash( $_COOKIE[ self::COOKIE_NAME ] ) ) );
			if ( strlen( $raw ) >= 16 ) {
				return substr( $raw, 0, 64 );
			}
		}
		$id = self::generate_cookie_id();
		if ( ! headers_sent() ) {
			setcookie(
				self::COOKIE_NAME,
				$id,
				array(
					'expires'  => time() + self::COOKIE_TTL,
					'path'     => COOKIEPATH ?: '/',
					'domain'   => COOKIE_DOMAIN ?: '',
					'secure'   => is_ssl(),
					'httponly' => true,
					'samesite' => 'Lax',
				)
			);
		}
		$_COOKIE[ self::COOKIE_NAME ] = $id;
		return $id;
	}

	public static function toggle_favorite( $gallery_id, $file_id ) {
		global $wpdb;
		$table      = $wpdb->prefix . 'ccgal_favorites';
		$gallery_id = absint( $gallery_id );
		$file_id    = CCGAL_Storage_Adapter::sanitize( $file_id );

		if ( ! $gallery_id || '' === $file_id ) {
			return new WP_Error( 'ccgal_bad_input', __( 'Missing parameters.', 'customer-cloud-gallery' ) );
		}

		// Favoriten brauchen den Besucher-Cookie zur Zuordnung. Wenn Cookies für die
		// Galerie deaktiviert sind, kann es keine serverseitige Favoriten-Zuordnung geben.
		if ( ! self::cookies_allowed_for( $gallery_id ) ) {
			return new WP_Error( 'ccgal_cookies_disabled', __( 'Favorites are disabled in this gallery.', 'customer-cloud-gallery' ), array( 'status' => 403 ) );
		}

		$cookie_id = self::get_cookie_id();

		$existing = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT id FROM {$table} WHERE gallery_id = %d AND file_id = %s AND cookie_id = %s",
				$gallery_id,
				$file_id,
				$cookie_id
			)
		);

		if ( $existing ) {
			$wpdb->delete( $table, array( 'id' => (int) $existing ), array( '%d' ) );
			return array( 'favorited' => false );
		}

		$wpdb->insert(
			$table,
			array(
				'gallery_id' => $gallery_id,
				'file_id'    => $file_id,
				'cookie_id'  => $cookie_id,
				'created_at' => current_time( 'mysql' ),
			),
			array( '%d', '%s', '%s', '%s' )
		);

		return array( 'favorited' => true );
	}

	public static function get_favorites_for_visitor( $gallery_id ) {
		global $wpdb;
		$table      = $wpdb->prefix . 'ccgal_favorites';
		$gallery_id = absint( $gallery_id );

		// Wenn keine Cookies erlaubt sind, gibt es keine Visitor-Zuordnung — daher leer.
		if ( ! self::cookies_allowed_for( $gallery_id ) ) {
			return array();
		}

		$cookie_id = self::get_cookie_id();

		$rows = $wpdb->get_col(
			$wpdb->prepare(
				"SELECT file_id FROM {$table} WHERE gallery_id = %d AND cookie_id = %s",
				$gallery_id,
				$cookie_id
			)
		);

		return is_array( $rows ) ? $rows : array();
	}
}
