<?php
if ( ! class_exists( 'NISHIKI_PRO_BLOCK_EDITOR_COLOR' ) ) {
	/**
	 * カラーパレット作成
	 */
	class NISHIKI_PRO_BLOCK_EDITOR_COLOR extends NISHIKI_PRO_BLOCK_EDITOR_UTILITY {

		/**
		 * Constructor.
		 */
		public function __construct() {
			// フォーム追加.
			add_action( 'admin_init', array( $this, 'register_settings' ), 20 );

			// Ajax追加
			add_action( 'admin_enqueue_scripts', array( $this, 'add_ajax' ), 10 );
			add_action( 'wp_ajax_nishiki-pro-add-color-action', array( $this, 'add_ajax_cb' ), 10 );
			add_action( 'wp_ajax_nishiki-pro-delete-color-action', array( $this, 'delete_ajax_cb' ), 10 );
			add_action( 'wp_ajax_nishiki-pro-reset-color-action', array( $this, 'reset_ajax_cb' ), 10 );
		}

		/**
		 * CSRF検証の統一関数
		 *
		 * @param string $action アクション名
		 * @param string $nonce_key nonceのキー名（通常は'nonce'）
		 * @return bool 検証結果
		 */
		private function verify_nonce_and_permission( $action, $nonce_key = 'nonce' ) {
			// 権限チェック
			if ( ! current_user_can( 'manage_options' ) ) {
				$this->send_error_response( '権限がありません。', 403 );
				return false;
			}

			// CSRF対策 - nonceチェック
			$nonce_value = isset( $_REQUEST[ $nonce_key ] ) ? sanitize_text_field( wp_unslash( $_REQUEST[ $nonce_key ] ) ) : '';
			
			if ( empty( $nonce_value ) ) {
				$this->send_error_response( 'セキュリティトークンが見つかりません。', 403 );
				return false;
			}

			if ( ! wp_verify_nonce( $nonce_value, $action ) ) {
				$this->send_error_response( 'セキュリティチェックに失敗しました。ページを再読み込みしてお試しください。', 403 );
				return false;
			}

			// Ajax処理でない場合はここで終了
			if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) {
				return true;
			}

			// Refererチェック（Ajax処理のみ）
			$referer = wp_get_referer();
			if ( ! $referer ) {
				// Refererが設定されていない場合は、HTTP_REFERERを確認
				$referer = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : '';
			}
			
			if ( ! empty( $referer ) && ! wp_validate_redirect( $referer, admin_url() ) ) {
				$this->send_error_response( 'リファラーが不正です。', 403 );
				return false;
			}

			// リクエストメソッドのチェック（Ajax処理のみ）
			if ( isset( $_SERVER['REQUEST_METHOD'] ) && ! in_array( $_SERVER['REQUEST_METHOD'], array( 'POST', 'GET' ), true ) ) {
				$this->send_error_response( '無効なリクエストメソッドです。', 405 );
				return false;
			}

			return true;
		}

		/**
		 * エラーレスポンスの送信
		 *
		 * @param string $message エラーメッセージ
		 * @param int    $status_code HTTPステータスコード
		 */
		private function send_error_response( $message, $status_code = 400 ) {
			status_header( $status_code );
			$data = wp_json_encode( array( 'error' => $message ) );
			header( 'Content-Type: application/json; charset=UTF-8' );
			echo wp_kses_post( $data );
			wp_die();
		}

		/**
		 * 成功レスポンスの送信
		 *
		 * @param mixed $data レスポンスデータ
		 */
		private function send_success_response( $data ) {
			$response = wp_json_encode( array( 'success' => true, 'data' => $data ) );
			header( 'Content-Type: application/json; charset=UTF-8' );
			echo wp_kses_post( $response );
			wp_die();
		}

		/**
		 * オプション名の検証
		 *
		 * @param string $option_name オプション名
		 * @return bool 検証結果
		 */
		private function validate_option_name( $option_name ) {
			if ( ! preg_match( '/^[a-zA-Z0-9_-]+$/', $option_name ) ) {
				$this->send_error_response( '無効なオプション名です。', 400 );
				return false;
			}
			return true;
		}

		/**
		 * 数値パラメータの安全な取得
		 *
		 * @param string $key パラメータのキー
		 * @param int    $min 最小値
		 * @param int    $max 最大値
		 * @return int|false 検証済みの数値またはfalse
		 */
		private function get_safe_numeric_param( $key, $min = 0, $max = 999 ) {
			// POSTとGETの両方をチェック
			$value = null;
			if ( isset( $_POST[ $key ] ) ) {
				$value = $_POST[ $key ];
			} elseif ( isset( $_GET[ $key ] ) ) {
				$value = $_GET[ $key ];
			}

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

			$value = absint( $value );
			
			if ( $value < $min || $value > $max ) {
				$this->send_error_response( "パラメータ「{$key}」の値が範囲外です。", 400 );
				return false;
			}

			return $value;
		}

		/**
		 * フォームの設定
		 */
		public function register_settings() {

			register_setting(
				$this->page_name . '_colors',
				$this->page_name . '_colors',
				array( $this, 'sanitize' )
			);

			add_settings_section(
				$this->page_name . '_colors',
				'',
				array( $this, 'cb' ),
				$this->page_name . '_colors'
			);
		}

		/**
		 * 出力（コールバック）
		 *
		 * @param array $args 設定
		 * @return void
		 */
		public function cb( $args ) {
			$option_name   = $this->page_name . '_colors';
			$this->options = get_option( $option_name );

			echo '<h3>カラーパレット設定</h3>';
			echo '<hr>';

			if ( ! empty( $this->options ) ) {
				$color_array = $this->options;
			} else {
				$color_array = NISHIKI_PRO_BLOCK_EDITOR_DEFAULT_COLORS;

				update_option( $option_name, $color_array );
			}

			echo '<span class="nishiki-pro-add-color button-secondary dashicons-before dashicons-plus">カラーを追加</span>';
			echo '<p>お好みでカラーを追加ください。他のカラーと区別するため、名前とクラス名を必ず入力してください。</p>';

			$this->create_form( $option_name, '', $color_array, 'カラー', 'color' );

			echo '<div style="border: 1px solid #aaa; padding: 1rem; border-radius: 4px;position: relative;margin-top: 2.5rem;">';
			echo '<h3 style="margin: 0;display: inline-block;position: absolute;top: -0.7rem;background: #f1f1f1;padding: 0 0.5rem;">※ クラス名の入力ルール</h3>';
			echo '<p><strong>1.利用可能文字</strong></p>';
			echo '<p>半角英数字（a-z,0-9）、ハイフン（-）のみ使えます。</p>';
			echo '<p><strong>2.英数字を混合して連続で使用しないでください。</strong></p>';
			echo '<p>NG：「v123」「1v23」「123v」</p>';
			echo '<p><strong>3.ハイフン(-) を 2 回以上連続で使用しないでください。</strong></p>';
			echo '<p>NG：「--123」「1--23」「12--3」「123--」</p>';
			echo '<p><strong>4.クラス名の先頭は数字または英字からはじめてください。</strong></p>';
			echo '<p><strong>5.同じクラス名は使用しないでください。</strong></p>';
			echo '</div>';

			submit_button();

			echo '<hr>';
			echo '<p><span class="nishiki-pro-reset-color button-secondary dashicons-before dashicons-undo">カラーを初期設定に戻す（リセット）</span></p>';
		}

		/**
		 * フォーム作成
		 *
		 * @param string $option_name 名前
		 * @param string $section セクション
		 * @param array  $data_array 配列
		 * @param string $title タイトル名
		 * @param string $label ラベル名
		 * @return void
		 */
		public function create_form( $option_name, $section, $data_array, $title, $label ) {
			$name = $option_name;
			$num  = 0;

			echo '<ul id="nishiki-pro-color-palette-wrap" class="nishiki-pro-color-palette-wrap">';
			foreach ( $data_array as $data ) {
				echo '<li>';
				echo '<span class="num">' . ( absint( $num ) + 1 ) . '</span><label for="' . esc_attr( 'color_' . $num . '_code' ) . '"><input class="nishiki-pro-color-picker" type="text" id="' . esc_attr( 'color_' . $num . '_code' ) . '" name="' . esc_attr( $name ) . '[' . absint( $num ) . '][color]" value="' . esc_attr( $data['color'] ) . '"></label>';
				echo '<label for="' . esc_attr( 'color_' . $num . '_name' ) . '">名前：<input type="text" id="' . esc_attr( 'color_' . $num . '_name' ) . '" name="' . esc_attr( $name ) . '[' . absint( $num ) . '][name]" value="' . esc_attr( $data['name'] ) . '"></label>';
				echo '<label for="' . esc_attr( 'color_' . $num . '_slug' ) . '">クラス名：<input type="text" id="' . esc_attr( 'color_' . $num . '_slug' ) . '" name="' . esc_attr( $name ) . '[' . absint( $num ) . '][slug]" value="' . esc_attr( $data['slug'] ) . '" pattern="^[a-z0-9-_]+$"></label>';
				echo '<span class="nishiki-pro-delete-color button-secondary" data-color-num="' . absint( $num ) . '">削除</span>';
				echo '</li>';

				++$num;
			}
			echo '</ul>';
		}

		/**
		 * 入力値のサニタイズ
		 *
		 * @param array $input Contains all settings fields as array keys
		 */
		public function sanitize( $input ) {
			$new_input = array();

			// Color
			$color_num = 0;
			if ( isset( $input ) ) {
				foreach ( $input as $color ) {
					// 配列のサニタイズ
					foreach ( $color as $key => $val ) {
						$new_input[ $color_num ][ $key ] = sanitize_text_field( $val );
					}

					++$color_num;
				}
			}

			return $new_input;
		}

		/**
		 * Ajax定義
		 */
		public function add_ajax() {
			$add_handle = 'nishiki-pro-add-color-script';
			wp_enqueue_script(
				$add_handle,
				get_template_directory_uri() . '/extension/class/block-editor/js/color.js',
				array( 'jquery' ),
				'1.0.0',
				false
			);

			// Add
			$add_action = 'nishiki-pro-add-color-action';
			wp_localize_script(
				$add_handle,
				'nishiki_pro_add_color_ajax',
				array(
					'ajax_url' => admin_url( 'admin-ajax.php' ),
					'action'   => $add_action,
					'nonce'    => wp_create_nonce( $add_action ),
				)
			);

			// Delete
			$delete_action = 'nishiki-pro-delete-color-action';
			wp_localize_script(
				$add_handle,
				'nishiki_pro_delete_color_ajax',
				array(
					'ajax_url' => admin_url( 'admin-ajax.php' ),
					'action'   => $delete_action,
					'nonce'    => wp_create_nonce( $delete_action ),
				)
			);

			// Reset
			$reset_action = 'nishiki-pro-reset-color-action';
			wp_localize_script(
				$add_handle,
				'nishiki_pro_reset_color_ajax',
				array(
					'ajax_url' => admin_url( 'admin-ajax.php' ),
					'action'   => $reset_action,
					'nonce'    => wp_create_nonce( $reset_action ),
				)
			);
		}

		/**
		 * Add Ajax処理（CSRF強化版）
		 *
		 * @return void
		 */
		public function add_ajax_cb() {
			$action = 'nishiki-pro-add-color-action';

			// 統一されたCSRF検証
			if ( ! $this->verify_nonce_and_permission( $action ) ) {
				return;
			}

			// オプション名の検証とサニタイズ
			$option_name = sanitize_text_field( $this->page_name . '_colors' );
			if ( ! $this->validate_option_name( $option_name ) ) {
				return;
			}

			$value = get_option( $option_name, array() );

			if ( ! is_array( $value ) ) {
				$value = array();
			}

			// データサイズのチェック（DoS対策）
			if ( count( $value ) >= 50 ) {
				$this->send_error_response( 'カラーの数が上限に達しています。', 400 );
				return;
			}

			// 新しいカラーデータを追加
			$new_color = array(
				'name'  => sanitize_text_field( 'white' ),
				'color' => sanitize_hex_color( '#ffffff' ),
				'slug'  => sanitize_key( 'white' ),
			);

			array_push( $value, $new_color );

			$update_result = update_option( $option_name, $value );

			if ( false === $update_result ) {
				$this->send_error_response( 'データ保存に失敗しました。', 500 );
				return;
			}

			$this->send_success_response( $value );
		}

		/**
		 * Delete Ajax処理（CSRF強化版）
		 *
		 * @return void
		 */
		public function delete_ajax_cb() {
			$action = 'nishiki-pro-delete-color-action';

			// 統一されたCSRF検証
			if ( ! $this->verify_nonce_and_permission( $action ) ) {
				return;
			}

			// 数値パラメータの安全な取得
			$num = $this->get_safe_numeric_param( 'num', 0, 999 );
			if ( false === $num ) {
				$this->send_error_response( '削除対象が指定されていないか、値が不正です。', 400 );
				return;
			}

			// オプション名の検証
			$option_name = sanitize_text_field( $this->page_name . '_colors' );
			if ( ! $this->validate_option_name( $option_name ) ) {
				return;
			}

			$value = get_option( $option_name, array() );

			if ( ! is_array( $value ) || ! isset( $value[ $num ] ) ) {
				$this->send_error_response( '削除対象が見つかりません。', 404 );
				return;
			}

			unset( $value[ $num ] );
			$value = array_values( $value );

			$update_result = update_option( $option_name, $value );

			if ( false === $update_result ) {
				$this->send_error_response( 'データ削除に失敗しました。', 500 );
				return;
			}

			$this->send_success_response( $value );
		}

		/**
		 * Reset Ajax処理（CSRF強化版）
		 *
		 * @return void
		 */
		public function reset_ajax_cb() {
			$action = 'nishiki-pro-reset-color-action';

			// 統一されたCSRF検証
			if ( ! $this->verify_nonce_and_permission( $action ) ) {
				return;
			}

			// オプション名の検証
			$option_name = sanitize_text_field( $this->page_name . '_colors' );
			if ( ! $this->validate_option_name( $option_name ) ) {
				return;
			}

			// デフォルトカラーが定義されているかチェック
			if ( ! defined( 'NISHIKI_PRO_BLOCK_EDITOR_DEFAULT_COLORS' ) ) {
				$this->send_error_response( 'デフォルトカラーが定義されていません。', 500 );
				return;
			}

			$default_colors = NISHIKI_PRO_BLOCK_EDITOR_DEFAULT_COLORS;

			if ( ! is_array( $default_colors ) ) {
				$this->send_error_response( 'デフォルトカラーの形式が不正です。', 500 );
				return;
			}

			delete_option( $option_name );
			$update_result = update_option( $option_name, $default_colors );

			if ( false === $update_result ) {
				$this->send_error_response( 'データリセットに失敗しました。', 500 );
				return;
			}

			$this->send_success_response( $default_colors );
		}
	}

	if ( is_admin() ) {
		$output = new NISHIKI_PRO_BLOCK_EDITOR_COLOR();
	}
}