<?php

namespace ElementPack\Modules\TextGradientBackground\Normalizers;

use ElementPack\Modules\TextGradientBackground\Atomic\Support;

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

class Settings_Normalizer {
	public function normalize( $widget ) {
		if ( $this->is_atomic_heading_widget( $widget ) ) {
			return $this->normalize_atomic_settings( $widget );
		}

		return $this->normalize_legacy_settings( $widget );
	}

	private function normalize_atomic_settings( $widget ) {
		$candidates = [];

		if ( method_exists( $widget, 'get_atomic_setting' ) ) {
			$candidates[] = [
				Support::ENABLE_KEY => $this->get_atomic_setting( $widget, Support::ENABLE_KEY ),
				Support::SELECTOR_KEY => $this->get_atomic_setting( $widget, Support::SELECTOR_KEY ),
				Support::BACKGROUND_KEY => $this->get_atomic_setting( $widget, Support::BACKGROUND_KEY ),
				Support::BACKGROUND_START_COLOR_KEY => $this->get_atomic_setting( $widget, Support::BACKGROUND_START_COLOR_KEY ),
				Support::BACKGROUND_END_COLOR_KEY => $this->get_atomic_setting( $widget, Support::BACKGROUND_END_COLOR_KEY ),
				Support::BACKGROUND_START_COLOR_LOCATION_KEY => $this->get_atomic_setting( $widget, Support::BACKGROUND_START_COLOR_LOCATION_KEY ),
				Support::BACKGROUND_END_COLOR_LOCATION_KEY => $this->get_atomic_setting( $widget, Support::BACKGROUND_END_COLOR_LOCATION_KEY ),
				Support::BACKGROUND_ANGLE_KEY => $this->get_atomic_setting( $widget, Support::BACKGROUND_ANGLE_KEY ),
			];
		}

		if ( method_exists( $widget, 'get_data' ) ) {
			try {
				$raw_settings = $widget->get_data( 'settings' );
			} catch ( \Throwable $e ) {
				$raw_settings = null;
			}

			if ( is_array( $raw_settings ) ) {
				$candidates[] = $raw_settings;
			}
		}

		foreach ( $candidates as $candidate ) {
			$normalized = $this->normalize_settings_array( $candidate, true );

			if ( $normalized ) {
				return $this->apply_widget_defaults( $widget, $normalized );
			}
		}

		return null;
	}

	private function get_atomic_setting( $widget, string $key ) {
		try {
			return $widget->get_atomic_setting( $key );
		} catch ( \Throwable $e ) {
			return null;
		}
	}

	private function normalize_legacy_settings( $widget ) {
		if ( ! method_exists( $widget, 'get_settings_for_display' ) ) {
			return null;
		}

		$settings   = $this->get_legacy_settings_from_widget( $widget );
		$normalized = $this->normalize_settings_array( $settings, false );

		if ( ! $normalized ) {
			return null;
		}

		return $this->apply_widget_defaults( $widget, $normalized );
	}

	private function get_legacy_settings_from_widget( $widget ): array {
		$prefix = Support::BACKGROUND_KEY;

		return [
			Support::ENABLE_KEY => $widget->get_settings_for_display( Support::ENABLE_KEY ),
			Support::SELECTOR_KEY => $widget->get_settings_for_display( Support::SELECTOR_KEY ),
			Support::BACKGROUND_KEY => $widget->get_settings_for_display( Support::BACKGROUND_KEY ),
			Support::BACKGROUND_START_COLOR_KEY => $widget->get_settings_for_display( Support::BACKGROUND_START_COLOR_KEY ),
			Support::BACKGROUND_END_COLOR_KEY => $widget->get_settings_for_display( Support::BACKGROUND_END_COLOR_KEY ),
			Support::BACKGROUND_START_COLOR_LOCATION_KEY => $widget->get_settings_for_display( Support::BACKGROUND_START_COLOR_LOCATION_KEY ),
			Support::BACKGROUND_END_COLOR_LOCATION_KEY => $widget->get_settings_for_display( Support::BACKGROUND_END_COLOR_LOCATION_KEY ),
			Support::BACKGROUND_ANGLE_KEY => $widget->get_settings_for_display( Support::BACKGROUND_ANGLE_KEY ),
			$prefix . '_background' => $widget->get_settings_for_display( $prefix . '_background' ),
			$prefix . '_color' => $widget->get_settings_for_display( $prefix . '_color' ),
			$prefix . '_color_b' => $widget->get_settings_for_display( $prefix . '_color_b' ),
			$prefix . '_gradient_angle' => $widget->get_settings_for_display( $prefix . '_gradient_angle' ),
			$prefix . '_color_stop' => $widget->get_settings_for_display( $prefix . '_color_stop' ),
			$prefix . '_color_b_stop' => $widget->get_settings_for_display( $prefix . '_color_b_stop' ),
			$prefix . '_gradient_type' => $widget->get_settings_for_display( $prefix . '_gradient_type' ),
			$prefix . '_gradient_position' => $widget->get_settings_for_display( $prefix . '_gradient_position' ),
		];
	}

