<?php

namespace Automattic\WCShipping\Connect;

use WP_Error;

class WC_Connect_Service_Schemas_Validator {

	/**
	 * Validates the overall passed services object (all service types and all services therein)
	 *
	 * @param object $services
	 *
	 * @return WP_Error|true
	 */
	public function validate_service_schemas( $service_schemas ) {

		if ( ! is_object( $service_schemas ) ) {
			return new WP_Error(
				'outermost_container_not_object',
				'Malformed service schemas. Outermost container is not an object.'
			);
		}

		if ( ! isset( $service_schemas->shipping ) || ! is_array( $service_schemas->shipping ) ) {
			return new WP_Error(
				'service_type_not_ref_array',
				'Malformed service schemas. \'shipping\' does not reference an array.'
			);
		}

		$service_counter = 0;
		foreach ( $service_schemas->shipping as $service_schema ) {
			if ( ! is_object( $service_schema ) ) {
				return new WP_Error(
					'service_not_ref_object',
					sprintf(
						'Malformed service schema. Service type \'shipping\' [%d] does not reference an object.',
						$service_counter
					)
				);
			}

			$result = $this->validate_service_schema( 'shipping', $service_counter, $service_schema );
			if ( is_wp_error( $result ) ) {
				return $result;
			}

			++$service_counter;
		}

		if ( ! isset( $service_schemas->boxes ) || ! is_object( $service_schemas->boxes ) ) {
			return new WP_Error(
				'boxes_not_object',
				'Malformed service schemas. \'boxes\' is not an object.'
			);
		}

		return true;
	}

	/**
	 * Validates a particular service schema, especially the parts of the service that WC relies
	 * on like id, method_title, method_description, etc
	 *
	 * @param string  $service_type
	 * @param integer $service_counter
	 * @param object  $service
	 *
	 * @return WP_Error|true
	 */
	protected function validate_service_schema( $service_type, $service_counter, $service_schema ) {
		$required_properties = array(
			'id'                 => 'string',
			'method_description' => 'string',
			'method_title'       => 'string',
			'service_settings'   => 'object',
			'form_layout'        => 'array',
		);

		foreach ( $required_properties as $required_property => $required_property_type ) {
			if ( ! property_exists( $service_schema, $required_property ) ) {
				return new WP_Error(
					'required_service_property_missing',
					sprintf(
						'Malformed service schema. Service type \'%s\' [%d] does not include a required \'%s\' property.',
						$service_type,
						$service_counter,
						$required_property
					)
				);
			}

			$property_type = gettype( $service_schema->$required_property );
			if ( $required_property_type !== $property_type ) {
				return new WP_Error(
					'required_service_property_wrong_type',
					sprintf(
						'Malformed service schema. Service type \'%s\' [%d] property \'%s\' is a %s. Was expecting a %s.',
						$service_type,
						$service_counter,
						$service_schema->$required_property,
						$property_type,
						$required_property_type
					)
				);
			}
		}

		return $this->validate_service_schema_settings( $service_schema->id, $service_schema->service_settings );
	}

	/**
	 * Validates a particular service's service settings schema, especially the parts of the
	 * service settings that WC relies on like type, required and properties
	 *
	 * @param string $service_id
	 * @param object $service_settings
	 *
	 * @return WP_Error|true
	 */
	protected function validate_service_schema_settings( $service_id, $service_settings ) {
		$required_properties = array(
			'type'       => 'string',
			'required'   => 'array',
			'properties' => 'object',
		);

		foreach ( $required_properties as $required_property => $required_property_type ) {
			if ( ! property_exists( $service_settings, $required_property ) ) {
				return new WP_Error(
					'service_settings_missing_required_property',
					sprintf(
						'The settings part of a service schema is malformed. Service \'%s\' service_settings do not include a required \'%s\' property.',
						$service_id,
						$required_property
					)
				);
			}

			$property_type = gettype( $service_settings->$required_property );
			if ( $required_property_type !== $property_type ) {
				return new WP_Error(
					'service_settings_property_wrong_type',
					sprintf(
						"The settings part of a service schema is malformed. Service '%s' service_setting property '%s' is a %s. Was expecting a %s.",
						$service_id,
						$required_property,
						$property_type,
						$required_property_type
					)
				);
			}
		}

		$result = $this->validate_service_settings_required_properties( $service_id, $service_settings->properties );
		if ( is_wp_error( $result ) ) {
			return $result;
		}

		return true;
	}


	/**
	 * Validates a particular service's schema's required properties, especially the parts of the
	 * properties that WC relies on and title
	 *
	 * @param string $service_id
	 * @param object $service_settings_properties
	 *
	 * @return WP_Error|true
	 */
	protected function validate_service_settings_required_properties( $service_id, $service_settings_properties ) {
		$required_properties = array(
			'title',
		);

		foreach ( $required_properties as $required_property ) {
			if ( ! property_exists( $service_settings_properties, $required_property ) ) {
				return new WP_Error(
					'service_properties_missing_required_property',
					sprintf(
						"The properties part of a service schema is malformed. Service '%s' service_settings properties do not include a required '%s' property.",
						$service_id,
						$required_property
					)
				);
			}
		}

		return true;
	}
}
