<?php
/**
 * Bounded limits for local schema validation.
 *
 * @package SEOvaultSEO\Schema\Validation
 */

namespace SEOvaultSEO\Schema\Validation;

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

/**
 * Defines bounded, filterable limits for untrusted custom JSON-LD.
 */
class ValidationLimits {

	const DEFAULT_MAX_JSON_BYTES = 262144;
	const HARD_MAX_JSON_BYTES    = 1048576;
	const DEFAULT_MAX_ENTITIES   = 250;
	const HARD_MAX_ENTITIES      = 500;
	const DEFAULT_MAX_DEPTH      = 32;
	const HARD_MAX_DEPTH         = 64;
	const DEFAULT_MAX_PROPERTIES = 100;
	const HARD_MAX_PROPERTIES    = 500;
	const DEFAULT_MAX_ISSUES     = 200;
	const HARD_MAX_ISSUES        = 500;

	/**
	 * Get effective, bounded validation limits.
	 *
	 * @param array $overrides Explicit safe overrides, primarily for tests.
	 * @return array
	 */
	public static function get( $overrides = array() ) {
		$limits = array(
			'max_json_bytes' => self::DEFAULT_MAX_JSON_BYTES,
			'max_entities'   => self::DEFAULT_MAX_ENTITIES,
			'max_depth'      => self::DEFAULT_MAX_DEPTH,
			'max_properties' => self::DEFAULT_MAX_PROPERTIES,
			'max_issues'     => self::DEFAULT_MAX_ISSUES,
		);

		if ( function_exists( 'apply_filters' ) ) {
			$limits['max_json_bytes'] = apply_filters( 'seovaultseo_schema_validation_max_json_bytes', $limits['max_json_bytes'] );
			$limits['max_entities']   = apply_filters( 'seovaultseo_schema_validation_max_entities', $limits['max_entities'] );
			$limits['max_depth']      = apply_filters( 'seovaultseo_schema_validation_max_depth', $limits['max_depth'] );
			$limits['max_properties'] = apply_filters( 'seovaultseo_schema_validation_max_properties', $limits['max_properties'] );
			$limits['max_issues']     = apply_filters( 'seovaultseo_schema_validation_max_issues', $limits['max_issues'] );
		}

		if ( is_array( $overrides ) ) {
			foreach ( array_keys( $limits ) as $key ) {
				if ( isset( $overrides[ $key ] ) ) {
					$limits[ $key ] = $overrides[ $key ];
				}
			}
		}

		$limits['max_json_bytes'] = self::clamp( $limits['max_json_bytes'], self::DEFAULT_MAX_JSON_BYTES, self::HARD_MAX_JSON_BYTES );
		$limits['max_entities']   = self::clamp( $limits['max_entities'], self::DEFAULT_MAX_ENTITIES, self::HARD_MAX_ENTITIES );
		$limits['max_depth']      = self::clamp( $limits['max_depth'], self::DEFAULT_MAX_DEPTH, self::HARD_MAX_DEPTH );
		$limits['max_properties'] = self::clamp( $limits['max_properties'], self::DEFAULT_MAX_PROPERTIES, self::HARD_MAX_PROPERTIES );
		$limits['max_issues']     = self::clamp( $limits['max_issues'], self::DEFAULT_MAX_ISSUES, self::HARD_MAX_ISSUES );

		return $limits;
	}

	/**
	 * Clamp a limit to a positive value with a hard upper bound.
	 *
	 * @param mixed $value   Candidate value.
	 * @param int   $fallback Default value.
	 * @param int   $maximum Hard maximum.
	 * @return int
	 */
	private static function clamp( $value, $fallback, $maximum ) {
		$value = is_numeric( $value ) ? (int) $value : $fallback;
		$value = max( 1, $value );

		return min( $value, $maximum );
	}
}