	private function apply_widget_defaults( $widget, array $normalized ): array {
		if ( '' !== $normalized['selector'] || ! method_exists( $widget, 'get_name' ) ) {
			return $normalized;
		}

		if ( 'heading' === $widget->get_name() ) {
			$normalized['selector'] = '.elementor-heading-title';
		} elseif ( $this->is_atomic_heading_widget( $widget ) ) {
			$normalized['selector'] = '.e-heading-base';
		}

		return $normalized;
	}

	private function is_atomic_heading_widget( $widget ): bool {
		if ( ! method_exists( $widget, 'get_atomic_settings' ) ) {
			return false;
		}

		$element_type = method_exists( $widget, 'get_element_type' ) ? $widget::get_element_type() : '';

		return 'e-heading' === $element_type;
	}

	private function normalize_settings_array( array $settings, bool $unwrap_transformable ) {
		$active = $this->extract_value( $settings[ Support::ENABLE_KEY ] ?? null, $unwrap_transformable );

		if ( ! $this->is_truthy( $active ) ) {
			return null;
		}

		$background = $this->normalize_background( $settings, $unwrap_transformable );

		if ( '' === $background ) {
			return null;
		}

		return [
			'enable' => 'yes',
			'selector' => $this->string_value( $settings[ Support::SELECTOR_KEY ] ?? '', $unwrap_transformable, '' ),
			'background' => $background,
		];
	}

	private function normalize_background( array $settings, bool $unwrap_transformable ): string {
		$background = $this->string_value( $settings[ Support::BACKGROUND_KEY ] ?? '', $unwrap_transformable, '' );

		if ( '' !== $background ) {
			return $background;
		}

		if ( $this->has_legacy_group_settings( $settings, $unwrap_transformable ) ) {
			$legacy_background = $this->normalize_legacy_group_background( $settings, $unwrap_transformable );

			if ( '' !== $legacy_background ) {
				return $legacy_background;
			}
		}

		return $this->build_atomic_gradient( $settings, $unwrap_transformable );
	}

	private function has_legacy_group_settings( array $settings, bool $unwrap_transformable ): bool {
		$prefix = Support::BACKGROUND_KEY;

		return '' !== $this->string_value( $settings[ $prefix . '_background' ] ?? '', $unwrap_transformable, '' )
			|| '' !== $this->string_value( $settings[ $prefix . '_color' ] ?? '', $unwrap_transformable, '' )
			|| '' !== $this->string_value( $settings[ $prefix . '_color_b' ] ?? '', $unwrap_transformable, '' );
	}

