<?php
/**
 * REST API: get/save global widget styles.
 *
 * @package PRECO
 */

namespace PRECO\Includes\Admin\Rest;

use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

defined( 'ABSPATH' ) || exit;

/**
 * Global Styles REST controller.
 *
 * Handles:
 *  GET  /preco/v1/global-styles — read the saved global widget style.
 *  POST /preco/v1/global-styles — save the global widget style.
 */
class GlobalStylesRest {

	/**
	 * Option name where the global style is stored.
	 */
	const OPTION_NAME = 'preco_global_styles';

	/**
	 * REST namespace.
	 */
	const PRECO_REST_NAMESPACE = 'preco/v1';

	/**
	 * Allowed button style values.
	 *
	 * @var array<int,string>
	 */
	const BUTTON_STYLES = array( 'sharp', 'rounded', 'pill' );

	/**
	 * Allowed border radius values.
	 *
	 * @var array<int,string>
	 */
	const BORDER_RADII = array( 'none', 'rounded' );

	/**
	 * Color theme presets. Every preset defines the 6 user-facing colors plus
	 * button_text_color — the contrast color for button_bg_color. There is no
	 * input for button_text_color; the admin JS (re)computes it whenever
	 * button_bg_color changes and sends the result along with the rest of
	 * the payload.
	 *
	 * @var array<string,array<string,string>>
	 */
	const COLOR_PRESETS = array(
		'classic' => array(
			'button_bg_color'   => '#0A0A0A',
			'button_text_color' => '#FFFFFF',
			'rating_color'      => '#A3A3A3',
			'title_color'       => '#262626',
			'cat_color'         => '#737373',
			'border_color'      => '#CECECE',
			'background_color'  => '#FFFFFF00',
		),
		'blue'    => array(
			'button_bg_color'   => '#0049BE',
			'button_text_color' => '#FFFFFF',
			'rating_color'      => '#A3A3A3',
			'title_color'       => '#010D28',
			'cat_color'         => '#0045DA',
			'border_color'      => '#D0DBF3',
			'background_color'  => '#FFFFFF00',
		),
		'green'   => array(
			'button_bg_color'   => '#218146',
			'button_text_color' => '#FFFFFF',
			'rating_color'      => '#A3A3A3',
			'title_color'       => '#010D28',
			'cat_color'         => '#1AB75B',
			'border_color'      => '#D0F3DE',
			'background_color'  => '#FFFFFF00',
		),
	);

	/**
	 * Constructor.
	 */
	public function __construct() {
		add_action( 'rest_api_init', array( $this, 'register_routes' ) );
	}

	/**
	 * Register routes.
	 *
	 * @return void
	 */
	public function register_routes() {
		register_rest_route(
			self::PRECO_REST_NAMESPACE,
			'/global-styles',
			array(
				array(
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_global_styles' ),
					'permission_callback' => array( $this, 'permission_check' ),
				),
				array(
					'methods'             => WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'save_global_styles' ),
					'permission_callback' => array( $this, 'permission_check' ),
					'args'                => array(
						'styles' => array(
							'required' => true,
						),
					),
				),
			)
		);
	}

	/**
	 * Permission check.
	 *
	 * @return bool
	 */
	public function permission_check() {
		return current_user_can( 'manage_woocommerce' ) || current_user_can( 'manage_options' );
	}

	/**
	 * GET /preco/v1/global-styles
	 *
	 * @param WP_REST_Request $request Request.
	 * @return WP_REST_Response
	 */
	public function get_global_styles( WP_REST_Request $request ) {
		if ( ! wp_verify_nonce( $request->get_header( 'X-WP-Nonce' ), 'wp_rest' ) ) {
			return new WP_REST_Response( array( 'message' => 'Invalid nonce.' ), 403 );
		}

		$stored = get_option( self::OPTION_NAME, null );
		$styles = $this->sanitize_styles( is_array( $stored ) ? $stored : array() );

		return new WP_REST_Response(
			array(
				'styles' => $styles,
			),
			200
		);
	}

	/**
	 * POST /preco/v1/global-styles
	 *
	 * @param WP_REST_Request $request Request.
	 * @return WP_REST_Response
	 */
	public function save_global_styles( WP_REST_Request $request ) {
		if ( ! wp_verify_nonce( $request->get_header( 'X-WP-Nonce' ), 'wp_rest' ) ) {
			return new WP_REST_Response( array( 'message' => 'Invalid nonce.' ), 403 );
		}

		$raw = $request->get_param( 'styles' );
		if ( ! is_array( $raw ) ) {
			return new WP_REST_Response( array( 'message' => 'Invalid styles payload.' ), 400 );
		}

		$styles  = $this->sanitize_styles( $raw );
		$updated = update_option( self::OPTION_NAME, $styles, false );

		return new WP_REST_Response(
			array(
				'updated' => (bool) $updated,
				'styles'  => $styles,
			),
			200
		);
	}

	/**
	 * Sanitize and normalize a raw styles payload, filling in defaults for
	 * anything missing/invalid and always (re)computing button_text_color
	 * from button_bg_color.
	 *
	 * @param array $raw Raw input (from get_option or the REST request).
	 * @return array
	 */
	private function sanitize_styles( array $raw ) {
		$defaults = self::COLOR_PRESETS['classic'];

		$color_theme = isset( $raw['color_theme'] ) && is_string( $raw['color_theme'] )
			? sanitize_key( $raw['color_theme'] )
			: 'classic';
		if ( ! isset( self::COLOR_PRESETS[ $color_theme ] ) && 'custom' !== $color_theme ) {
			$color_theme = 'classic';
		}

		$color_keys = array(
			'button_bg_color',
			'button_text_color',
			'rating_color',
			'title_color',
			'cat_color',
			'border_color',
			'background_color',
		);

		$styles = array( 'color_theme' => $color_theme );
		foreach ( $color_keys as $key ) {
			$value          = isset( $raw[ $key ] ) ? self::sanitize_hex_color_alpha( $raw[ $key ] ) : null;
			$styles[ $key ] = $value ? $value : $defaults[ $key ];
		}

		$button_style           = isset( $raw['button_style'] ) ? sanitize_key( $raw['button_style'] ) : 'rounded';
		$styles['button_style'] = in_array( $button_style, self::BUTTON_STYLES, true ) ? $button_style : 'rounded';

		$border_radius           = isset( $raw['border_radius'] ) ? sanitize_key( $raw['border_radius'] ) : 'rounded';
		$styles['border_radius'] = in_array( $border_radius, self::BORDER_RADII, true ) ? $border_radius : 'rounded';

		return $styles;
	}

	/**
	 * Sanitize a hex color that may carry an alpha channel.
	 *
	 * Accepts #RGB, #RGBA, #RRGGBB and #RRGGBBAA. Falls back to
	 * sanitize_hex_color() for the alpha-less forms so behaviour there is
	 * unchanged.
	 *
	 * @param mixed $color Raw color value.
	 * @return string|null Sanitized color, or null when invalid.
	 */
	public static function sanitize_hex_color_alpha( $color ) {
		if ( ! is_string( $color ) ) {
			return null;
		}

		$color = trim( $color );
		if ( '' === $color ) {
			return null;
		}

		if ( preg_match( '/^#(?:[A-Fa-f0-9]{4}|[A-Fa-f0-9]{8})$/', $color ) ) {
			return $color;
		}

		$sanitized = sanitize_hex_color( $color );

		return $sanitized ? $sanitized : null;
	}
}
