<?php

declare(strict_types=1);

namespace FormConnector\API;

/**
 * Reusable REST `args` definitions for `register_rest_route`.
 *
 * Centralising the sanitize/validate callbacks here means every endpoint
 * gets the same, audited handling instead of pulling raw values from the
 * request body via `$request->get_param()` and trusting them.
 *
 * Usage:
 *   register_rest_route(self::NAMESPACE, '/carehq/settings', [
 *       'methods'             => \WP_REST_Server::CREATABLE,
 *       'callback'            => [self::class, 'save_settings'],
 *       'permission_callback' => [self::class, 'permission_check'],
 *       'args'                => [
 *           'account_id' => Schema::text(['required' => true]),
 *           'api_key'    => Schema::text(['required' => true]),
 *       ],
 *   ]);
 */
class Schema {

	/**
	 * A short text field (single line). Strips tags and trims whitespace.
	 *
	 * @param array<string,mixed> $overrides
	 * @return array<string,mixed>
	 */
	public static function text(array $overrides = []): array {
		return array_merge([
			'type' => 'string',
			'required' => false,
			'sanitize_callback' => 'sanitize_text_field',
			'validate_callback' => [self::class, 'validate_string'],
		], $overrides);
	}

	/**
	 * A multi-line text field. Strips disallowed tags but keeps newlines.
	 *
	 * @param array<string,mixed> $overrides
	 * @return array<string,mixed>
	 */
	public static function textarea(array $overrides = []): array {
		return array_merge([
			'type' => 'string',
			'required' => false,
			'sanitize_callback' => 'sanitize_textarea_field',
			'validate_callback' => [self::class, 'validate_string'],
		], $overrides);
	}

	/**
	 * An email address.
	 *
	 * @param array<string,mixed> $overrides
	 * @return array<string,mixed>
	 */
	public static function email(array $overrides = []): array {
		return array_merge([
			'type' => 'string',
			'required' => false,
			'format' => 'email',
			'sanitize_callback' => 'sanitize_email',
			'validate_callback' => static function ($value): bool {
				if ($value === '' || $value === null) {
					return true;
				}
				return is_string($value) && is_email($value) !== false;
			},
		], $overrides);
	}

	/**
	 * A URL.
	 *
	 * @param array<string,mixed> $overrides
	 * @return array<string,mixed>
	 */
	public static function url(array $overrides = []): array {
		return array_merge([
			'type' => 'string',
			'required' => false,
			'format' => 'uri',
			'sanitize_callback' => 'esc_url_raw',
			'validate_callback' => static function ($value): bool {
				if ($value === '' || $value === null) {
					return true;
				}
				return is_string($value) && wp_http_validate_url($value) !== false;
			},
		], $overrides);
	}

	/**
	 * A slug-style identifier.
	 *
	 * @param array<string,mixed> $overrides
	 * @return array<string,mixed>
	 */
	public static function slug(array $overrides = []): array {
		return array_merge([
			'type' => 'string',
			'required' => false,
			'sanitize_callback' => 'sanitize_key',
			'validate_callback' => [self::class, 'validate_string'],
		], $overrides);
	}

	/**
	 * A positive integer.
	 *
	 * @param array<string,mixed> $overrides
	 * @return array<string,mixed>
	 */
	public static function int(array $overrides = []): array {
		return array_merge([
			'type' => 'integer',
			'required' => false,
			'sanitize_callback' => 'absint',
			'validate_callback' => static function ($value): bool {
				return is_numeric($value);
			},
		], $overrides);
	}

	/**
	 * A boolean.
	 *
	 * @param array<string,mixed> $overrides
	 * @return array<string,mixed>
	 */
	public static function bool(array $overrides = []): array {
		return array_merge([
			'type' => 'boolean',
			'required' => false,
			'sanitize_callback' => static function ($value): bool {
				return rest_sanitize_boolean($value);
			},
			'validate_callback' => static function ($value): bool {
				return rest_is_boolean($value);
			},
		], $overrides);
	}

	/**
	 * Restrict a string to a fixed set of allowed values.
	 *
	 * @param array<int,string>   $allowed
	 * @param array<string,mixed> $overrides
	 * @return array<string,mixed>
	 */
	public static function enum(array $allowed, array $overrides = []): array {
		return array_merge([
			'type' => 'string',
			'required' => false,
			'enum' => $allowed,
			'sanitize_callback' => 'sanitize_text_field',
			'validate_callback' => static function ($value) use ($allowed): bool {
				return is_string($value) && in_array($value, $allowed, true);
			},
		], $overrides);
	}

	/**
	 * Permissive object validator — relies on JSON shape from the request.
	 *
	 * @param array<string,mixed> $overrides
	 * @return array<string,mixed>
	 */
	public static function object(array $overrides = []): array {
		return array_merge([
			'type' => 'object',
			'required' => false,
			'validate_callback' => static function ($value): bool {
				return is_array($value);
			},
		], $overrides);
	}

	/**
	 * Permissive array validator.
	 *
	 * @param array<string,mixed> $overrides
	 * @return array<string,mixed>
	 */
	public static function array(array $overrides = []): array {
		return array_merge([
			'type' => 'array',
			'required' => false,
			'validate_callback' => static function ($value): bool {
				return is_array($value);
			},
		], $overrides);
	}

	/**
	 * Validate that a value is a string (allowing optional / empty).
	 */
	public static function validate_string($value, $request = null, $key = ''): bool {
		if ($value === null || $value === '') {
			return true;
		}
		return is_string($value);
	}
}
