<?php
/**
 * AbstractModelWithMeta.php.
 *
 * @package RewardsWP
 */

namespace Awesomemotive\Rewardswp\Abstracts;

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

use InvalidArgumentException;

/**
 * Provides helpers to manage meta for models with a meta table.
 *
 * @since 1.0.0
 */
abstract class AbstractModelWithMeta extends AbstractModel {

	/**
	 * Get meta value by meta key for the current object.
	 *
	 * @since 1.0.0
	 *
	 * @param string $meta_key  The key of the meta value to retrieve.
	 * @param bool   $single    Whether to return a single value (true) or an array of values (false).
	 *
	 * @return mixed The meta value, or false if not found.
	 */
	public function get_meta( string $meta_key, bool $single = true ) {
		if ( empty( $this->id ) ) {
			throw new InvalidArgumentException( esc_html__( 'Object ID is required.', 'rewardswp' ) );
		}

		return $this->db_handler->get_meta(
			$this->db_table->get_prefixed_meta_table_name(),
			$this->db_table->get_meta_table_relationship_field_name(),
			$this->id,
			$meta_key,
			$single
		);
	}

	/**
	 * Update or create meta value for the current object.
	 *
	 * @since 1.0.0
	 *
	 * @param string $meta_key   The key of the meta value to update or create.
	 * @param mixed  $meta_value The value to update or create.
	 * @param mixed  $prev_value Optional. The previous value to match before updating. If null, all values will be updated.
	 *
	 * @return bool True on success, false on failure.
	 */
	public function update_meta( string $meta_key, $meta_value, $prev_value = null ): bool {
		if ( empty( $this->id ) ) {
			throw new InvalidArgumentException( esc_html__( 'Object ID is required.', 'rewardswp' ) );
		}

		return $this->db_handler->update_meta(
			$this->db_table->get_prefixed_meta_table_name(),
			$this->db_table->get_meta_table_relationship_field_name(),
			$this->id,
			$meta_key,
			$meta_value,
			$prev_value
		);
	}

	/**
	 * Delete meta value for the current object.
	 *
	 * @since 1.0.0
	 *
	 * @param string     $meta_key  The key of the meta value to delete.
	 * @param mixed|null $meta_value Optional. If provided, only delete entries with this meta value.
	 *
	 * @return bool True on success, false on failure.
	 */
	public function delete_meta( string $meta_key, $meta_value = null ): bool {
		if ( empty( $this->id ) ) {
			throw new InvalidArgumentException( esc_html__( 'Object ID is required.', 'rewardswp' ) );
		}

		return $this->db_handler->delete_meta(
			$this->db_table->get_prefixed_meta_table_name(),
			$this->db_table->get_meta_table_relationship_field_name(),
			$this->id,
			$meta_key,
			$meta_value
		);
	}

	/**
	 * Deletes the object and its associated metadata from the database.
	 *
	 * This method first deletes all metadata associated with the object's ID,
	 * then calls the parent's delete method to remove the main object record.
	 *
	 * @since 1.0.0 // Or your specific version
	 *
	 * @return bool True on success, false on failure.
	 * @throws \InvalidArgumentException If the object ID is not set.
	 * @throws \Exception If metadata deletion or main object deletion fails.
	 */
	public function delete(): bool {
		if ( empty( $this->id ) ) {
			// Using \InvalidArgumentException to match usage in AbstractModel.php.
			throw new \InvalidArgumentException( esc_html__( 'Object ID is required for deletion.', 'rewardswp' ) );
		}

		// Call before_delete() hook if it exists in the child class.
		// This must be called before deleting metadata so we can still access object properties.
		if ( method_exists( $this, 'before_delete' ) ) {
			$this->before_delete();
		}

		$prefixed_meta_table = $this->db_table->get_prefixed_meta_table_name();
		$relationship_column = $this->db_table->get_meta_table_relationship_field_name();

		// Delete all metadata associated with this object's ID.
		$meta_deleted = $this->db_handler->delete(
			$prefixed_meta_table,
			[ $relationship_column => $this->id ]
		);

		if ( false === $meta_deleted ) {
			// Using \Exception to match usage in AbstractModel.php.
			throw new \Exception(
				sprintf(
					/* translators: %s: Meta table name. */
					esc_html__( 'Failed to delete metadata from %s.', 'rewardswp' ),
					esc_html( $prefixed_meta_table )
				)
			);
		}

		// Call parent's delete method to delete the main record.
		return parent::delete();
	}
}
