<?php // phpcs:ignore WordPress.Files.FileName
/**
 * Dynamic Rule base class.
 *
 * @class   YWDPD_Rule
 * @version 3.0.0
 * @package YITH WooCommerce Dynamic Prcing and Discounts\Abstracts
 */

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

/**
 * Abstract Dynamic Rule class.
 *
 * @since   3.0.0
 * @package YITH WooCommerce Dynamic Prcing and Discounts\Abstracts
 */
abstract class YWDPD_Rule extends YWDPD_Data {

	/**
	 * Stores rule data.
	 *
	 * @var array the common data
	 */
	protected $data = array(
		'key'                    => '',
		'name'                   => '',
		'active'                 => 'yes',
		'priority'               => 1,
		'schedule_discount_mode' => array(),
	);

	/**
	 * Post type.
	 *
	 * @var string
	 */
	protected $post_type = 'ywdpd_discount';

	/**
	 * Get the rule if the ID is passed, otherwise the rule is new and empty.
	 *
	 * @param int|YWDPD_Rule|object $obj ID to load from the DB (optional) or already queried data.
	 */
	public function __construct( $obj = 0 ) {
		parent::__construct( $obj );

		if ( is_numeric( $obj ) && $obj > 0 ) {
			$this->set_id( $obj );
		} elseif ( $obj instanceof self ) {
			$this->set_id( absint( $obj->get_id() ) );
		} elseif ( ! empty( $obj->ID ) ) {
			$this->set_id( absint( $obj->ID ) );
		} else {
			$this->set_object_read( true );
		}
	}
	/** SETTERS */

	/**
	 * Set the name.
	 *
	 * @param string $name the rule name.
	 *
	 * @since 3.0.0
	 */
	public function set_name( $name ) {
		$this->set_prop( 'name', $name );
	}

	/**
	 * Set the active prop.
	 *
	 * @param string $active the prop to check if the rule is enabled or not.
	 *
	 * @since 3.0.0
	 */
	public function set_active( $active ) {
		$this->set_prop( 'active', $active );
	}

	/**
	 * Set the key prop.
	 *
	 * @param string $key the prop to check if the rule is enabled or not.
	 *
	 * @since 3.0.0
	 */
	public function set_key( $key ) {
		$this->set_prop( 'key', $key );
	}

	/**
	 * Set the priority prop.
	 *
	 * @param int $priority the rule priority.
	 *
	 * @since 3.0.0
	 */
	public function set_priority( $priority ) {
		$this->set_prop( 'priority', absint( $priority ) );
	}

	/**
	 * Set the schedule discount prop.
	 *
	 * @param array $schedule_discount_mode The schedule meta.
	 *
	 * @since 3.0.0
	 */
	public function set_schedule_discount_mode( $schedule_discount_mode ) {
		$this->set_prop( 'schedule_discount_mode', $schedule_discount_mode );
	}
	/**
	 * GETTERS
	 */

	/**
	 * Get the rule priority.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return int
	 * @since 3.0.0
	 */
	public function get_priority( $context = 'view' ) {
		$value = $this->get_prop( 'priority', $context );

		return absint( $value );
	}

	/**
	 * Get the rule Name
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 * @since 3.0.0
	 */
	public function get_name( $context = 'view' ) {

		$value = $this->get_prop( 'name', $context );

		return $value;
	}

	/**
	 * Return the active meta.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @since  3.0.0
	 * @retrun string
	 */
	public function get_active( $context = 'view' ) {

		return $this->get_prop( 'active', $context );
	}

	/**
	 * Get the key prop.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 * @since 3.0.0
	 */
	public function get_key( $context = 'view' ) {
		return $this->get_prop( 'key', $context );
	}

	/**
	 * Get the schedule discount prop.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return array
	 * @since 3.0.0
	 */
	public function get_schedule_discount_mode( $context = 'view' ) {
		return $this->get_prop( 'schedule_discount_mode', $context );
	}

	/**
	 * Return the post type object.
	 *
	 * @return string
	 * @since 3.0.0
	 */
	public function get_post_type() {
		return $this->post_type;
	}

	/**
	 * Return the rule type ( Price or Cart )
	 *
	 * @return string
	 * @since 3.0.0
	 */
	abstract public function get_type();


	// Conditional methods.

	/**
	 * Return whether or not the rulse is active.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return bool
	 * @since 3.0.0
	 */
	public function is_enabled( $context = 'view' ) {
		$value = yith_plugin_fw_is_true( $this->get_active( $context ) );
		if ( 'view' === $context ) {
			/**
			 * APPLY_FILTERS: ywdpd_is_rule_enabled
			 *
			 * With this filter is possible change the enable status of a rule.
			 *
			 * @param bool       $value  The value to filter.
			 * @param YWDPD_Rule $object The current object.
			 *
			 * @return bool
			 */
			$value = apply_filters( 'ywdpd_is_rule_enabled', $value, $this );
		}

		return $value;
	}

