<?php
/**
 * Appointment form class
 */
class WC_Appointment_Form implements WC_Appointments_Form_Renderer_Interface {

	/**
	 * Appointment product data.
	 *
	 * @var WC_Product_Appointment
	 */
	public $product;

	/**
	 * Appointment fields.
	 *
	 * @var array
	 */
	private $fields;

	/**
	 * Constructor.
	 *
	 * @param WC_Product_Appointment $product Appointment product.
	 */
	public function __construct( $product ) {
		$this->product = $product;
	}

	/**
	 * Prepare fields for the appointment form.
	 *
	 * Initializes and populates the form fields including timezone, staff, date, and add-ons.
	 *
	 * @return void
	 */
	public function prepare_fields(): void {
		// Destroy existing fields
		$this->reset_fields();

		// Add fields in order
		$this->timezone_field();
		$this->staff_field();
		$this->date_field();
		$this->addons_data_field();

		$this->fields = apply_filters( 'appointment_form_fields', $this->fields );
	}

	/**
	 * Reset fields array
	 */
	public function reset_fields(): void {
		$this->fields = [];
	}

	/**
	 * Add timezones field.
	 *
	 * Adds the timezone selection field if enabled for the product.
	 *
	 * @return void
	 */
	private function timezone_field(): void {
		// Timezones field
		if ( ! $this->product->has_timezones() ) {
			return;
		}

		// Get site's timezone.
		$tzstring = wc_timezone_string();

		// Add timezone from cookie.
		$tzstring = isset( $_COOKIE['appointments_time_zone'] ) ? sanitize_text_field( wp_unslash( $_COOKIE['appointments_time_zone'] ) ) : $tzstring;

		$this->add_field(
		    [
				'type'     => 'select-timezone',
				'name'     => 'timezone',
				'label'    => __( 'Timezone:', 'woocommerce-appointments' ) . ' <a class="selected-timezone">' . esc_html( str_replace( '_', ' ', $tzstring ) ) . '</a>',
				'selected' => $tzstring,
			],
		);
	}

	/**
	 * Add staff field.
	 *
	 * Adds the staff selection field if staff assignment is set to 'customer'.
	 *
	 * @return void
	 */
	private function staff_field(): void {
		// Staff field
		if ( ! $this->product->has_staff() || 'customer' !== $this->product->get_staff_assignment() ) {
			return;
		}

		$staff         = $this->product->get_staff();
		$staff_options = [];

		foreach ( $staff as $staff_member ) {
			$additional_cost = [];

			if ( $staff_member->get_base_cost() ) {
				$additional_cost[] = ' + ' . wp_strip_all_tags( wc_price( (float) $staff_member->get_base_cost() ) );
			}

			$additional_cost_string = [] !== $additional_cost ? implode( ', ', $additional_cost ) : '';

			$staff_options[ $staff_member->get_id() ] = $staff_member->get_display_name() . apply_filters( 'woocommerce_appointments_staff_additional_cost_string', $additional_cost_string, $staff_member );
		}

		$this->add_field(
		    [
				'type'    => 'select-staff',
				'name'    => 'staff',
				'label'   => $this->product->get_staff_label() ?: __( 'Providers', 'woocommerce-appointments' ),
				'nopref'  => $this->product->get_staff_nopref(),
				'class'   => [ 'wc_appointment_field_' . sanitize_title( $this->product->get_staff_label() ) ],
				'options' => $staff_options,
			],
		);
	}

	/**
	 * Add the date field to the appointment form.
	 *
	 * Selects and instantiates the appropriate date/time picker based on product duration unit.
	 *
	 * @return void
	 */
	private function date_field(): void {
		$picker = null;

		// Get date picker specific to the duration unit for this product
		switch ( $this->product->get_duration_unit() ) {
			case WC_Appointments_Constants::DURATION_MONTH:
				include_once __DIR__ . '/class-wc-appointment-form-month-picker.php';
				$picker = new WC_Appointment_Form_Month_Picker( $this );
				break;
			case WC_Appointments_Constants::DURATION_DAY:
			case WC_Appointments_Constants::DURATION_NIGHT:
				include_once __DIR__ . '/class-wc-appointment-form-date-picker.php';
				$picker = new WC_Appointment_Form_Date_Picker( $this );
				break;
			case WC_Appointments_Constants::DURATION_MINUTE:
			case WC_Appointments_Constants::DURATION_HOUR:
				include_once __DIR__ . '/class-wc-appointment-form-datetime-picker.php';
				$picker = new WC_Appointment_Form_Datetime_Picker( $this );
				break;
			default:
				break;
		}

		if ( ! is_null( $picker ) ) {
			$this->add_field( $picker->get_args() );
		}
	}

