<?php

class The7_Post_Meta_WP_Sanitizer {

	public static function sanitize_meta( $meta, $post_id ) {
		$post_type = get_post_type( $post_id );
		$meta_fields = self::get_meta_fields_by_post_type( $post_type );
		$meta = is_array( $meta ) ? $meta : array();

		$sanitized_meta = array();
		foreach ( $meta_fields as $meta_field ) {
			if ( self::should_skip_meta_field( $meta_field ) ) {
				continue;
			}

			// Exclude from preset.
			if ( ! empty( $meta_field['exclude_from_presets'] ) ) {
				continue;
			}

			$field_id = $meta_field['id'];

			if ( array_key_exists( $field_id, $meta ) ) {
				$old = get_post_meta( $post_id, $field_id, empty( $meta_field['multiple'] ) );
				$value = self::apply_meta_box_value_filters( $meta[ $field_id ], $meta_field, $old, $post_id );
				$sanitized_meta[ $field_id ] = self::sanitize_meta_value( $value, $meta_field );
				continue;
			}

			if ( 'checkbox' === $meta_field['type'] ) {
				$sanitized_meta[ $field_id ] = '0';
			} elseif ( 'image_advanced_mk2' === $meta_field['type'] ) {
				$images = get_post_meta( $post_id, $field_id, true );
				$sanitized_meta[ $field_id ] = self::sanitize_integer_list( $images ? $images : array() );
			}
		}

		return $sanitized_meta;
	}

	public static function sanitize_title( $title ) {
		return sanitize_text_field( $title );
	}

	public static function get_meta_fields_by_post_type( $post_type ) {
		global $DT_META_BOXES;

		$fields = array();
		foreach ( $DT_META_BOXES as $box ) {
			if ( ! isset( $box['pages'], $box['fields'] ) || ! in_array( $post_type, $box['pages'] ) ) {
				continue;
			}

			$box_fields = $box['fields'];
			if ( class_exists( 'The7_RW_Meta_Box' ) ) {
				$box_fields = The7_RW_Meta_Box::normalize_fields( $box_fields );
				$box_fields = The7_RW_Meta_Box::get_fields( $box_fields );
			} else {
				$box_fields = self::flatten_fields( $box_fields );
			}

			$fields = array_merge( $fields, $box_fields );
		}

		return $fields;
	}

	protected static function should_skip_meta_field( $field ) {
		if ( ! is_array( $field ) || empty( $field['id'] ) || empty( $field['type'] ) ) {
			return true;
		}

		if ( class_exists( 'The7_RW_Meta_Box' ) && The7_RW_Meta_Box::is_decorative_field( $field ) ) {
			return true;
		}

		return in_array( $field['type'], array( 'heading', 'divider', 'button' ), true );
	}

	protected static function apply_meta_box_value_filters( $value, $field, $old, $post_id ) {
		if ( class_exists( 'The7_RW_Meta_Box' ) ) {
			$value = The7_RW_Meta_Box::apply_field_class_filters( $field, 'value', $value, $old, $post_id );
		}

		$name = $field['id'];
		$value = apply_filters( "the7_mb_{$field['type']}_value", $value, $field, $old );
		$value = apply_filters( "the7_mb_{$name}_value", $value, $field, $old );

		return $value;
	}

	protected static function sanitize_meta_value( $value, $field ) {
		if ( ! empty( $field['sanitize'] ) ) {
			$value = self::apply_declared_sanitizer( $value, $field );
		}

		switch ( $field['type'] ) {
			case 'checkbox':
				return empty( $value ) ? '0' : '1';
			case 'radio':
			case 'select':
				return self::sanitize_option_value( $value, $field );
			case 'checkbox_list':
			case 'taxonomy_list':
				return self::sanitize_option_list( $value, $field );
			case 'image_advanced_mk2':
				return self::sanitize_integer_list( $value );
			case 'color':
				return self::sanitize_color( $value );
			case 'slider':
				return self::sanitize_slider( $value, $field );
			case 'dimension':
				return function_exists( 'of_sanitize_css_width' ) ? of_sanitize_css_width( $value ) : sanitize_text_field( $value );
		}

		return self::sanitize_text_value( $value );
	}

	protected static function apply_declared_sanitizer( $value, $field ) {
		$sanitizer_name = sanitize_key( $field['sanitize'] );
		$filters = array(
			"of_sanitize_{$sanitizer_name}",
			"of_{$sanitizer_name}",
		);

		foreach ( $filters as $filter ) {
			if ( has_filter( $filter ) ) {
				return apply_filters( $filter, $value, $field );
			}
		}

		return $value;
	}

	protected static function sanitize_option_value( $value, $field ) {
		if ( ! empty( $field['multiple'] ) ) {
			return self::sanitize_option_list( $value, $field );
		}

		$options = self::get_option_keys( $field );
		$value = is_scalar( $value ) ? (string) $value : '';
		if ( in_array( $value, $options, true ) ) {
			return $value;
		}

		$default = isset( $field['std'] ) && is_scalar( $field['std'] ) ? (string) $field['std'] : '';
		return in_array( $default, $options, true ) ? $default : '';
	}

	protected static function sanitize_option_list( $value, $field ) {
		$options = self::get_option_keys( $field );
		if ( empty( $options ) ) {
			return array();
		}

		$values = array_map( 'strval', (array) $value );
		return array_values( array_intersect( $values, $options ) );
	}

	protected static function get_option_keys( $field ) {
		if ( empty( $field['options'] ) || ! is_array( $field['options'] ) ) {
			return array();
		}

		return array_map( 'strval', array_keys( $field['options'] ) );
	}

	protected static function sanitize_integer_list( $value ) {
		return array_values( array_filter( array_map( 'absint', (array) $value ) ) );
	}

	protected static function sanitize_color( $value ) {
		$value = is_scalar( $value ) ? (string) $value : '';
		if ( function_exists( 'of_sanitize_color' ) ) {
			return of_sanitize_color( $value );
		}

		$sanitized = sanitize_hex_color( $value );
		return $sanitized ? $sanitized : '';
	}

	protected static function sanitize_slider( $value, $field ) {
		if ( ! empty( $field['sanitize'] ) ) {
			return self::sanitize_text_value( $value );
		}

		$value = absint( $value );
		$options = isset( $field['js_options'] ) && is_array( $field['js_options'] ) ? $field['js_options'] : array();
		$options = wp_parse_args(
			$options,
			array(
				'min' => null,
				'max' => null,
			)
		);

		if ( null !== $options['min'] ) {
			$value = max( (int) $options['min'], $value );
		}

		if ( null !== $options['max'] ) {
			$value = min( (int) $options['max'], $value );
		}

		return (string) $value;
	}

	protected static function sanitize_text_value( $value ) {
		if ( is_array( $value ) ) {
			return array_map( array( __CLASS__, 'sanitize_text_value' ), $value );
		}

		return is_scalar( $value ) ? sanitize_text_field( $value ) : '';
	}

	protected static function flatten_fields( $fields ) {
		$flat_fields = array();
		foreach ( $fields as $field ) {
			$flat_fields[] = $field;
			if ( isset( $field['fields'] ) && is_array( $field['fields'] ) ) {
				$flat_fields = array_merge( $flat_fields, self::flatten_fields( $field['fields'] ) );
			}
		}

		return $flat_fields;
	}
}