	/**
	 * Check if the rule is scheduled.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return bool
	 * @since 3.0.0
	 */
	public function is_scheduled( $context = 'view' ) {

		$schedule = $this->get_schedule_discount_mode( $context );
		$value    = true;

		if ( isset( $schedule['schedule_type'] ) && 'schedule_dates' === $schedule['schedule_type'] ) {

			$from = ! empty( $schedule['schedule_from'] ) ? $schedule['schedule_from'] : false;
			$to   = ! empty( $schedule['schedule_to'] ) ? $schedule['schedule_to'] : false;

			if ( $from && $to ) {

				$timezone   = get_option( 'timezone_string' );
				$zone       = '' !== $timezone ? new DateTimeZone( $timezone ) : '';
				$gmt_offset = get_option( 'gmt_offset' );
				$ve         = $gmt_offset > 0 ? '+' : '-';
				try {
					if ( ! empty( $zone ) ) {
						$today_dt = new DateTime( 'now', $zone );
						$from_dt  = new DateTime( $from, $zone );
						$to_dt    = new DateTime( $to, $zone );

					} else {

						$today_dt = new DateTime( '@' . strtotime( 'now ' . $ve . absint( $gmt_offset ) . ' HOURS' ) );
						$from_dt  = new DateTime( '@' . strtotime( $from . ' ' . $ve . absint( $gmt_offset ) . ' HOURS' ) );
						$to_dt    = new DateTime( '@' . strtotime( $to . ' ' . $ve . absint( $gmt_offset ) . ' HOURS' ) );
					}
					$value = ! ( ( $today_dt < $from_dt ) || ( $today_dt > $to_dt ) );
				} catch ( Exception $e ) {

					$value = false;
				}
			} else {
				$value = false;
			}
		}
		if ( 'view' === $context ) {
			/**
			 * APPLY_FILTERS: ywdpd_rule_is_scheduled
			 *
			 * With this filter is possible schedule the rule.
			 *
			 * @param bool       $value    The value to filter.
			 * @param array      $schedule The schedule configuration.
			 * @param YWDPD_Rule $object   The current object.
			 *
			 * @return bool
			 */
			$value = apply_filters( 'ywdpd_rule_is_scheduled', $value, $schedule, $this );
		}

		return $value;
	}


	/**
	 * Check if this rule is Valid.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return bool
	 * @since 3.0.0
	 */
	abstract public function is_valid( $context = 'view' );


	/**
	 * Read the rule object.
	 *
	 * @throws Exception If invalid rule.
	 * @since 3.0.0
	 */
	protected function read() {
		$this->set_defaults();
		$post_object = get_post( $this->get_id() );

		if ( ! $this->get_id() || ! $post_object || $this->get_post_type() !== $post_object->post_type ) {
			throw new Exception( __( 'Invalid rule.', 'ywdpd' ) );
		}
		foreach ( $this->data as $prop => $default_value ) {
			if ( 'name' === $prop ) {
				$value = $post_object->post_title;
			} else {
				$meta  = $this->get_meta_by_prop( $prop );
				$value = metadata_exists( 'post', $this->get_id(), $meta ) ? get_post_meta( $this->get_id(), $meta, true ) : $default_value;
				$value = maybe_unserialize( $value );

			}
			$method_name = str_replace( '-', '_', $prop );
			$setter      = "set_{$method_name}";
			if ( method_exists( $this, $setter ) ) {
				$this->$setter( $value );
			} else {
				$this->set_prop( $prop, $value );
			}
		}
		$this->set_object_read( true );
		/**
		 * DO_ACTION: ywdpd_rule_read
		 *
		 * The action is triggered after to read the rule from db.
		 *
		 * @param int $id the rule id.
		 */
		do_action( 'ywdpd_rule_read', $this->get_id() );
	}

	/**
	 * Get the status of this rule
	 *
	 * @return string
	 * @throws Exception The exception.
	 * @since 4.0.0
	 */
	public function get_rule_status() {
		$schedule_options = $this->get_schedule_discount_mode();

		$current_key = 'inactive';

		if ( $this->is_enabled() && isset( $schedule_options['schedule_type'] ) ) {
			if ( 'schedule_dates' !== $schedule_options['schedule_type'] ) {
				$current_key = 'active';

			} else {

				$from      = ! empty( $schedule_options['schedule_from'] ) ? $schedule_options['schedule_from'] : false;
				$to        = ! empty( $schedule_options['schedule_to'] ) ? $schedule_options['schedule_to'] : false;
				$from_date = false;
				$to_date   = false;
				$now       = new DateTime( 'now', new DateTimeZone( wc_timezone_string() ) );

				if ( $from ) {
					$from_date = new DateTime( $from, new DateTimeZone( wc_timezone_string() ) );
				}
				if ( $to ) {
					$to_date = new DateTime( $to, new DateTimeZone( wc_timezone_string() ) );
				}

				if ( $from_date && $from_date > $now ) {

					$current_key = 'scheduled';

				} elseif ( $to_date && $to_date < $now ) {

					$current_key = 'ended';

				} else {
					$current_key = 'active';
				}
			}
		}

		return $current_key;
	}

	/**
	 * Get the formatted status of this rule
	 *
	 * @return string
	 * @throws Exception The exception.
	 * @since 4.0.0
	 */
	public function get_formatted_status() {
		$schedule_options = $this->get_schedule_discount_mode();
		$status           = $this->get_rule_status();
		$status_label     = array(
			'active'    => __( 'Active', 'ywdpd' ),
			'inactive'  => __( 'Inactive', 'ywdpd' ),
			'scheduled' => __( 'Scheduled', 'ywdpd' ),
			'ended'     => __( 'Ended', 'ywdpd' ),
		);

		if ( 'scheduled' === $status ) {
			$current_content = sprintf( '%1$s ( %2$s )', $status_label[ $status ], date_i18n( wc_date_format() . ' ' . wc_time_format(), strtotime( $schedule_options['schedule_from'] ) ) );
		} else {
			$current_content = $status_label[ $status ];
		}

		return sprintf( '<span class="ywdpd-rule-status %1$s">%2$s</span>', $status, $current_content );

	}
}
