<?php
/**
 * Block Data REST API Controller.
 *
 * @package Ugcify\Admin\API
 */

namespace Ugcify\Admin\API;

use WP_Error;
use WP_REST_Controller;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

/**
 * Class BlockDataAPI
 */
class BlockDataAPI extends WP_REST_Controller {
	/**
	 * Namespace.
	 *
	 * @var string
	 */
	protected $namespace = 'ugcify/v1';

	/**
	 * Rest base.
	 *
	 * @var string
	 */
	protected $rest_base = 'block-data';

	/**
	 * Register routes.
	 *
	 * @return void
	 */
	public function register_routes() {
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/products',
			array(
				array(
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_products' ),
					'permission_callback' => array( $this, 'check_permission' ),
				),
			)
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/terms',
			array(
				array(
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_terms' ),
					'permission_callback' => array( $this, 'check_permission' ),
				),
			)
		);
	}

	/**
	 * Check permission.
	 *
	 * @return bool|WP_Error
	 */
	public function check_permission() {
		if ( ! current_user_can( 'edit_posts' ) ) {
			return new WP_Error(
				'rest_forbidden',
				__( 'Sorry, you are not allowed to access this resource.', 'ugcify' ),
				array( 'status' => 403 )
			);
		}

		return true;
	}

	/**
	 * Get products for block editor.
	 *
	 * @param WP_REST_Request $request Request object.
	 * @return WP_REST_Response|WP_Error
	 */
	public function get_products( $request ) {
		if ( ! function_exists( 'wc_get_products' ) || ! function_exists( 'wc_get_product' ) ) {
			return new WP_Error(
				'woocommerce_required',
				__( 'WooCommerce is required for this endpoint.', 'ugcify' ),
				array( 'status' => 400 )
			);
		}

		$search_raw = $request->get_param( 'q' );
		if ( null === $search_raw ) {
			$search_raw = $request->get_param( 'term' );
		}

		$query      = sanitize_text_field( (string) $search_raw );
		$limit      = max( 1, min( 20, absint( $request->get_param( 'limit' ) ) ) );
		$product_id = absint( $request->get_param( 'product_id' ) );
		$is_select2 = 'select2' === sanitize_key( (string) $request->get_param( 'format' ) );
		$items      = array();

		if ( $product_id > 0 ) {
			$product = wc_get_product( $product_id );
			if ( $product ) {
				$items[] = $this->map_product( $product );
			}

			return new WP_REST_Response( $this->format_products_response( $items, $is_select2 ), 200 );
		}

		$args = array(
			'status' => array( 'publish' ),
			'limit'  => $limit,
			'return' => 'objects',
			's'      => $query,
		);

		$products = wc_get_products( $args );

		foreach ( $products as $product ) {
			if ( ! $product ) {
				continue;
			}

			$items[] = $this->map_product( $product );
		}

		return new WP_REST_Response( $this->format_products_response( $items, $is_select2 ), 200 );
	}

	/**
	 * Get terms for block editor.
	 *
	 * @param WP_REST_Request $request Request object.
	 * @return WP_REST_Response|WP_Error
	 */
	public function get_terms( $request ) {
		$taxonomy = sanitize_key( (string) $request->get_param( 'taxonomy' ) );
		$search_raw = $request->get_param( 'q' );
		if ( null === $search_raw ) {
			$search_raw = $request->get_param( 'term' );
		}
		$query    = sanitize_text_field( (string) $search_raw );
		$limit    = max( 1, min( 20, absint( $request->get_param( 'limit' ) ) ) );
		$is_select2 = 'select2' === sanitize_key( (string) $request->get_param( 'format' ) );

		if ( ! in_array( $taxonomy, array( 'product_cat', 'product_tag' ), true ) ) {
			return new WP_Error(
				'invalid_taxonomy',
				__( 'Invalid taxonomy.', 'ugcify' ),
				array( 'status' => 400 )
			);
		}

		$args = array(
			'taxonomy'   => $taxonomy,
			'hide_empty' => false,
			'number'     => $limit,
			'orderby'    => 'name',
			'order'      => 'ASC',
		);

		if ( '' !== $query ) {
			$args['name__like'] = $query;
		}

		$terms = get_terms( $args );

		if ( is_wp_error( $terms ) ) {
			return new WP_Error(
				'terms_fetch_failed',
				__( 'Unable to fetch terms.', 'ugcify' ),
				array( 'status' => 500 )
			);
		}

		$items = array_map(
			static function ( $term ) {
				return array(
					'id'   => (int) $term->term_id,
					'name' => (string) $term->name,
					'slug' => (string) $term->slug,
				);
			},
			$terms
		);

		if ( $is_select2 ) {
			$select2_items = array_map(
				static function ( $item ) {
					return array(
						'id'   => (string) $item['slug'],
						'text' => (string) $item['name'],
					);
				},
				$items
			);

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

		return new WP_REST_Response(
			array(
				'success' => true,
				'data'    => $items,
			),
			200
		);
	}

	/**
	 * Map WooCommerce product object to response payload.
	 *
	 * @param \WC_Product $product Product object.
	 * @return array
	 */
	private function map_product( $product ) {
		$product_id = $product->get_id();

		return array(
			'id'    => (int) $product_id,
			'title' => (string) $product->get_name(),
		);
	}

	/**
	 * Format products response payload.
	 *
	 * @param array $items      Product items.
	 * @param bool  $is_select2 Select2 format.
	 * @return array
	 */
	private function format_products_response( $items, $is_select2 ) {
		if ( $is_select2 ) {
			$select2_items = array_map(
				static function ( $item ) {
					return array(
						'id'   => (string) $item['id'],
						'text' => (string) $item['title'],
					);
				},
				$items
			);

			return array(
				'results' => $select2_items,
			);
		}

		return array(
			'success' => true,
			'data'    => $items,
		);
	}
}