	private function build_atomic_gradient( array $settings, bool $unwrap_transformable ): string {
		$start_color = $this->string_value( $settings[ Support::BACKGROUND_START_COLOR_KEY ] ?? '', $unwrap_transformable, '' );
		$end_color   = $this->string_value( $settings[ Support::BACKGROUND_END_COLOR_KEY ] ?? '', $unwrap_transformable, '' );
		$start_location = $this->float_value( $settings[ Support::BACKGROUND_START_COLOR_LOCATION_KEY ] ?? 0, $unwrap_transformable, 0 );
		$end_location   = $this->float_value( $settings[ Support::BACKGROUND_END_COLOR_LOCATION_KEY ] ?? 100, $unwrap_transformable, 100 );
		$angle          = $this->float_value( $settings[ Support::BACKGROUND_ANGLE_KEY ] ?? 90, $unwrap_transformable, 90 );

		if ( '' === $start_color ) {
			$start_color = '#08aeec';
		}

		if ( '' === $end_color ) {
			$end_color = '#2af598';
		}

		return sprintf(
			'linear-gradient(%sdeg, %s %s%%, %s %s%%)',
			$this->format_number( $angle ),
			$start_color,
			$this->format_number( $start_location ),
			$end_color,
			$this->format_number( $end_location )
		);
	}

	private function normalize_legacy_group_background( array $settings, bool $unwrap_transformable ): string {
		$prefix      = Support::BACKGROUND_KEY;
		$type        = $this->string_value( $settings[ $prefix . '_background' ] ?? '', $unwrap_transformable, '' );
		$start_color      = $this->string_value( $settings[ $prefix . '_color' ] ?? '', $unwrap_transformable, '' );
		$end_color        = $this->string_value( $settings[ $prefix . '_color_b' ] ?? '', $unwrap_transformable, '' );
		$start_location   = $this->float_value( $settings[ $prefix . '_color_stop' ] ?? 0, $unwrap_transformable, 0 );
		$end_location     = $this->float_value( $settings[ $prefix . '_color_b_stop' ] ?? 100, $unwrap_transformable, 100 );
		$angle            = $this->float_value( $settings[ $prefix . '_gradient_angle' ] ?? 90, $unwrap_transformable, 90 );

		if ( '' === $type ) {
			if ( '' !== $start_color && '' !== $end_color ) {
				$type = 'gradient';
			} elseif ( '' !== $start_color ) {
				$type = 'classic';
			} else {
				$type = 'gradient';
			}
		}

		if ( 'classic' === $type ) {
			if ( '' === $start_color ) {
				return '';
			}

			return sprintf(
				'linear-gradient(0deg, %s %s%%, %s %s%%)',
				$start_color,
				$this->format_number( $start_location ),
				$start_color,
				$this->format_number( $end_location )
			);
		}

		if ( 'gradient' !== $type ) {
			return '';
		}

		if ( '' === $start_color ) {
			$start_color = '#08aeec';
		}

		if ( '' === $end_color ) {
			$end_color = '#2af598';
		}

		return sprintf(
			'linear-gradient(%sdeg, %s %s%%, %s %s%%)',
			$this->format_number( $angle ),
			$start_color,
			$this->format_number( $start_location ),
			$end_color,
			$this->format_number( $end_location )
		);
	}

	private function format_number( float $value ): string {
		return rtrim( rtrim( sprintf( '%.4F', $value ), '0' ), '.' );
	}

	private function extract_value( $value, bool $unwrap_transformable ) {
		if ( $unwrap_transformable && is_array( $value ) && array_key_exists( 'value', $value ) ) {
			return $this->extract_value( $value['value'], $unwrap_transformable );
		}

		return $value;
	}

	private function is_truthy( $value ): bool {
		return true === $value || 'yes' === $value || 1 === $value || '1' === $value;
	}

	private function float_value( $value, bool $unwrap_transformable, float $fallback ): float {
		$value = $this->extract_value( $value, $unwrap_transformable );

		if ( is_array( $value ) ) {
			if ( isset( $value['size'] ) ) {
				$value = $value['size'];
			} elseif ( isset( $value['offset'] ) ) {
				$value = $value['offset'];
			}
		}

		return is_numeric( $value ) ? (float) $value : $fallback;
	}

	private function string_value( $value, bool $unwrap_transformable, string $fallback ): string {
		$value = $this->extract_value( $value, $unwrap_transformable );

		return is_string( $value ) && '' !== $value ? $value : $fallback;
	}
}
