<?php
/**
 * File containing \Crowdsignal_Forms\Gateways\Post_Survey_Meta_Gateway
 *
 * @package crowdsignal-forms/Gateways
 * @since 1.8.0
 */

namespace Crowdsignal_Forms\Gateways;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class Post_Survey_Meta_Gateway
 *
 * Handles storage and retrieval of survey data (NPS and Feedback) in post meta.
 * Surveys are identified by a client-generated UUID (surveyClientId) that is
 * stored in the block markup. The platform survey ID is stored in post meta
 * and never exposed in the frontend.
 *
 * @package Crowdsignal_Forms\Gateways
 */
class Post_Survey_Meta_Gateway {
	/**
	 * Prefix for meta entries.
	 */
	const META_PREFIX = '_cs_survey_';

	/**
	 * Get a survey's data for a given client ID.
	 *
	 * @param int    $post_id   The post where the survey block is embedded.
	 * @param string $client_id The UUID assigned to the survey block.
	 * @return array The survey data or empty array if not found.
	 */
	public function get_survey_data_for_client_id( $post_id, $client_id ) {
		global $wpdb;

		if ( null === $client_id ) {
			return array();
		}

		$survey_meta_key = $this->get_survey_meta_key( $client_id );

		if ( null === $post_id ) {
			// Search across all posts for this client_id.
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
			$row = $wpdb->get_row(
				$wpdb->prepare(
					"SELECT meta_value FROM {$wpdb->postmeta} WHERE meta_key = %s LIMIT 1",
					$survey_meta_key
				)
			);

			if ( $row && ! empty( $row->meta_value ) ) {
				$data = maybe_unserialize( $row->meta_value );
				return is_array( $data ) ? $data : array();
			}

			return array();
		}

		$meta_value = get_post_meta( $post_id, $survey_meta_key, true );

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

	/**
	 * Update a survey's data for a given client ID.
	 *
	 * @param int    $post_id   The post ID.
	 * @param string $client_id The client ID (UUID).
	 * @param array  $data      Data to store.
	 *
	 * @return bool|int
	 */
	public function update_survey_data_for_client_id( $post_id, $client_id, $data ) {
		return update_post_meta( $post_id, $this->get_survey_meta_key( $client_id ), $data );
	}

	/**
	 * Get the original post ID where a survey client ID was first created.
	 *
	 * This is used for copy/paste protection - if a survey block is copied
	 * to a different post, the new post should not be able to sync changes
	 * to the original survey.
	 *
	 * @param string $client_id The client ID (UUID).
	 * @return int|null The post ID, or null if not found.
	 */
	public function get_original_post_id_for_client_id( $client_id ) {
		global $wpdb;

		if ( null === $client_id ) {
			return null;
		}

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
		$post_id = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = %s LIMIT 1",
				$this->get_survey_meta_key( $client_id )
			)
		);

		return $post_id ? (int) $post_id : null;
	}

	/**
	 * Find the client_id (UUID) whose stored meta data maps to a given survey_id.
	 *
	 * Scans all `_cs_survey_*` meta on the given post to find one whose
	 * stored array contains `'id' => $survey_id`.
	 *
	 * @param int $post_id   The post ID.
	 * @param int $survey_id The platform survey ID.
	 * @return string|null The client_id (UUID) or null if not found.
	 */
	public function get_client_id_for_survey_id( $post_id, $survey_id ) {
		$meta = get_post_meta( $post_id );

		if ( ! is_array( $meta ) ) {
			return null;
		}

		$matches = array();

		foreach ( $meta as $key => $values ) {
			if ( 0 !== strpos( $key, self::META_PREFIX ) ) {
				continue;
			}

			$client_id = substr( $key, strlen( self::META_PREFIX ) );
			$data      = maybe_unserialize( $values[0] );

			if ( is_array( $data ) && isset( $data['id'] ) && (int) $data['id'] === (int) $survey_id ) {
				$matches[] = $client_id;
			}
		}

		if ( empty( $matches ) ) {
			return null;
		}

		sort( $matches );
		return $matches[0];
	}

	/**
	 * Ensure a `_cs_survey_{uuid}` meta entry exists for a given survey_id on a post.
	 *
	 * If a mapping already exists, returns the existing client_id.
	 * Otherwise generates a new UUID, stores the mapping, and returns it.
	 *
	 * @param int $post_id   The post ID.
	 * @param int $survey_id The platform survey ID.
	 * @return string The client_id (UUID).
	 */
	public function ensure_meta_for_survey_id( $post_id, $survey_id ) {
		$existing = $this->get_client_id_for_survey_id( $post_id, $survey_id );

		if ( null !== $existing ) {
			return $existing;
		}

		$client_id = wp_generate_uuid4();
		$this->update_survey_data_for_client_id(
			$post_id,
			$client_id,
			array( 'id' => (int) $survey_id )
		);

		return $client_id;
	}

	/**
	 * Find which post owns a survey_id by querying `_crowdsignal_forms_survey_ids` meta.
	 *
	 * The meta stores a serialized array of integer IDs. We search for
	 * the serialized integer format `i:{survey_id};` using a LIKE query.
	 *
	 * @param int $survey_id The platform survey ID.
	 * @return int|null The post ID, or null if not found.
	 */
	public function get_original_post_id_for_survey_id( $survey_id ) {
		global $wpdb;

		$survey_id = (int) $survey_id;

		$like = '%' . $wpdb->esc_like( 'i:' . $survey_id . ';' ) . '%';

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
		$post_id = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = '_crowdsignal_forms_survey_ids' AND meta_value LIKE %s LIMIT 1",
				$like
			)
		);

		return $post_id ? (int) $post_id : null;
	}

	/**
	 * Return the meta key for a survey block client ID.
	 *
	 * @param string $client_id The client ID (UUID).
	 * @return string
	 */
	private function get_survey_meta_key( $client_id ) {
		return self::META_PREFIX . $client_id;
	}
}