<?php

namespace Elementor\Modules\AtomicWidgets\Parsers;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

class Style_Parser {
	const VALID_TYPES = [
		'class',
	];

	const VALID_STATES = [
		'hover',
		'active',
		'focus',
		null,
	];

	private array $schema;

	public function __construct( array $schema ) {
		$this->schema = $schema;
	}

	public static function make( array $schema ): self {
		return new static( $schema );
	}

	/**
	 * @param array $style
	 * the style object to validate
	 */
	private function validate( array $style ): Parse_Result {
		$validated_style = $style;
		$result = Parse_Result::make();

		if ( ! isset( $style['id'] ) || ! is_string( $style['id'] ) ) {
			$result->errors()->add( 'id', 'missing_or_invalid' );
		}

		if ( ! isset( $style['type'] ) || ! in_array( $style['type'], self::VALID_TYPES, true ) ) {
			$result->errors()->add( 'type', 'missing_or_invalid' );
		}

		if ( ! isset( $style['label'] ) || ! is_string( $style['label'] ) ) {
			$result->errors()->add( 'label', 'missing_or_invalid' );
		}

		if ( ! isset( $style['variants'] ) || ! is_array( $style['variants'] ) ) {
			$result->errors()->add( 'variants', 'missing_or_invalid' );

			unset( $validated_style['variants'] );

			return $result->wrap( $validated_style );
		}

		$props_parser = Props_Parser::make( $this->schema );

		foreach ( $style['variants'] as $variant_index => $variant ) {
			if ( ! isset( $variant['meta'] ) ) {
				$result->errors()->add( 'meta', 'missing' );

				continue;
			}

			$meta_result = $this->validate_meta( $variant['meta'] );

			$result->errors()->merge( $meta_result->errors(), 'meta' );

			if ( $meta_result->is_valid() ) {
				$variant_result = $props_parser->validate( $variant['props'] );

				$result->errors()->merge( $variant_result->errors(), "variants[$variant_index]" );

				$validated_style['variants'][ $variant_index ]['props'] = $variant_result->unwrap();
			} else {
				unset( $validated_style['variants'][ $variant_index ] );
			}
		}

		return $result->wrap( $validated_style );
	}

	private function validate_meta( $meta ): Parse_Result {
		$result = Parse_Result::make();

		if ( ! is_array( $meta ) ) {
			$result->errors()->add( 'meta', 'invalid_type' );

			return $result;
		}

		if ( ! array_key_exists( 'state', $meta ) || ! in_array( $meta['state'], self::VALID_STATES, true ) ) {
			$result->errors()->add( 'state', 'missing_or_invalid_value' );

			return $result;
		}

		// TODO: Validate breakpoint based on the existing breakpoints in the system [EDS-528]
		if ( ! isset( $meta['breakpoint'] ) || ! is_string( $meta['breakpoint'] ) ) {
			$result->errors()->add( 'breakpoint', 'missing_or_invalid_value' );

			return $result;
		}

		return $result;
	}

	private function sanitize_meta( $meta ) {
		if ( ! is_array( $meta ) ) {
			return [];
		}

		if ( isset( $meta['breakpoint'] ) ) {
			$meta['breakpoint'] = sanitize_key( $meta['breakpoint'] );
		}

		return $meta;
	}

	/**
	 * @param array $style
	 * the style object to sanitize
	 */
	private function sanitize( array $style ): Parse_Result {
		$props_parser = Props_Parser::make( $this->schema );

		if ( isset( $style['label'] ) ) {
			$style['label'] = sanitize_text_field( $style['label'] );
		}

		if ( isset( $style['id'] ) ) {
			$style['id'] = sanitize_key( $style['id'] );
		}

		if ( ! empty( $style['variants'] ) ) {
			foreach ( $style['variants'] as $variant_index => $variant ) {
				$style['variants'][ $variant_index ]['props'] = $props_parser->sanitize( $variant['props'] )->unwrap();
				$style['variants'][ $variant_index ]['meta'] = $this->sanitize_meta( $variant['meta'] );
			}
		}

		return Parse_Result::make()->wrap( $style );
	}

	/**
	 * @param array $style
	 * the style object to parse
	 */
	public function parse( array $style ): Parse_Result {
		$validate_result = $this->validate( $style );

		$sanitize_result = $this->sanitize( $validate_result->unwrap() );

		$sanitize_result->errors()->merge( $validate_result->errors() );

		return $sanitize_result;
	}
}
