<?php

namespace Limb_Chatbot\Includes\Database_Strategies;

use Limb_Chatbot\Includes\Database_Strategy_Interface;
use Limb_Chatbot\Includes\Services\Collection;
use Limb_Chatbot\Includes\Traits\SingletonTrait;

/**
 * Class WP_Meta_Query
 *
 * Implements a database strategy for querying and manipulating WordPress post meta data.
 * Provides methods for creating, finding, updating, deleting, and filtering post meta entries.
 *
 * Uses WordPress native functions such as get_post_meta, update_post_meta, and delete_post_meta.
 *
 * @since 1.0.0
 */
class WP_Meta_Query extends Database_Strategy implements Database_Strategy_Interface {

	use SingletonTrait;

	/**
	 * WordPress postmeta table name.
	 * @since 1.0.0
	 */
	const TABLE_NAME = 'postmeta';

	/**
	 * List of fillable columns in the postmeta table.
	 * @since 1.0.0
	 */
	const FILLABLE = [ 'id', 'post_id', 'meta_key', 'meta_value', ];

	/**
	 * Create or update a post meta entry.
	 *
	 * @param array $data Associative array containing 'post_id', 'meta_key', and 'meta_value'.
	 * @return bool True on success, false on failure.
	 * @since 1.0.0
	 */
	public function create( $data ) {
		return $this->update( [ 'post_id' => $data['post_id'], 'meta_key' => $data['meta_key'] ], $data );
	}

	/**
	 * Find a meta entry by its ID.
	 *
	 * @param int $id Meta ID.
	 * @return void Meta entry data or null if not found.
	 * @since 1.0.0
	 */
	public function find( $id ) {
	}

	/**
	 * Retrieve post meta entries filtered by conditions.
	 *
	 * Supports filtering by post ID, meta key (exact or LIKE), and meta value.
	 *
	 * @param array $conditions Filtering conditions. Expected keys:
	 *                          - 'post_id' (int): Required post ID.
	 *                          - 'meta_key' (string|array): Meta key(s) for exact match.
	 *                          - 'meta_keyLIKE' (string): Meta key LIKE pattern.
	 *                          - 'meta_value' (mixed): Meta value to match.
	 * @param string|null $table Optional table name override.
	 * @return array List of matching meta entries, each as associative array with keys:
	 *               'post_id', 'meta_key', and 'meta_value'.
	 * @since 1.0.0
	 */
	public function where( $conditions = array(), $table = null ) {
		// Default arguments
		$defaults = array(
			'meta_key'     => '',
			'meta_keyLIKE' => '',
			'meta_value'   => '',
			'post_id'      => 0
		);
		// Merge provided arguments with defaults
		$args = wp_parse_args( $conditions, $defaults );
		// post_id existence is required
		if ( empty( $args['post_id'] ) ) {
			return [];
		}
		// Extract arguments
		$meta_key_like = $args['meta_keyLIKE'];
		$meta_key      = $args['meta_key'];
		$meta_value    = $args['meta_value'];
		$post_id       = $args['post_id'];
		if ( ! empty( $meta_key_like ) ) {
			$results = ( new WPDB() )->where( $conditions, 'postmeta' );
			foreach ( $results as $result ) {
				$filtered_results[] = [ 'meta_key' => $result['meta_key'], 'meta_value' => maybe_unserialize( $result['meta_value'] ), 'post_id' => $result['post_id'] ];
			}

			return $filtered_results ?? [];
		} elseif ( ! empty( $meta_key ) && ! empty( $meta_value ) ) {
			// Check if both meta_key and meta_value are provided
			$meta_keys = is_array( $meta_key ) ? $meta_key : [ $meta_key ];
			foreach ( $meta_keys as $meta_key ) {
				// Get metadata with specific key and value
				foreach ( get_post_meta( $post_id, $meta_key ) as $item ) {
					if ( $item == $meta_value ) {
						$results[] = [ 'meta_value' => $meta_value, 'meta_key' => $meta_key, 'post_id' => $post_id ];
					}
				}
			}
		} elseif ( ! empty( $meta_key ) ) {
			$meta_keys = is_array( $meta_key ) ? $meta_key : [ $meta_key ];
			foreach ( $meta_keys as $meta_key ) {
				// Get meta metadata with specific key
				$meta_values = get_post_meta( $post_id, $meta_key );
				foreach ( $meta_values as $meta_value ) {
					$results[] = [ 'meta_value' => $meta_value, 'meta_key' => $meta_key, 'post_id' => $post_id ];
				}
			}
		} else {
			// Get all metadata data for the post
			$filtered_metas = get_post_meta( $post_id );
			foreach ( $filtered_metas as $key => $filtered_meta ) {
				$results[] = [ 'meta_value' => $filtered_meta[0], 'meta_key' => $key, 'post_id' => $post_id ];
			}
		}

		return $results ?? [];
	}

	/**
	 * Count meta entries matching given conditions.
	 *
	 * @param array $conditions Conditions to filter meta entries.
	 * @return int Number of matching meta entries.
	 * @since 1.0.0
	 */
	public function count( $conditions ) {
		$metas = $this->where( $conditions );

		return is_array( $metas ) ? count( $metas ) : 0;
	}

	/**
	 * Delete a post meta entry.
	 *
	 * @param array $where Associative array with keys:
	 *                     - 'post_id' (int): Post ID.
	 *                     - 'meta_key' (string): Meta key.
	 *                     - 'meta_value' (mixed, optional): Specific meta value to delete.
	 * @return bool True on success, false on failure.
	 * @since 1.0.0
	 */
	public function delete( $where ) {
		return delete_post_meta( $where['post_id'], $where['meta_key'], $where['meta_value'] ?? null );
	}

	/**
	 * Update a post meta entry.
	 *
	 * Serializes arrays, objects, and Collection instances as JSON before updating.
	 *
	 * @param array $where Associative array with keys:
	 *                     - 'post_id' (int): Post ID.
	 *                     - 'meta_key' (string): Meta key.
	 * @param array $data Associative array containing 'meta_value' to update.
	 * @return bool True on success, false on failure.
	 * @since 1.0.0
	 */
	public function update( $where, $data ) {
		if ( ! empty( $data['meta_value'] ) && ( is_array( $data['meta_value'] ) || is_object( $data['meta_value'] ) || $data['meta_value'] instanceof Collection ) ) {
			$data['meta_value'] = json_encode( wp_unslash( $data['meta_value'] ), JSON_PRESERVE_ZERO_FRACTION | JSON_UNESCAPED_UNICODE );
		}
		$meta_value = $data['meta_value'] ?? null;
		if ( is_string( $meta_value ) ) {
			$meta_value = WPDB::instance()->sanitize_string_for_table( $meta_value, self::TABLE_NAME );
		}

		return update_post_meta( $where['post_id'], $where['meta_key'], $meta_value );
	}
}