	/**
	 * Add Addons field.
	 *
	 * Adds hidden fields for tracking add-ons duration and cost.
	 *
	 * @return void
	 */
	private function addons_data_field(): void {
		// Addons fields
		$this->add_field(
		    [
				'type'  => 'hidden',
				'name'  => 'addons_duration',
				'value' => 0,
			],
		);
		$this->add_field(
		    [
				'type'  => 'hidden',
				'name'  => 'addons_cost',
				'value' => 0,
			],
		);
	}

	/**
	 * Add Field.
	 *
	 * Registers a new field to be rendered in the appointment form.
	 *
	 * @param array $field Field configuration array.
	 * @return void
	 */
	public function add_field( $field ): void {
		$default = [
			'name'  => '',
			'class' => [],
			'label' => '',
			'type'  => 'text',
		];

		$field = wp_parse_args( $field, $default );

		if ( ! $field['name'] || ! $field['type'] ) {
			return;
		}

		$nicename = 'wc_appointments_field_' . sanitize_title( $field['name'] );

		$field['name']    = $nicename;
		$field['class'][] = $nicename;

		$this->fields[ sanitize_title( $field['name'] ) ] = $field;
	}

	/**
	 * Output the form - called from the add to cart templates.
	 *
	 * Renders the complete appointment form.
	 *
	 * @return void
	 */
	public function output(): void {
		$this->render( $this->product );
	}

	/**
	 * Render the appointment form (interface implementation).
	 *
	 * @param WC_Product_Appointment $product Product to render form for.
	 * @return void
	 */
	public function render( WC_Product_Appointment $product ): void {
		$this->prepare_fields();

		foreach ( $this->fields as $field ) {
			wc_get_template(
			    'appointment-form/' . $field['type'] . '.php',
			    [
					'field'   => $field,
					'product' => $product,
				],
			    '',
			    WC_APPOINTMENTS_TEMPLATE_PATH,
			);
		}
	}

	/**
	 * Get posted form data into a neat array.
	 *
	 * @param  array  $posted   Posted data.
	 * @param  bool   $get_data Whether to return data.
	 * @return array|string|void Formatted data.
	 */
	public function get_posted_data( $posted = [], $get_data = false ) {
		wc_deprecated_function( __METHOD__, '4.7.0', 'wc_appointments_get_posted_data()' );
		return wc_appointments_get_posted_data( $posted, $this->product, $get_data );
	}

	/**
	 * Checks appointment data is correctly set, and that the chosen slots are indeed available.
	 *
	 * @param  array $data Appointment data.
	 * @return WP_Error|bool True on success, WP_Error on failure.
	 */
	public function is_appointable( $data ) {
		wc_deprecated_function( __METHOD__, '4.7.0', 'WC_Product_Appointment::is_appointable()' );
		return $this->product->is_appointable( $data );
	}

	/**
	 * Get an array of formatted time values.
	 *
	 * @param  int|string $timestamp Timestamp.
	 * @return array      Formatted time strings.
	 */
	public function get_formatted_times( $timestamp ) {
		wc_deprecated_function( __METHOD__, '4.7.0', 'wc_appointments_get_formatted_times()' );
		return wc_appointments_get_formatted_times( $timestamp );
	}

	/**
	 * Calculate costs from posted values.
	 *
	 * @param  array $posted Posted data.
	 * @return string        Calculated cost.
	 */
	public function calculate_appointment_cost( $posted ) {
		wc_deprecated_function( __METHOD__, '1.15.0', 'WC_Appointments_Cost_Calculation::calculate_appointment_cost()' );
		$data = wc_appointments_get_posted_data( $posted, $this->product );
		$cost = WC_Appointments_Cost_Calculation::calculate_appointment_cost( $data, $this->product );
		return apply_filters( 'appointment_form_calculated_appointment_cost', $cost, $this->product, $posted );
	}

}
