<?php
declare(strict_types=1);

namespace forge12\floating_menu;

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

use forge12\floating_menu\component\floatingmenu\FloatingMenu;
use WP_Error;
use WP_Post;
use WP_REST_Controller;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

// Ensure UISettings is available (not auto-loaded outside admin context).
if ( ! class_exists( UISettings::class ) ) {
	require_once plugin_dir_path( __DIR__ ) . 'ui/UISettings.class.php';
}

// sanitize_hex_color() is only loaded with the Customizer by default.
if ( ! function_exists( 'sanitize_hex_color' ) ) {
	/**
	 * Sanitize a hex color value.
	 *
	 * @param string $color Hex color with or without hash.
	 * @return string|null Sanitized hex or null.
	 */
	function sanitize_hex_color( string $color ): ?string {
		if ( '' === $color ) {
			return '';
		}
		if ( preg_match( '/^#([A-Fa-f0-9]{3}){1,2}$/', $color ) ) {
			return $color;
		}
		return null;
	}
}

/**
 * REST API Controller for the F12 Floating Menu plugin.
 *
 * Provides endpoints for managing floating menus, menu items,
 * global settings, page search, and post type listing.
 *
 * @since 1.4.0
 */
class REST_API extends WP_REST_Controller {

	/**
	 * @var string
	 */
	protected $namespace = 'f12-floating-menu/v1';

	/**
	 * Position mapping from WordPress (PHP) to React frontend.
	 */
	private const POSITION_TO_REACT = [
		'bottomright' => 'lower-right',
		'upperright'  => 'upper-right',
		'upperleft'   => 'upper-left',
		'lowerleft'   => 'lower-left',
		'left'        => 'left',
		'right'       => 'right',
		'top'         => 'top',
		'bottom'      => 'bottom',
		'hidden'      => 'hidden',
		''            => 'hidden',
	];

	/**
	 * Position mapping from React frontend to WordPress (PHP).
	 */
	private const POSITION_FROM_REACT = [
		'lower-right' => 'bottomright',
		'upper-right' => 'upperright',
		'upper-left'  => 'upperleft',
		'lower-left'  => 'lowerleft',
		'left'        => 'left',
		'right'       => 'right',
		'top'         => 'top',
		'bottom'      => 'bottom',
		'hidden'      => 'hidden',
	];

	/**
	 * Allowed values for select fields.
	 */
	private const ALLOWED_POSITIONS          = [ 'hidden', 'left', 'top', 'right', 'bottom', 'lower-right', 'upper-right', 'upper-left', 'lower-left' ];
	private const ALLOWED_RESPONSIVE         = [ 'default', 'visible', 'hidden' ];
	private const ALLOWED_ANIMATION          = [ 'default', 'enabled', 'disabled' ];
	private const ALLOWED_DISPLAY            = [ 'default', 'full', 'icon' ];
	private const ALLOWED_GLOBAL_RESPONSIVE  = [ 'visible', 'hidden' ];
	private const ALLOWED_GLOBAL_ANIMATION   = [ 'enabled', 'disabled' ];
	private const ALLOWED_GLOBAL_DISPLAY     = [ 'full', 'icon' ];

	/**
	 * Register all REST routes.
	 */
	public function register_routes(): void {
		// Menus collection.
		register_rest_route( $this->namespace, '/menus', [
			[
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => [ $this, 'get_menus' ],
				'permission_callback' => [ $this, 'check_admin_permission' ],
			],
			[
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'create_menu' ],
				'permission_callback' => [ $this, 'check_admin_permission' ],
			],
		] );

		// Single menu.
		register_rest_route( $this->namespace, '/menus/(?P<id>\d+)', [
			[
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => [ $this, 'get_menu' ],
				'permission_callback' => [ $this, 'check_admin_permission' ],
			],
			[
				'methods'             => WP_REST_Server::EDITABLE,
				'callback'            => [ $this, 'update_menu' ],
				'permission_callback' => [ $this, 'check_admin_permission' ],
			],
			[
				'methods'             => WP_REST_Server::DELETABLE,
				'callback'            => [ $this, 'delete_menu' ],
				'permission_callback' => [ $this, 'check_admin_permission' ],
			],
		] );

