<?php

namespace Elementor\App\Modules\Onboarding\Validation;

use WP_Error;

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

abstract class Base_Validator {

	protected array $errors = [];

	abstract protected function get_rules(): array;

	public function validate( array $params ) {
		if ( ! is_array( $params ) ) {
			return new WP_Error( 'invalid_params', 'Parameters must be an array.', [ 'status' => 400 ] );
		}

		$this->errors = [];
		$validated = [];

		foreach ( $this->get_rules() as $field => $rule ) {
			if ( ! array_key_exists( $field, $params ) ) {
				continue;
			}

			$result = $this->validate_field( $field, $params[ $field ], $rule );

			if ( is_wp_error( $result ) ) {
				return $result;
			}

			$validated[ $field ] = $result;
		}

		return $validated;
	}

	protected function validate_field( string $field, $value, array $rule ) {
		$type = $rule['type'] ?? 'string';
		$nullable = $rule['nullable'] ?? false;

		if ( null === $value ) {
			if ( $nullable ) {
				return null;
			}

			return $this->error( $field, "{$field} cannot be null." );
		}

		switch ( $type ) {
			case 'string':
				return $this->validate_string( $field, $value );
			case 'int':
				return $this->validate_int( $field, $value );
			case 'bool':
				return $this->validate_bool( $field, $value );
			case 'array':
				return $this->validate_array( $field, $value, $rule );
			case 'string_array':
				return $this->validate_string_array( $field, $value );
			case 'custom_data':
				return $this->validate_custom_data( $field, $value );
			default:
				return $value;
		}
	}

	protected function validate_string( string $field, $value ) {
		if ( ! is_string( $value ) ) {
			return $this->error( $field, "{$field} must be a string." );
		}

		return sanitize_text_field( $value );
	}

	protected function validate_int( string $field, $value ) {
		if ( ! is_numeric( $value ) ) {
			return $this->error( $field, "{$field} must be a number." );
		}

		return (int) $value;
	}

	protected function validate_bool( string $field, $value ) {
		if ( ! is_bool( $value ) ) {
			return $this->error( $field, "{$field} must be a boolean." );
		}

		return $value;
	}

	protected function validate_array( string $field, $value, array $rule ) {
		if ( ! is_array( $value ) ) {
			return $this->error( $field, "{$field} must be an array." );
		}

		$allowed = $rule['allowed'] ?? null;

		if ( $allowed && ! in_array( $value, $allowed, true ) ) {
			return $this->error( $field, "{$field} contains invalid value." );
		}

		return $value;
	}

	protected function validate_string_array( string $field, $value ) {
		if ( ! is_array( $value ) ) {
			return $this->error( $field, "{$field} must be an array." );
		}

		return array_values(
			array_filter(
				array_map(
					static function ( $item ) {
						return is_string( $item ) ? sanitize_text_field( $item ) : null;
					},
					$value
				),
				static function ( $item ) {
					return null !== $item;
				}
			)
		);
	}

	protected function validate_custom_data( string $field, $value ) {
		if ( ! is_array( $value ) ) {
			return $this->error( $field, "{$field} must be an array." );
		}

		return $this->sanitize_recursive( $value );
	}

	protected function sanitize_recursive( array $data ): array {
		$sanitized = [];

		foreach ( $data as $key => $value ) {
			$safe_key = sanitize_key( $key );

			if ( is_string( $value ) ) {
				$sanitized[ $safe_key ] = sanitize_text_field( $value );
			} elseif ( is_array( $value ) ) {
				$sanitized[ $safe_key ] = $this->sanitize_recursive( $value );
			} elseif ( is_numeric( $value ) || is_bool( $value ) || null === $value ) {
				$sanitized[ $safe_key ] = $value;
			} else {
				$sanitized[ $safe_key ] = null;
			}
		}

		return $sanitized;
	}

	protected function error( string $field, string $message ): WP_Error {
		return new WP_Error(
			'invalid_' . $field,
			$message,
			[ 'status' => 400 ]
		);
	}
}
