<?php
/**
 * Scoring Rule model
 *
 * @link       https://personalizewp.com/
 * @since      3.2.0 Merged into PWP
 * @since      1.0.0
 *
 * @package    PersonalizeWP
 * @subpackage PersonalizeWP/Model
 */

namespace PersonalizeWP\Model;

defined( 'ABSPATH' ) || exit; // Exit if accessed directly.

/**
 * Scoring Rule model
 *
 * This class defines Lead Score structure.
 */
class Scoring_Rule extends Model {

	/**
	 * Database table name storing lead score rules
	 *
	 * @since 1.1.0
	 *
	 * @var string
	 */
	protected static string $table = 'pwp_scoring_rules';

	/**
	 * Matches to database columns, not including ID
	 *
	 * @since 1.1.0
	 *
	 * @var string[]
	 */
	protected static $attributes = [
		'name',
		'type',
		'conditions', // JSON encoded string of various data structures depending on Lead Score type.
		'operator', // String indicator of how multiple conditions are evaluated.
		'lead_score',
		'created',
	];

	/**
	 * Default operator, used when multiple conditions are evaluated.
	 *
	 * @since 2.3.0
	 *
	 * @var string
	 */
	public static $default_operator = 'ANY';

	/**
	 * Constructor
	 *
	 * @since 2.3.0
	 *
	 * @param array $data Data to validate against attributes
	 */
	public function __construct( $data = [] ) {

		// Ensure database defaults are enforced.
		$data = wp_parse_args(
			$data,
			[
				'operator'   => self::$default_operator,
				'lead_score' => 0,
			]
		);

		parent::__construct( $data );
	}

	/**
	 * Update a lead score rule with new data
	 *
	 * @since 1.1.0
	 *
	 * @param array $data  Lead Score data to update
	 *
	 * @return bool|integer Database ID on success, false otherwise
	 */
	public function update( array $data ) {

		foreach ( $data as $property => $value ) {
			// Only store that which is expected.
			if ( in_array( $property, self::$attributes, true ) ) {
				$this->data[ $property ] = $value;
			}
		}

		return $this->save();
	}

	/**
	 * Deletes Scoring Rule
	 *
	 * @since 2.3.0
	 *
	 * @param integer $id ID of database row
	 *
	 * @return bool True on success, false on failure
	 */
	public static function delete( int $id ) {

		$scoring_rule = self::find( $id );
		if ( false === $scoring_rule ) {
			// Nothing to do.
			return;
		}

		/**
		 * Filters whether a Scoring_Rule deletion should take place.
		 *
		 * @since 2.3.0
		 *
		 * @param false|null   $delete       Whether to go forward with deletion.
		 * @param Scoring_Rule $scoring_rule Scoring_Rule object.
		 */
		$check = apply_filters( 'personalizewp_pre_delete_scoring_rule', null, $scoring_rule );
		if ( null !== $check ) {
			return $check;
		}

		/**
		 * Fires before a Scoring_Rule is deleted, at the start of Scoring_Rule::delete().
		 *
		 * @since 2.3.0
		 *
		 * @param Scoring_Rule $scoring_rule Scoring_Rule object.
		 */
		do_action( 'personalizewp_before_delete_scoring_rule', $scoring_rule );

		// Use parent delete to remove the Scoring_Rule itself.
		return parent::delete( $id );
	}

	/**
	 * Retrieve Scoring_Rule instance via ID
	 *
	 * @since 1.1.0 Added to ensure correct return for docs.
	 *
	 * @param  integer $id ID of database row
	 *
	 * @return Scoring_Rule|false Instance of Scoring_Rule on success, false on failure
	 */
	public static function find( $id ) { // phpcs:ignore Generic.CodeAnalysis.UselessOverridingMethod.Found -- Reason: adjusting return type in docs
		return parent::find( $id );
	}

	/**
	 * Return the Scoring rules for a specific type
	 *
	 * @since 1.1.0
	 *
	 * @param array $types Types of Scoring rules
	 *
	 * @return array Array of Scoring rules
	 */
	public static function get_scoring_rules( $types = [] ) {
		global $wpdb;

		if ( empty( $types ) ) {
			return [];
		}

		// FIXME: Add caching?

		// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- Reason: SQL is prepared with placeholders.
		$placeholders  = implode( ', ', array_fill( 0, count( $types ), '%s' ) );
		$scoring_rules = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT * FROM {$wpdb->prefix}pwp_scoring_rules WHERE type IN ( {$placeholders} )",
				$types
			),
			ARRAY_A
		);
		// phpcs:enable

		return $scoring_rules;
	}
}