		// Duplicate menu.
		register_rest_route( $this->namespace, '/menus/(?P<id>\d+)/duplicate', [
			[
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'duplicate_menu' ],
				'permission_callback' => [ $this, 'check_admin_permission' ],
			],
		] );

		// Menu items.
		register_rest_route( $this->namespace, '/menus/(?P<id>\d+)/items', [
			[
				'methods'             => WP_REST_Server::EDITABLE,
				'callback'            => [ $this, 'save_items' ],
				'permission_callback' => [ $this, 'check_admin_permission' ],
			],
		] );

		// Menu settings.
		register_rest_route( $this->namespace, '/menus/(?P<id>\d+)/settings', [
			[
				'methods'             => WP_REST_Server::EDITABLE,
				'callback'            => [ $this, 'save_settings' ],
				'permission_callback' => [ $this, 'check_admin_permission' ],
			],
		] );

		// Global settings.
		register_rest_route( $this->namespace, '/settings', [
			[
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => [ $this, 'get_global_settings' ],
				'permission_callback' => [ $this, 'check_admin_permission' ],
			],
			[
				'methods'             => WP_REST_Server::EDITABLE,
				'callback'            => [ $this, 'save_global_settings' ],
				'permission_callback' => [ $this, 'check_admin_permission' ],
			],
		] );

		// Page search.
		register_rest_route( $this->namespace, '/pages', [
			[
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => [ $this, 'search_pages' ],
				'permission_callback' => [ $this, 'check_admin_permission' ],
			],
		] );

		// Post types.
		register_rest_route( $this->namespace, '/post-types', [
			[
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => [ $this, 'get_post_types' ],
				'permission_callback' => [ $this, 'check_admin_permission' ],
			],
		] );

		// Status / health check.
		register_rest_route( $this->namespace, '/status', [
			[
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => [ $this, 'get_status' ],
				'permission_callback' => [ $this, 'check_admin_permission' ],
			],
		] );

		// Repair icons.
		register_rest_route( $this->namespace, '/repair/icons', [
			[
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'repair_icons' ],
				'permission_callback' => [ $this, 'check_admin_permission' ],
			],
		] );

		// WPML languages.
		register_rest_route( $this->namespace, '/wpml/languages', [
			[
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => [ $this, 'get_wpml_languages' ],
				'permission_callback' => [ $this, 'check_admin_permission' ],
			],
		] );
	}

	/**
	 * Permission check: current user must have manage_options capability.
	 */
	public function check_admin_permission(): bool {
		return current_user_can( 'manage_options' );
	}

	// -------------------------------------------------------------------------
	// Menu CRUD
	// -------------------------------------------------------------------------

	/**
	 * GET /menus — List all floating menus.
	 */
	public function get_menus( WP_REST_Request $request ): WP_REST_Response {
		$posts = get_posts( [
			'post_type'        => 'floating_menu',
			'numberposts'      => -1,
			'suppress_filters' => false,
			'post_status'      => [ 'publish', 'draft' ],
		] );

		$menus = [];
		foreach ( $posts as $post ) {
			$fm    = new FloatingMenu( $post );
			$items = $fm->getLinks();

			$menus[] = [
				'id'           => (string) $post->ID,
				'name'         => $post->post_title,
				'status'       => 'publish' === $post->post_status ? 'published' : 'draft',
				'position'     => self::POSITION_TO_REACT[ $fm->getSettings()['position'] ] ?? 'hidden',
				'itemCount'    => count( $items ),
				'wpmlLanguage' => $fm->getSettings()['wpml_language'] ?? '',
			];
		}

		return new WP_REST_Response( $menus, 200 );
	}

	/**
	 * GET /menus/{id} — Get a single menu with items and settings.
	 */
	public function get_menu( WP_REST_Request $request ): WP_REST_Response|WP_Error {
		$post = get_post( absint( $request['id'] ) );

		if ( ! $post || 'floating_menu' !== $post->post_type ) {
			return new WP_Error( 'not_found', __( 'Menu not found.', 'f12-floating-menu' ), [ 'status' => 404 ] );
		}

		return new WP_REST_Response( $this->transform_menu_to_response( $post ), 200 );
	}

	/**
	 * POST /menus — Create a new menu.
	 */
	public function create_menu( WP_REST_Request $request ): WP_REST_Response|WP_Error {
		$params = $request->get_json_params();
		$name   = sanitize_text_field( $params['name'] ?? __( 'New Menu', 'f12-floating-menu' ) );

		$post_id = wp_insert_post( [
			'post_type'   => 'floating_menu',
			'post_title'  => $name,
			'post_status' => 'draft',
		], true );

		if ( is_wp_error( $post_id ) ) {
			return $post_id;
		}

		$fm = new FloatingMenu( get_post( $post_id ) );
		update_post_meta( $post_id, '_floating_settings', $fm->getDefaultSettings() );
		update_post_meta( $post_id, '_floating_links', [] );

		return new WP_REST_Response( $this->transform_menu_to_response( get_post( $post_id ) ), 201 );
	}

	/**
	 * PUT /menus/{id} — Update menu name and/or status.
	 */
	public function update_menu( WP_REST_Request $request ): WP_REST_Response|WP_Error {
		$post_id = absint( $request['id'] );
		$post    = get_post( $post_id );

		if ( ! $post || 'floating_menu' !== $post->post_type ) {
			return new WP_Error( 'not_found', __( 'Menu not found.', 'f12-floating-menu' ), [ 'status' => 404 ] );
		}

		$params = $request->get_json_params();
		$update = [ 'ID' => $post_id ];

		if ( isset( $params['name'] ) ) {
			$update['post_title'] = sanitize_text_field( $params['name'] );
		}

		if ( isset( $params['status'] ) ) {
			$update['post_status'] = 'published' === $params['status'] ? 'publish' : 'draft';
		}

		$result = wp_update_post( $update, true );

		if ( is_wp_error( $result ) ) {
			return $result;
		}

		return new WP_REST_Response( $this->transform_menu_to_response( get_post( $post_id ) ), 200 );
	}

	/**
	 * DELETE /menus/{id} — Delete a menu.
	 */
	public function delete_menu( WP_REST_Request $request ): WP_REST_Response|WP_Error {
		$post_id = absint( $request['id'] );
		$post    = get_post( $post_id );

		if ( ! $post || 'floating_menu' !== $post->post_type ) {
			return new WP_Error( 'not_found', __( 'Menu not found.', 'f12-floating-menu' ), [ 'status' => 404 ] );
		}

		wp_delete_post( $post_id, true );

		return new WP_REST_Response( [ 'deleted' => true ], 200 );
	}

	/**
	 * POST /menus/{id}/duplicate — Duplicate a menu.
	 */
	public function duplicate_menu( WP_REST_Request $request ): WP_REST_Response|WP_Error {
		$post_id = absint( $request['id'] );
		$post    = get_post( $post_id );

		if ( ! $post || 'floating_menu' !== $post->post_type ) {
			return new WP_Error( 'not_found', __( 'Menu not found.', 'f12-floating-menu' ), [ 'status' => 404 ] );
		}

		/* translators: %s: Original menu name */
		$new_title = sprintf( __( '%s (Copy)', 'f12-floating-menu' ), $post->post_title );

		$new_id = wp_insert_post( [
			'post_type'   => 'floating_menu',
			'post_title'  => $new_title,
			'post_status' => 'draft',
		], true );

		if ( is_wp_error( $new_id ) ) {
			return $new_id;
		}

		$links    = get_post_meta( $post_id, '_floating_links', true );
		$settings = get_post_meta( $post_id, '_floating_settings', true );

		update_post_meta( $new_id, '_floating_links', is_array( $links ) ? $links : [] );
		update_post_meta( $new_id, '_floating_settings', is_array( $settings ) ? $settings : [] );

		return new WP_REST_Response( $this->transform_menu_to_response( get_post( $new_id ) ), 201 );
	}

	// -------------------------------------------------------------------------
	// Menu Items
	// -------------------------------------------------------------------------

	/**
	 * PUT /menus/{id}/items — Save all menu items.
	 */
	public function save_items( WP_REST_Request $request ): WP_REST_Response|WP_Error {
		$post_id = absint( $request['id'] );
		$post    = get_post( $post_id );

		if ( ! $post || 'floating_menu' !== $post->post_type ) {
			return new WP_Error( 'not_found', __( 'Menu not found.', 'f12-floating-menu' ), [ 'status' => 404 ] );
		}

		$react_items = $request->get_json_params();

		if ( ! is_array( $react_items ) ) {
			return new WP_Error( 'invalid_data', __( 'Items must be an array.', 'f12-floating-menu' ), [ 'status' => 400 ] );
		}

		$php_items = $this->transform_items_from_request( $react_items );
		update_post_meta( $post_id, '_floating_links', $php_items );

		// Sync theme mods.
		set_theme_mod( $post->post_name . '_floating_links', '' );

		return new WP_REST_Response( $this->transform_menu_to_response( get_post( $post_id ) ), 200 );
	}

	// -------------------------------------------------------------------------
	// Menu Settings
	// -------------------------------------------------------------------------

	/**
	 * PUT /menus/{id}/settings — Save menu settings.
	 */
	public function save_settings( WP_REST_Request $request ): WP_REST_Response|WP_Error {
		$post_id = absint( $request['id'] );
		$post    = get_post( $post_id );

		if ( ! $post || 'floating_menu' !== $post->post_type ) {
			return new WP_Error( 'not_found', __( 'Menu not found.', 'f12-floating-menu' ), [ 'status' => 404 ] );
		}

		$react_settings = $request->get_json_params();
		$php_settings   = $this->transform_settings_from_request( $react_settings );

		update_post_meta( $post_id, '_floating_settings', $php_settings );

		// Sync theme mods for customizer compatibility.
		foreach ( $php_settings as $key => $value ) {
			if ( 'pages_post_type' === $key ) {
				continue;
			}
			set_theme_mod( $post->post_name . '_' . $key, $value );
		}

		return new WP_REST_Response( $this->transform_menu_to_response( get_post( $post_id ) ), 200 );
	}

	// -------------------------------------------------------------------------
	// Global Settings
	// -------------------------------------------------------------------------

	/**
	 * GET /settings — Get global plugin settings.
	 */
	public function get_global_settings( WP_REST_Request $request ): WP_REST_Response {
		$settings = UISettings::getOptions( true );

		return new WP_REST_Response( [
			'fontSize'           => (int) $settings['font_size'],
			'iconSize'           => (int) $settings['attachment_size'],
			'iconPadding'        => (int) $settings['icon_padding'],
			'bgColor'            => $settings['background_color'],
			'bgColorHover'       => $settings['background_color_hover'],
			'linkColor'          => $settings['link_color'],
			'linkColorHover'     => $settings['link_color_hover'],
			'animationDistance'   => $settings['animation_distance'],
			'animationSlideout'  => $settings['animation_slideout'],
			'displaySettings'    => $settings['display_settings'],
			'responsiveDesktop'  => $settings['display_responsive_desktop'],
			'responsiveTablet'   => $settings['display_responsive_tablet'],
			'responsiveMobile'   => $settings['display_responsive_mobile'],
			'support'            => (bool) $settings['support'],
		], 200 );
	}

	/**
	 * PUT /settings — Save global plugin settings.
	 */
	public function save_global_settings( WP_REST_Request $request ): WP_REST_Response {
		$params   = $request->get_json_params();
		$settings = UISettings::getOptions();

		$settings['font_size']                   = isset( $params['fontSize'] ) ? absint( $params['fontSize'] ) : $settings['font_size'];
		$settings['attachment_size']              = isset( $params['iconSize'] ) ? absint( $params['iconSize'] ) : $settings['attachment_size'];
		$settings['icon_padding']                 = isset( $params['iconPadding'] ) ? absint( $params['iconPadding'] ) : $settings['icon_padding'];
		$settings['background_color']             = isset( $params['bgColor'] ) ? sanitize_hex_color( $params['bgColor'] ) : $settings['background_color'];
		$settings['background_color_hover']       = isset( $params['bgColorHover'] ) ? sanitize_hex_color( $params['bgColorHover'] ) : $settings['background_color_hover'];
		$settings['link_color']                   = isset( $params['linkColor'] ) ? sanitize_hex_color( $params['linkColor'] ) : $settings['link_color'];
		$settings['link_color_hover']             = isset( $params['linkColorHover'] ) ? sanitize_hex_color( $params['linkColorHover'] ) : $settings['link_color_hover'];
		$settings['animation_distance']           = $this->validate_select( $params['animationDistance'] ?? '', self::ALLOWED_GLOBAL_ANIMATION, $settings['animation_distance'] );
		$settings['animation_slideout']           = $this->validate_select( $params['animationSlideout'] ?? '', self::ALLOWED_GLOBAL_ANIMATION, $settings['animation_slideout'] );
		$settings['display_settings']             = $this->validate_select( $params['displaySettings'] ?? '', self::ALLOWED_GLOBAL_DISPLAY, $settings['display_settings'] );
		$settings['display_responsive_desktop']   = $this->validate_select( $params['responsiveDesktop'] ?? '', self::ALLOWED_GLOBAL_RESPONSIVE, $settings['display_responsive_desktop'] );
		$settings['display_responsive_tablet']    = $this->validate_select( $params['responsiveTablet'] ?? '', self::ALLOWED_GLOBAL_RESPONSIVE, $settings['display_responsive_tablet'] );
		$settings['display_responsive_mobile']    = $this->validate_select( $params['responsiveMobile'] ?? '', self::ALLOWED_GLOBAL_RESPONSIVE, $settings['display_responsive_mobile'] );
		$settings['support']                      = isset( $params['support'] ) ? ( $params['support'] ? 1 : 0 ) : $settings['support'];

		update_option( 'f12_floating_menu_settings', $settings );
		UISettings::getOptions( true );

		return $this->get_global_settings( $request );
	}

	// -------------------------------------------------------------------------
	// Pages Search & Post Types
	// -------------------------------------------------------------------------

	/**
	 * GET /pages?search={term} — Search pages/posts.
	 */
	public function search_pages( WP_REST_Request $request ): WP_REST_Response {
		global $wpdb;

		$search     = sanitize_text_field( $request->get_param( 'search' ) ?? '' );
		$post_types = get_post_types( [ 'public' => true ], 'names' );
		unset( $post_types['attachment'], $post_types['floating_menu'] );

		$placeholders = implode( ',', array_fill( 0, count( $post_types ), '%s' ) );
		$query_args   = array_values( $post_types );
		$query_args[] = 'publish';

		if ( ! empty( $search ) ) {
			$like         = '%' . $wpdb->esc_like( $search ) . '%';
			$query_args[] = $like;

			$results = $wpdb->get_results( $wpdb->prepare(
				"SELECT ID, post_title, post_type FROM {$wpdb->posts}
				 WHERE post_type IN ({$placeholders})
				 AND post_status = %s
				 AND post_title LIKE %s
				 ORDER BY post_title ASC
				 LIMIT 50",
				...$query_args
			) );
		} else {
			$results = $wpdb->get_results( $wpdb->prepare(
				"SELECT ID, post_title, post_type FROM {$wpdb->posts}
				 WHERE post_type IN ({$placeholders})
				 AND post_status = %s
				 ORDER BY post_title ASC
				 LIMIT 50",
				...$query_args
			) );
		}

		$pages = [];
		foreach ( $results as $row ) {
			$pages[] = [
				'id'    => (string) $row->ID,
				'title' => $row->post_title,
				'type'  => $row->post_type,
			];
		}

		return new WP_REST_Response( $pages, 200 );
	}

	/**
	 * GET /post-types — List all public post types.
	 */
	public function get_post_types( WP_REST_Request $request ): WP_REST_Response {
		$types  = \get_post_types( [ 'public' => true ], 'objects' );
		$result = [];

		foreach ( $types as $type ) {
			if ( 'attachment' === $type->name ) {
				continue;
			}
			$result[] = [
				'slug'  => $type->name,
				'label' => $type->label,
			];
		}

		return new WP_REST_Response( $result, 200 );
	}

	// -------------------------------------------------------------------------
	// Status & WPML
	// -------------------------------------------------------------------------

	/**
	 * GET /status — Environment info, integration checks, menu diagnostics.
	 */
	public function get_status( WP_REST_Request $request ): WP_REST_Response {
		global $wp_version;

		$wpml_active  = defined( 'ICL_SITEPRESS_VERSION' );
		$wpml_st      = defined( 'WPML_ST_VERSION' );
		$icl_lang     = defined( 'ICL_LANGUAGE_CODE' );
		$wpml_langs   = apply_filters( 'wpml_active_languages', [], 'skip_missing=0' );
		$langs_ok     = is_array( $wpml_langs ) && ! empty( $wpml_langs );

		$environment = [
			[ 'label' => 'Plugin Version',    'value' => FORGE12_FLOATING_VERSION ],
			[ 'label' => 'WordPress Version', 'value' => $wp_version ],
			[ 'label' => 'PHP Version',       'value' => PHP_VERSION ],
			[ 'label' => 'Active Theme',      'value' => wp_get_theme()->get( 'Name' ) . ' ' . wp_get_theme()->get( 'Version' ) ],
			[ 'label' => 'Multisite',         'value' => is_multisite() ? 'Yes' : 'No' ],
		];

		$integrations = [
			[
				'label' => 'WPML SitePress',
				'ok'    => $wpml_active,
				'value' => $wpml_active
					? sprintf( 'Active (v%s)', ICL_SITEPRESS_VERSION )
					: 'Not active',
			],
			[
				'label' => 'WPML String Translation',
				'ok'    => $wpml_st,
				'value' => $wpml_st
					? sprintf( 'Active (v%s)', WPML_ST_VERSION )
					: 'Not active',
			],
			[
				'label' => 'ICL_SITEPRESS_VERSION',
				'ok'    => $wpml_active,
				'value' => $wpml_active ? 'defined' : 'not defined',
			],
			[
				'label' => 'ICL_LANGUAGE_CODE',
				'ok'    => $icl_lang,
				'value' => $icl_lang ? ICL_LANGUAGE_CODE : 'not defined',
			],
			[
				'label' => 'wpml_active_languages',
				'ok'    => $langs_ok,
				'value' => $langs_ok
					? count( $wpml_langs ) . ' languages: ' . implode( ', ', array_keys( $wpml_langs ) )
					: 'Returns empty',
			],
			[
				'label' => 'icl_get_languages()',
				'ok'    => function_exists( 'icl_get_languages' ),
				'value' => function_exists( 'icl_get_languages' ) ? 'available' : 'not available',
			],
		];

		$wpml_details = [];
		if ( $wpml_active ) {
			$is_translated = apply_filters( 'wpml_is_translated_post_type', null, 'floating_menu' );
			$wpml_details  = [
				[
					'label' => 'Current Admin Language',
					'value' => apply_filters( 'wpml_current_language', '' ),
				],
				[
					'label' => 'Default Language',
					'value' => apply_filters( 'wpml_default_language', '' ),
				],
				[
					'label' => 'floating_menu translatable',
					'ok'    => ! $is_translated,
					'value' => $is_translated ? 'Yes — may cause filtering issues!' : 'No (correct)',
				],
			];
		}

		// Menu diagnostics.
		$posts = get_posts( [
			'post_type'        => 'floating_menu',
			'numberposts'      => -1,
			'suppress_filters' => true,
			'post_status'      => [ 'publish', 'draft' ],
		] );

		$menus = [];
		foreach ( $posts as $post ) {
			$fm       = new FloatingMenu( $post );
			$settings = $fm->getSettings();
			$links    = $fm->getLinks();
			$issues   = [];

			$position = $fm->getPosition();
			if ( empty( $position ) || 'hidden' === $position ) {
				$issues[] = 'Position is hidden';
			}
			if ( empty( $links ) ) {
				$issues[] = 'No links defined';
			}

			$visibility = [];
			if ( ! empty( $settings['pages_all'] ) && 1 == $settings['pages_all'] ) {
				$visibility[] = 'All pages';
			}
			$post_types = $fm->getPostTypes();
			if ( ! empty( $post_types ) ) {
				$visibility[] = 'Post types: ' . implode( ', ', $post_types );
			}
			if ( ! empty( $settings['pages'] ) ) {
				$visibility[] = 'Page IDs: ' . $settings['pages'];
			}
			if ( empty( $visibility ) ) {
				$issues[]     = 'No visibility rules';
				$visibility[] = '—';
			}

			$menus[] = [
				'id'         => $post->ID,
				'title'      => $post->post_title,
				'status'     => $post->post_status,
				'position'   => $position,
				'language'   => $settings['wpml_language'] ?? '',
				'links'      => count( $links ),
				'visibility' => $visibility,
				'issues'     => $issues,
			];
		}

		return new WP_REST_Response( [
			'environment'  => $environment,
			'integrations' => $integrations,
			'wpml'         => $wpml_details,
			'menus'        => $menus,
		], 200 );
	}

	/**
	 * GET /wpml/languages — List available WPML languages.
	 */
	public function get_wpml_languages( WP_REST_Request $request ): WP_REST_Response {
		if ( ! defined( 'ICL_SITEPRESS_VERSION' ) ) {
			return new WP_REST_Response( [ 'active' => false, 'languages' => [] ], 200 );
		}

		$wpml_languages = apply_filters( 'wpml_active_languages', [], 'skip_missing=0' );
		$languages      = [];

		if ( is_array( $wpml_languages ) ) {
			foreach ( $wpml_languages as $lang ) {
				$languages[] = [
					'code'       => $lang['language_code'],
					'name'       => $lang['native_name'],
					'translated' => $lang['translated_name'] ?? $lang['native_name'],
					'flag'       => $lang['country_flag_url'] ?? '',
				];
			}
		}

		return new WP_REST_Response( [
			'active'    => true,
			'languages' => $languages,
		], 200 );
	}

	// -------------------------------------------------------------------------
	// Repair
	// -------------------------------------------------------------------------

	/**
	 * POST /repair/icons — Detect and fix broken icon_id values in all menus.
	 *
	 * Broken means: icon_id contains a Lucide name (e.g. "Phone") instead of a
	 * FontAwesome class (e.g. "fas fa-phone"), or icon_id is empty but could be
	 * inferred from the link context.
	 */
	public function repair_icons( WP_REST_Request $request ): WP_REST_Response {
		$posts = get_posts( [
			'post_type'        => 'floating_menu',
			'numberposts'      => -1,
			'suppress_filters' => true,
			'post_status'      => [ 'publish', 'draft' ],
		] );

		$repaired = [];

		foreach ( $posts as $post ) {
			$links   = get_post_meta( $post->ID, '_floating_links', true );
			if ( ! is_array( $links ) ) {
				continue;
			}

			$changed = false;

			foreach ( $links as $index => &$link ) {
				$icon_id  = $link['icon_id'] ?? '';
				$original = $icon_id;

				// Skip if already a valid FontAwesome class.
				if ( preg_match( '/^(fas?|far|fab)\s+fa-/', $icon_id ) ) {
					continue;
				}

				// Try to reverse-map from Lucide name (case-insensitive).
				if ( ! empty( $icon_id ) ) {
					$lucide_lower = strtolower( $icon_id );
					// Convert PascalCase to kebab-case (e.g. "MessageCircle" -> "message-circle")
					$lucide_lower = strtolower( preg_replace( '/([a-z])([A-Z])/', '$1-$2', $icon_id ) );
					$fa_class     = $this->lucide_to_fontawesome( $lucide_lower );

					if ( ! empty( $fa_class ) ) {
						$link['icon_id'] = $fa_class;
						$changed         = true;
						$repaired[]      = [
							'menu'    => $post->post_title,
							'menuId'  => $post->ID,
							'item'    => $index,
							'label'   => $link['name'] ?? '',
							'before'  => $original,
							'after'   => $fa_class,
						];
					}
				}
			}
			unset( $link );

			if ( $changed ) {
				update_post_meta( $post->ID, '_floating_links', $links );
			}
		}

		return new WP_REST_Response( [
			'repaired' => $repaired,
			'count'    => count( $repaired ),
		], 200 );
	}

	// -------------------------------------------------------------------------
	// Transformation Helpers
	// -------------------------------------------------------------------------

	/**
	 * Transform a WP_Post (floating_menu) to the React response format.
	 */
	private function transform_menu_to_response( WP_Post $post ): array {
		$fm       = new FloatingMenu( $post );
		$settings = $fm->getSettings();
		$links    = $fm->getLinks();

		// Transform items.
		$items = [];
		foreach ( $links as $index => $link ) {
			$icon_type = 'none';
			$icon_name = '';

			if ( ! empty( $link['icon_id'] ) ) {
				$icon_name = $this->fontawesome_to_lucide( $link['icon_id'] );
				$icon_type = ! empty( $icon_name ) ? 'lucide' : 'none';
			}

			if ( ! empty( $link['attachment_id'] ) && absint( $link['attachment_id'] ) > 0 ) {
				$icon_type = 'image';
				$src       = wp_get_attachment_image_url( absint( $link['attachment_id'] ), 'full' );
				if ( $src ) {
					$link['attachment_url'] = $src;
				}
			} elseif ( ! empty( $link['attachment_url'] ) ) {
				$icon_type = 'url';
			}

			$items[] = [
				'id'         => 'item-' . $index,
				'label'      => $link['name'] ?? '',
				'showLabel'  => (bool) ( $link['display_name'] ?? 1 ),
				'url'        => $link['link'] ?? '',
				'target'     => $link['target'] ?? '_self',
				'title'      => $link['title'] ?? '',
				'noOpener'   => (bool) ( $link['noopener'] ?? 0 ),
				'noReferrer' => (bool) ( $link['noreferrer'] ?? 0 ),
				'iconType'   => $icon_type,
				'iconName'   => $icon_name,
				'iconId'     => $link['icon_id'] ?? '',
				'imageFile'  => null,
				'imageUrl'   => $link['attachment_url'] ?? '',
			];
		}

		// Transform settings.
		$post_types_map = [];
		$enabled_types  = $settings['pages_post_type'] ?? [];
		if ( is_array( $enabled_types ) ) {
			foreach ( $enabled_types as $pt ) {
				$post_types_map[ $pt ] = true;
			}
		}

		$pages_str    = $settings['pages'] ?? '';
		$specific_ids = ! empty( $pages_str ) ? array_filter( explode( ',', $pages_str ) ) : [];

		$position_php = $settings['position'] ?? 'hidden';

		return [
			'id'       => (string) $post->ID,
			'name'     => $post->post_title,
			'status'   => 'publish' === $post->post_status ? 'published' : 'draft',
			'settings' => [
				'displayAllPages'    => (bool) ( $settings['pages_all'] ?? 0 ),
				'postTypes'          => $post_types_map,
				'specificPages'      => array_map( 'strval', $specific_ids ),
				'position'           => self::POSITION_TO_REACT[ $position_php ] ?? 'hidden',
				'responsiveDesktop'  => $settings['display_responsive_desktop'] ?? 'default',
				'responsiveTablet'   => $settings['display_responsive_tablet'] ?? 'default',
				'responsiveMobile'   => $settings['display_responsive_mobile'] ?? 'default',
				'fontSize'           => (int) ( $settings['font_size'] ?? 16 ),
				'iconSize'           => (int) ( $settings['attachment_size'] ?? 32 ),
				'iconPadding'        => (int) ( $settings['icon_padding'] ?? 5 ),
				'bgColor'            => $settings['background_color'] ?? '',
				'bgColorHover'       => $settings['background_color_hover'] ?? '',
				'linkColor'          => $settings['link_color'] ?? '',
				'linkColorHover'     => $settings['link_color_hover'] ?? '',
				'animationDistance'   => $settings['animation_distance'] ?? 'default',
				'animationSlideout'  => $settings['animation_slideout'] ?? 'default',
				'displaySettings'    => $settings['display_settings'] ?? 'default',
				'wpmlLanguage'       => $settings['wpml_language'] ?? '',
			],
			'items' => $items,
		];
	}

	/**
	 * Transform React menu items to WordPress _floating_links format.
	 *
	 * @param array $react_items Array of menu items from the React frontend.
	 * @return array WordPress-formatted items.
	 */
	private function transform_items_from_request( array $react_items ): array {
		$php_items = [];

		foreach ( $react_items as $item ) {
			// Preserve original FontAwesome icon_id if provided, otherwise try to reverse-map from Lucide name.
			$icon_id = sanitize_text_field( $item['iconId'] ?? '' );
			if ( empty( $icon_id ) && ! empty( $item['iconName'] ) ) {
				$icon_id = $this->lucide_to_fontawesome( sanitize_text_field( $item['iconName'] ) );
			}

			$php_items[] = [
				'name'           => sanitize_text_field( $item['label'] ?? '' ),
				'display_name'   => ! empty( $item['showLabel'] ) ? 1 : 0,
				'link'           => esc_url_raw( $item['url'] ?? '' ),
				'target'         => in_array( $item['target'] ?? '', [ '_self', '_blank' ], true ) ? $item['target'] : '_self',
				'title'          => sanitize_text_field( $item['title'] ?? '' ),
				'noopener'       => ! empty( $item['noOpener'] ) ? 1 : 0,
				'noreferrer'     => ! empty( $item['noReferrer'] ) ? 1 : 0,
				'icon_id'        => $icon_id,
				'attachment_id'  => 0,
				'attachment_url' => esc_url_raw( $item['imageUrl'] ?? '' ),
			];
		}

		return $php_items;
	}

	/**
	 * Transform React menu settings to WordPress _floating_settings format.
	 *
	 * @param array $react_settings Settings from the React frontend.
	 * @return array WordPress-formatted settings.
	 */
	private function transform_settings_from_request( array $react_settings ): array {
		// Build post types array from the map.
		$post_types = [];
		if ( isset( $react_settings['postTypes'] ) && is_array( $react_settings['postTypes'] ) ) {
			foreach ( $react_settings['postTypes'] as $slug => $enabled ) {
				if ( $enabled ) {
					$post_types[] = sanitize_key( $slug );
				}
			}
		}

		// Build specific pages string.
		$pages = '';
		if ( isset( $react_settings['specificPages'] ) && is_array( $react_settings['specificPages'] ) ) {
			$page_ids = array_map( 'absint', $react_settings['specificPages'] );
			$pages    = implode( ',', array_filter( $page_ids ) );
		}

		$position_react = $react_settings['position'] ?? 'hidden';

		return [
			'pages_all'                  => ! empty( $react_settings['displayAllPages'] ) ? 1 : 0,
			'pages_post_type'            => $post_types,
			'pages'                      => $pages,
			'position'                   => self::POSITION_FROM_REACT[ $position_react ] ?? 'hidden',
			'attachment_size'            => isset( $react_settings['iconSize'] ) ? absint( $react_settings['iconSize'] ) : 32,
			'font_size'                  => isset( $react_settings['fontSize'] ) ? absint( $react_settings['fontSize'] ) : 16,
			'icon_padding'               => isset( $react_settings['iconPadding'] ) ? absint( $react_settings['iconPadding'] ) : 5,
			'background_color'           => sanitize_hex_color( $react_settings['bgColor'] ?? '' ) ?: '',
			'background_color_hover'     => sanitize_hex_color( $react_settings['bgColorHover'] ?? '' ) ?: '',
			'link_color'                 => sanitize_hex_color( $react_settings['linkColor'] ?? '' ) ?: '',
			'link_color_hover'           => sanitize_hex_color( $react_settings['linkColorHover'] ?? '' ) ?: '',
			'animation_distance'         => $this->validate_select( $react_settings['animationDistance'] ?? '', self::ALLOWED_ANIMATION, 'default' ),
			'animation_slideout'         => $this->validate_select( $react_settings['animationSlideout'] ?? '', self::ALLOWED_ANIMATION, 'default' ),
			'display_settings'           => $this->validate_select( $react_settings['displaySettings'] ?? '', self::ALLOWED_DISPLAY, 'default' ),
			'display_responsive_desktop' => $this->validate_select( $react_settings['responsiveDesktop'] ?? '', self::ALLOWED_RESPONSIVE, 'default' ),
			'display_responsive_tablet'  => $this->validate_select( $react_settings['responsiveTablet'] ?? '', self::ALLOWED_RESPONSIVE, 'default' ),
			'display_responsive_mobile'  => $this->validate_select( $react_settings['responsiveMobile'] ?? '', self::ALLOWED_RESPONSIVE, 'default' ),
			'wpml_language'              => sanitize_key( $react_settings['wpmlLanguage'] ?? '' ),
		];
	}

	/**
	 * Validate a value against a whitelist.
	 */
	private function validate_select( string $value, array $allowed, string $default ): string {
		return in_array( $value, $allowed, true ) ? $value : $default;
	}

	/**
	 * Convert a FontAwesome icon class to a Lucide icon name.
	 *
	 * @param string $fa_class FontAwesome class (e.g. "fab fa-facebook-f").
	 * @return string Lucide icon name or empty string.
	 */
	private function fontawesome_to_lucide( string $fa_class ): string {
		// Values use PascalCase to match the React iconMap keys.
		$map = [
			'fas fa-phone'           => 'Phone',
			'fas fa-envelope'        => 'Mail',
			'fas fa-mail-bulk'       => 'Mail',
			'fab fa-whatsapp'        => 'MessageCircle',
			'fab fa-facebook-f'      => 'Facebook',
			'fab fa-facebook'        => 'Facebook',
			'fab fa-twitter'         => 'Twitter',
			'fab fa-linkedin-in'     => 'Linkedin',
			'fab fa-linkedin'        => 'Linkedin',
			'fab fa-instagram'       => 'Instagram',
			'fab fa-pinterest-p'     => 'Pin',
			'fab fa-tumblr'          => 'Rss',
			'fab fa-reddit-alien'    => 'MessageSquare',
			'fab fa-youtube'         => 'Youtube',
			'fab fa-google-plus-g'   => 'PlusCircle',
			'fas fa-question-circle' => 'HelpCircle',
			'fas fa-comments'        => 'MessageSquare',
			'fas fa-comment'         => 'MessageCircle',
			'fas fa-heart'           => 'Heart',
			'far fa-heart'           => 'Heart',
			'fas fa-home'            => 'Home',
			'fas fa-star'            => 'Star',
			'far fa-star'            => 'Star',
			'fas fa-globe'           => 'Globe',
			'fas fa-search'          => 'Search',
			'fas fa-cog'             => 'Settings',
			'fas fa-cogs'            => 'Settings',
			'fas fa-user'            => 'User',
			'far fa-user'            => 'User',
			'fas fa-shopping-cart'   => 'ShoppingCart',
			'fas fa-map-marker-alt'  => 'MapPin',
			'fas fa-clock'           => 'Clock',
			'far fa-clock'           => 'Clock',
			'fas fa-camera'          => 'Camera',
			'fas fa-image'           => 'Image',
			'far fa-image'           => 'Image',
			'fas fa-file'            => 'File',
			'far fa-file'            => 'File',
			'fas fa-lock'            => 'Lock',
			'fas fa-shield-alt'      => 'Shield',
			'fas fa-check'           => 'Check',
			'fas fa-times'           => 'X',
			'fas fa-plus'            => 'Plus',
			'fas fa-minus'           => 'Minus',
			'fas fa-arrow-up'        => 'ArrowUp',
			'fas fa-arrow-down'      => 'ArrowDown',
			'fas fa-arrow-left'      => 'ArrowLeft',
			'fas fa-arrow-right'     => 'ArrowRight',
			'fas fa-external-link-alt' => 'ExternalLink',
			'fas fa-download'        => 'Download',
			'fas fa-upload'          => 'Upload',
			'fas fa-share-alt'       => 'Share2',
			'fas fa-print'           => 'Printer',
			'fas fa-info-circle'     => 'Info',
			'fas fa-exclamation-triangle' => 'AlertTriangle',
			'far fa-trash-alt'       => 'Trash2',
			'fas fa-bars'            => 'Menu',
			'fas fa-grip-vertical'   => 'GripVertical',
			'fas fa-address-book'    => 'BookUser',
			'far fa-address-book'    => 'BookUser',
		];

		return $map[ $fa_class ] ?? '';
	}

	/**
	 * Convert a Lucide icon name back to a FontAwesome icon class.
	 *
	 * @param string $lucide_name Lucide icon name (e.g. "phone").
	 * @return string FontAwesome class or empty string.
	 */
	private function lucide_to_fontawesome( string $lucide_name ): string {
		// Build reverse map (first FA class wins for each Lucide name).
		static $reverse = null;
		if ( null === $reverse ) {
			$reverse = [];
			$forward = [
				'fas fa-phone'           => 'phone',
				'fas fa-envelope'        => 'mail',
				'fab fa-whatsapp'        => 'message-circle',
				'fab fa-facebook-f'      => 'facebook',
				'fab fa-twitter'         => 'twitter',
				'fab fa-linkedin-in'     => 'linkedin',
				'fab fa-instagram'       => 'instagram',
				'fab fa-pinterest-p'     => 'pin',
				'fab fa-tumblr'          => 'rss',
				'fab fa-reddit-alien'    => 'message-square',
				'fab fa-youtube'         => 'youtube',
				'fab fa-google-plus-g'   => 'plus-circle',
				'fas fa-question-circle' => 'help-circle',
				'fas fa-comment'         => 'message-circle',
				'fas fa-heart'           => 'heart',
				'fas fa-home'            => 'home',
				'fas fa-star'            => 'star',
				'fas fa-globe'           => 'globe',
				'fas fa-search'          => 'search',
				'fas fa-cog'             => 'settings',
				'fas fa-user'            => 'user',
				'fas fa-shopping-cart'   => 'shopping-cart',
				'fas fa-map-marker-alt'  => 'map-pin',
				'fas fa-clock'           => 'clock',
				'fas fa-camera'          => 'camera',
				'fas fa-image'           => 'image',
				'fas fa-file'            => 'file',
				'fas fa-lock'            => 'lock',
				'fas fa-shield-alt'      => 'shield',
				'fas fa-check'           => 'check',
				'fas fa-times'           => 'x',
				'fas fa-plus'            => 'plus',
				'fas fa-minus'           => 'minus',
				'fas fa-arrow-up'        => 'arrow-up',
				'fas fa-arrow-down'      => 'arrow-down',
				'fas fa-arrow-left'      => 'arrow-left',
				'fas fa-arrow-right'     => 'arrow-right',
				'fas fa-external-link-alt' => 'external-link',
				'fas fa-download'        => 'download',
				'fas fa-upload'          => 'upload',
				'fas fa-share-alt'       => 'share-2',
				'fas fa-print'           => 'printer',
				'fas fa-info-circle'     => 'info',
				'fas fa-exclamation-triangle' => 'alert-triangle',
				'far fa-trash-alt'       => 'trash-2',
				'fas fa-bars'            => 'menu',
				'fas fa-grip-vertical'   => 'grip-vertical',
				'fas fa-address-book'    => 'book-user',
			];
			foreach ( $forward as $fa => $lucide ) {
				if ( ! isset( $reverse[ $lucide ] ) ) {
					$reverse[ $lucide ] = $fa;
				}
			}
		}

		// React sends PascalCase names (e.g. "MapPin"), map uses lowercase with hyphens.
		$normalized = strtolower( preg_replace( '/([a-z])([A-Z])/', '$1-$2', $lucide_name ) );

		return $reverse[ $normalized ] ?? '';
	}
}

// Register the REST API routes.
$rest_api = new REST_API();
$rest_api->register_routes();
