<?php
/**
 * REST API endpoints for plugin settings.
 *
 * @package RawTypeOE
 */

namespace RawTypeOE\Free\Admin;

use RawTypeOE\Free\Activator;
use RawTypeOE\Free\Column_Registry;
use RawTypeOE\Free\Custom_Field_Scanner;
use RawTypeOE\Free\Export_Engine;
use RawTypeOE\Free\Export_Profile;
use RawTypeOE\Free\Plugin;
use RawTypeOE\Free\Query_Builder;
use RawTypeOE\Free\Template_Library;

// Prevent direct access.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Handles REST API endpoints for the admin app.
 */
class Rest_API {

	/**
	 * Gate every plugin endpoint behind the WooCommerce manager capability.
	 *
	 * @return bool
	 */
	public function check_permission() {
		return current_user_can( 'manage_woocommerce' );
	}

	/** REST API namespace. */
	const API_NAMESPACE = 'rawtype_oe/v1';

	/**
	 * Hard ceiling for preview sample sizes. Previews render a handful of rows
	 * in the admin table; without a cap a single request could ask the server
	 * to hydrate the entire store and hang the UI.
	 */
	const PREVIEW_MAX_LIMIT = 100;

	/**
	 * Column registry service.
	 *
	 * @var Column_Registry
	 */
	private $column_registry;

	/**
	 * Export engine service.
	 *
	 * @var Export_Engine
	 */
	private $export_engine;

	/**
	 * Custom field scanner service.
	 *
	 * @var Custom_Field_Scanner
	 */
	private $custom_field_scanner;

	/**
	 * Constructor.
	 *
	 * @param Column_Registry      $column_registry      Column registry service.
	 * @param Export_Engine        $export_engine        Export engine service.
	 * @param Custom_Field_Scanner $custom_field_scanner Custom field scanner service.
	 */
	public function __construct( Column_Registry $column_registry, Export_Engine $export_engine = null, Custom_Field_Scanner $custom_field_scanner = null ) {
		$this->column_registry      = $column_registry;
		$this->export_engine        = $export_engine;
		$this->custom_field_scanner = $custom_field_scanner ? $custom_field_scanner : new Custom_Field_Scanner();
	}

	/** Register WordPress hooks. */
	public function register_hooks() {
		add_action( 'rest_api_init', array( $this, 'register_routes' ) );
	}

	/** Register REST API routes. */
	public function register_routes() {
		register_rest_route(
			self::API_NAMESPACE,
			'/settings',
			array(
				'schema' => array( $this, 'get_settings_schema' ),
				array(
					'methods'             => \WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_settings' ),
					'permission_callback' => array( $this, 'check_permission' ),
				),
				array(
					'methods'             => \WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'update_settings' ),
					'permission_callback' => array( $this, 'check_permission' ),
					'args'                => $this->get_settings_args(),
				),
			)
		);

		register_rest_route(
			self::API_NAMESPACE,
			'/columns/(?P<entity>orders|products|customers|cross-entity)',
			array(
				'schema' => array( $this, 'get_columns_schema' ),
				array(
					'methods'             => \WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_columns' ),
					'permission_callback' => array( $this, 'check_permission' ),
					'args'                => array(
						'entity' => array(
							'description'       => __( 'Entity slug to retrieve columns for.', 'rawtype-order-exporter-woocommerce' ),
							'type'              => 'string',
							'required'          => true,
							'enum'              => array( 'orders', 'products', 'customers', 'cross-entity' ),
							'sanitize_callback' => 'sanitize_key',
						),
					),
				),
			)
		);

		register_rest_route(
			self::API_NAMESPACE,
			'/stats/counts',
			array(
				array(
					'methods'             => \WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_stats_counts' ),
					'permission_callback' => array( $this, 'check_permission' ),
				),
			)
		);

		register_rest_route(
			self::API_NAMESPACE,
			'/preview',
			array(
				array(
					'methods'             => \WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'get_preview' ),
					'permission_callback' => array( $this, 'check_permission' ),
					'args'                => array(
						'entity'            => array(
							'type'              => 'string',
							'required'          => true,
							'enum'              => array( 'orders', 'products', 'customers', 'cross-entity' ),
							'sanitize_callback' => 'sanitize_key',
						),
						'filters'           => array(
							'type'    => 'array',
							'default' => array(),
						),
						'logic'             => array(
							'type'              => 'string',
							'default'           => 'and',
							'enum'              => array( 'and', 'or' ),
							'sanitize_callback' => 'sanitize_key',
						),
						'count_only'        => array(
							'type'              => 'boolean',
							'default'           => false,
							'sanitize_callback' => 'rest_sanitize_boolean',
						),
						'columns'           => array(
							'type'    => 'array',
							'default' => array(),
						),
						'calculated_fields' => array(
							'type'    => 'array',
							'default' => array(),
						),
						'limit'             => array(
							'type'              => 'integer',
							'default'           => 10,
							'minimum'           => 1,
							'maximum'           => self::PREVIEW_MAX_LIMIT,
							'sanitize_callback' => 'absint',
						),
						'options'           => array(
							'type'    => 'object',
							'default' => array(),
						),
						'cross_entity'      => array(
							'type'    => 'object',
							'default' => array(),
						),
					),
				),
			)
		);

		register_rest_route(
			self::API_NAMESPACE,
			'/export',
			array(
				array(
					'methods'             => \WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'handle_export' ),
					'permission_callback' => array( $this, 'check_permission' ),
					'args'                => array(
						'entity'            => array(
							'type'              => 'string',
							'required'          => true,
							'enum'              => array( 'orders', 'products', 'customers', 'cross-entity' ),
							'sanitize_callback' => 'sanitize_key',
						),
						'filters'           => array(
							'type'    => 'array',
							'default' => array(),
						),
						'logic'             => array(
							'type'              => 'string',
							'default'           => 'and',
							'enum'              => array( 'and', 'or' ),
							'sanitize_callback' => 'sanitize_key',
						),
						'columns'           => array(
							'type'     => 'array',
							'required' => true,
						),
						'format'            => array(
							'type'              => 'string',
							'default'           => 'csv',
							'enum'              => array( 'csv', 'xlsx' ),
							'sanitize_callback' => 'sanitize_key',
						),
						'options'           => array(
							'type'    => 'object',
							'default' => array(),
						),
						'calculated_fields' => array(
							'type'    => 'array',
							'default' => array(),
						),
						'cross_entity'      => array(
							'type'    => 'object',
							'default' => array(),
						),
					),
				),
			)
		);

		register_rest_route(
			self::API_NAMESPACE,
			'/statuses/(?P<entity>orders|products|customers|cross-entity)',
			array(
				array(
					'methods'             => \WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_statuses' ),
					'permission_callback' => array( $this, 'check_permission' ),
					'args'                => array(
						'entity' => array(
							'type'              => 'string',
							'required'          => true,
							'enum'              => array( 'orders', 'products', 'customers', 'cross-entity' ),
							'sanitize_callback' => 'sanitize_key',
						),
					),
				),
			)
		);

		register_rest_route(
			self::API_NAMESPACE,
			'/custom-fields/(?P<entity>orders|products|customers)',
			array(
				array(
					'methods'             => \WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_custom_fields' ),
					'permission_callback' => array( $this, 'check_permission' ),
					'args'                => array(
						'entity'        => array(
							'type'              => 'string',
							'required'          => true,
							'enum'              => array( 'orders', 'products', 'customers' ),
							'sanitize_callback' => 'sanitize_key',
						),
						'show_internal' => array(
							'type'              => 'boolean',
							'default'           => false,
							'sanitize_callback' => 'rest_sanitize_boolean',
						),
						'refresh'       => array(
							'type'              => 'boolean',
							'default'           => false,
							'sanitize_callback' => 'rest_sanitize_boolean',
						),
					),
				),
			)
		);

		// Product categories (hierarchical tree).
		register_rest_route(
			self::API_NAMESPACE,
			'/categories',
			array(
				array(
					'methods'             => \WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_categories' ),
					'permission_callback' => array( $this, 'check_permission' ),
				),
			)
		);

		// Product tags (flat list).
		register_rest_route(
			self::API_NAMESPACE,
			'/tags',
			array(
				array(
					'methods'             => \WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_tags' ),
					'permission_callback' => array( $this, 'check_permission' ),
				),
			)
		);

		// Product search (autocomplete).
		register_rest_route(
			self::API_NAMESPACE,
			'/search/products',
			array(
				array(
					'methods'             => \WP_REST_Server::READABLE,
					'callback'            => array( $this, 'search_products' ),
					'permission_callback' => array( $this, 'check_permission' ),
					'args'                => array(
						'q' => array(
							'required'          => true,
							'type'              => 'string',
							'sanitize_callback' => 'sanitize_text_field',
						),
					),
				),
			)
		);

		// Templates.
		register_rest_route(
			self::API_NAMESPACE,
			'/templates',
			array(
				array(
					'methods'             => \WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_templates' ),
					'permission_callback' => array( $this, 'check_permission' ),
					'args'                => array(
						'entity' => array(
							'required'          => false,
							'type'              => 'string',
							'enum'              => array( 'orders', 'products', 'customers' ),
							'sanitize_callback' => 'sanitize_key',
						),
					),
				),
			)
		);

		register_rest_route(
			self::API_NAMESPACE,
			'/templates/(?P<template_id>[a-z0-9\-]+)',
			array(
				array(
					'methods'             => \WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_single_template' ),
					'permission_callback' => array( $this, 'check_permission' ),
					'args'                => array(
						'template_id' => array(
							'required'          => true,
							'type'              => 'string',
							'sanitize_callback' => 'sanitize_key',
						),
					),
				),
			)
		);

		// Profiles — collection.
		register_rest_route(
			self::API_NAMESPACE,
			'/profiles',
			array(
				array(
					'methods'             => \WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_profiles' ),
					'permission_callback' => array( $this, 'check_permission' ),
				),
				array(
					'methods'             => \WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'create_profile' ),
					'permission_callback' => array( $this, 'check_permission' ),
					'args'                => array(
						'name'   => array(
							'required'          => true,
							'type'              => 'string',
							'sanitize_callback' => 'sanitize_text_field',
						),
						'config' => array(
							'required' => true,
							'type'     => 'object',
						),
					),
				),
			)
		);

		// Profiles — single item.
		register_rest_route(
			self::API_NAMESPACE,
			'/profiles/(?P<id>\d+)',
			array(
				array(
					'methods'             => \WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_single_profile' ),
					'permission_callback' => array( $this, 'check_permission' ),
					'args'                => array(
						'id' => array(
							'required'          => true,
							'type'              => 'integer',
							'sanitize_callback' => 'absint',
						),
					),
				),
				array(
					'methods'             => 'PUT',
					'callback'            => array( $this, 'update_profile' ),
					'permission_callback' => array( $this, 'check_permission' ),
					'args'                => array(
						'id'     => array(
							'required'          => true,
							'type'              => 'integer',
							'sanitize_callback' => 'absint',
						),
						'name'   => array(
							'required'          => true,
							'type'              => 'string',
							'sanitize_callback' => 'sanitize_text_field',
						),
						'config' => array(
							'required' => true,
							'type'     => 'object',
						),
					),
				),
				array(
					'methods'             => \WP_REST_Server::DELETABLE,
					'callback'            => array( $this, 'delete_profile' ),
					'permission_callback' => array( $this, 'check_permission' ),
					'args'                => array(
						'id' => array(
							'required'          => true,
							'type'              => 'integer',
							'sanitize_callback' => 'absint',
						),
					),
				),
			)
		);

		// Profile run.
		register_rest_route(
			self::API_NAMESPACE,
			'/profiles/(?P<id>\d+)/run',
			array(
				array(
					'methods'             => \WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'run_profile' ),
					'permission_callback' => array( $this, 'check_permission' ),
					'args'                => array(
						'id' => array(
							'required'          => true,
							'type'              => 'integer',
							'sanitize_callback' => 'absint',
						),
					),
				),
			)
		);
	}

	/**
	 * Get current settings.
	 *
	 * @param \WP_REST_Request $request Full details about the request.
	 * @return array
	 */
	public function get_settings( $request ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
		$settings = $this->get_saved_settings();

		$settings['is_pro']    = Plugin::is_pro();
		$settings['isPro']     = $settings['is_pro'];
		$settings['row_limit'] = Export_Engine::get_row_limit();

		/**
		 * Filter the settings response before sending to the client.
		 *
		 * @param array $settings The settings array.
		 */
		$settings = apply_filters( 'rawtype_oe_rest_settings_response', $settings );

		return rest_ensure_response( $settings );
	}

	/**
	 * Update settings.
	 *
	 * @param \WP_REST_Request $request Full details about the request.
	 * @return array
	 */
	public function update_settings( $request ) {
		$input = $request->get_json_params();

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

		$current  = $this->get_saved_settings();
		$defaults = Activator::get_default_settings();

		$sanitized = array(
			'default_format' => isset( $input['default_format'] ) ? $this->sanitize_default_format( $input['default_format'] ) : $current['default_format'],
			'date_format'    => isset( $input['date_format'] ) ? $this->sanitize_date_format( $input['date_format'] ) : $current['date_format'],
		);

		/**
		 * Filter the sanitized settings before saving.
		 *
		 * @param array $sanitized The sanitized settings.
		 * @param array $input     The raw input.
		 */
		$sanitized = apply_filters( 'rawtype_oe_rest_sanitize_settings', $sanitized, $input );

		update_option( 'rawtype_oe_settings', $sanitized );

		return rest_ensure_response( $this->get_saved_settings() );
	}

	/**
	 * Get available columns for the requested entity.
	 *
	 * @param \WP_REST_Request $request Request object.
	 * @return array|\WP_Error
	 */
	public function get_columns( $request ) {
		$entity  = sanitize_key( $request->get_param( 'entity' ) );
		$columns = $this->column_registry->get_columns( $entity );

		if ( empty( $columns ) ) {
			return new \WP_Error(
				'invalid_entity',
				esc_html__( 'Invalid entity for column lookup.', 'rawtype-order-exporter-woocommerce' ),
				array( 'status' => 400 )
			);
		}

		return rest_ensure_response( array_values( $columns ) );
	}

	/**
	 * Get entity counts (orders, products, customers).
	 *
	 * @param \WP_REST_Request $request Request object.
	 * @return array
	 */
	public function get_stats_counts( $request ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
		$counts = Query_Builder::get_entity_counts();
		return rest_ensure_response( $counts );
	}

	/**
	 * Execute a preview query with filters.
	 *
	 * @param \WP_REST_Request $request Request object.
	 * @return array
	 */
	public function get_preview( $request ) {
		$count_only      = rest_sanitize_boolean( $request->get_param( 'count_only' ) );
		$limit           = absint( $request->get_param( 'limit' ) );
		$config          = $this->normalize_export_config(
			array(
				'entity'            => $request->get_param( 'entity' ),
				'filters'           => $request->get_param( 'filters' ),
				'logic'             => $request->get_param( 'logic' ),
				'columns'           => $request->get_param( 'columns' ),
				'options'           => $request->get_param( 'options' ),
				'cross_entity'      => $request->get_param( 'cross_entity' ),
				'calculated_fields' => $request->get_param( 'calculated_fields' ),
				'source'            => 'preview',
			)
		);
		$preview_options = array(
			'cross_entity'      => $config['cross_entity'],
			'calculated_fields' => isset( $config['calculated_fields'] ) ? $config['calculated_fields'] : array(),
			'options'           => $config['options'],
		);

		if ( $limit < 1 ) {
			$limit = 10;
		}

		// Belt and braces — schema validation can be bypassed by internal callers.
		$limit = min( $limit, self::PREVIEW_MAX_LIMIT );

		$row_limit = Export_Engine::get_row_limit();

		// Cross-entity always uses the export engine.
		if ( 'cross-entity' === $config['entity'] ) {
			if ( null === $this->export_engine ) {
				return new \WP_Error(
					'export_unavailable',
					esc_html__( 'Export engine is not available.', 'rawtype-order-exporter-woocommerce' ),
					array( 'status' => 500 )
				);
			}

			$result = $this->export_engine->preview( $config['entity'], $config['filters'], $config['logic'], $config['columns'], $limit, $preview_options, $count_only );
			if ( is_wp_error( $result ) ) {
				return $result;
			}

			$result['row_limit'] = $row_limit;
			return rest_ensure_response( $result );
		}

		// Use the export engine for formatted preview when columns are provided.
		if ( ! empty( $config['columns'] ) && null !== $this->export_engine && ! $count_only ) {
			$result = $this->export_engine->preview( $config['entity'], $config['filters'], $config['logic'], $config['columns'], $limit, $preview_options, false );
			if ( is_wp_error( $result ) ) {
				return $result;
			}

			$result['row_limit'] = $row_limit;
			return rest_ensure_response( $result );
		}

		// Fallback to legacy preview (count-only or no columns).
		$result = Query_Builder::preview( $config['entity'], $config['filters'], $config['logic'], $count_only, $limit );

		$result['row_limit'] = $row_limit;
		return rest_ensure_response( $result );
	}

	/**
	 * Get available statuses for an entity.
	 *
	 * @param \WP_REST_Request $request Request object.
	 * @return array
	 */
	public function get_statuses( $request ) {
		$entity   = sanitize_key( $request->get_param( 'entity' ) );
		$statuses = Query_Builder::get_entity_statuses( $entity );

		return rest_ensure_response( $statuses );
	}

	/**
	 * Get discovered custom fields for an entity.
	 *
	 * @param \WP_REST_Request $request Request object.
	 * @return array
	 */
	public function get_custom_fields( $request ) {
		$entity        = sanitize_key( $request->get_param( 'entity' ) );
		$show_internal = rest_sanitize_boolean( $request->get_param( 'show_internal' ) );
		$refresh       = rest_sanitize_boolean( $request->get_param( 'refresh' ) );

		$fields = $this->custom_field_scanner->get_fields( $entity, $show_internal, $refresh );

		return rest_ensure_response( $fields );
	}

	/**
	 * Get product categories as hierarchical tree.
	 *
	 * @param \WP_REST_Request $request Request object.
	 * @return array
	 */
	public function get_categories( $request ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
		$tree = Query_Builder::get_categories_tree();
		return rest_ensure_response( $tree );
	}

	/**
	 * Get product tags as flat list.
	 *
	 * @param \WP_REST_Request $request Request object.
	 * @return array
	 */
	public function get_tags( $request ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
		$tags = Query_Builder::get_product_tags();
		return rest_ensure_response( $tags );
	}

	/**
	 * Search products by name or SKU for autocomplete.
	 *
	 * @param \WP_REST_Request $request Request object.
	 * @return array
	 */
	public function search_products( $request ) {
		$term = sanitize_text_field( $request->get_param( 'q' ) );

		if ( strlen( $term ) < 3 ) {
			return rest_ensure_response( array() );
		}

		$results = Query_Builder::search_products( $term, 20 );
		return rest_ensure_response( $results );
	}

	/**
	 * Handle an export request.
	 *
	 * @param \WP_REST_Request $request Full details about the request.
	 * @return array|\WP_Error Export result or error.
	 */
	public function handle_export( $request ) {
		$result = $this->dispatch_export_config(
			array(
				'entity'            => $request->get_param( 'entity' ),
				'filters'           => $request->get_param( 'filters' ),
				'logic'             => $request->get_param( 'logic' ),
				'columns'           => $request->get_param( 'columns' ),
				'format'            => $request->get_param( 'format' ),
				'options'           => $request->get_param( 'options' ),
				'cross_entity'      => $request->get_param( 'cross_entity' ),
				'calculated_fields' => $request->get_param( 'calculated_fields' ),
				'source'            => 'manual',
			)
		);

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

		return rest_ensure_response( $result );
	}

	/**
	 * Get available templates, optionally filtered by entity.
	 *
	 * @param \WP_REST_Request $request Request object.
	 * @return array
	 */
	public function get_templates( $request ) {
		$library = new Template_Library();
		$entity  = $request->get_param( 'entity' );

		if ( $entity ) {
			$templates = $library->get_templates_by_entity( sanitize_key( $entity ) );
		} else {
			$templates = $library->get_templates();
		}

		return rest_ensure_response( array_values( $templates ) );
	}

	/**
	 * Get a single template by ID.
	 *
	 * @param \WP_REST_Request $request Request object.
	 * @return array|\WP_Error
	 */
	public function get_single_template( $request ) {
		$library  = new Template_Library();
		$template = $library->get_template( sanitize_key( $request->get_param( 'template_id' ) ) );

		if ( ! $template ) {
			return new \WP_Error(
				'template_not_found',
				esc_html__( 'Template not found.', 'rawtype-order-exporter-woocommerce' ),
				array( 'status' => 404 )
			);
		}

		return rest_ensure_response( $template );
	}

	/**
	 * Get all saved profiles.
	 *
	 * @param \WP_REST_Request $request Request object.
	 * @return array
	 */
	public function get_profiles( $request ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
		$profiles = new Export_Profile();
		return rest_ensure_response( $profiles->get_all() );
	}

	/**
	 * Get a single profile by ID.
	 *
	 * @param \WP_REST_Request $request Request object.
	 * @return array|\WP_Error
	 */
	public function get_single_profile( $request ) {
		$profiles = new Export_Profile();
		$profile  = $profiles->get( absint( $request->get_param( 'id' ) ) );

		if ( ! $profile ) {
			return new \WP_Error(
				'profile_not_found',
				esc_html__( 'Profile not found.', 'rawtype-order-exporter-woocommerce' ),
				array( 'status' => 404 )
			);
		}

		return rest_ensure_response( $profile );
	}

	/**
	 * Create a new profile.
	 *
	 * @param \WP_REST_Request $request Request object.
	 * @return array|\WP_Error
	 */
	public function create_profile( $request ) {
		$name   = sanitize_text_field( $request->get_param( 'name' ) );
		$config = $this->normalize_profile_config( $request->get_param( 'config' ) );

		$profiles = new Export_Profile();
		$post_id  = $profiles->create( $name, $config );

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

		return rest_ensure_response( $profiles->get( $post_id ) );
	}

	/**
	 * Update an existing profile.
	 *
	 * @param \WP_REST_Request $request Request object.
	 * @return array|\WP_Error
	 */
	public function update_profile( $request ) {
		$profile_id = absint( $request->get_param( 'id' ) );
		$name       = sanitize_text_field( $request->get_param( 'name' ) );
		$config     = $this->normalize_profile_config( $request->get_param( 'config' ) );

		$profiles = new Export_Profile();
		$result   = $profiles->update( $profile_id, $name, $config );

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

		return rest_ensure_response( $profiles->get( $profile_id ) );
	}

	/**
	 * Delete a profile.
	 *
	 * @param \WP_REST_Request $request Request object.
	 * @return array|\WP_Error
	 */
	public function delete_profile( $request ) {
		$profiles = new Export_Profile();
		$deleted  = $profiles->delete( absint( $request->get_param( 'id' ) ) );

		if ( ! $deleted ) {
			return new \WP_Error(
				'profile_not_found',
				esc_html__( 'Profile not found.', 'rawtype-order-exporter-woocommerce' ),
				array( 'status' => 404 )
			);
		}

		return rest_ensure_response( array( 'deleted' => true ) );
	}

	/**
	 * Run a saved profile export.
	 *
	 * @param \WP_REST_Request $request Request object.
	 * @return array|\WP_Error
	 */
	public function run_profile( $request ) {
		$profiles = new Export_Profile();
		$profile  = $profiles->get( absint( $request->get_param( 'id' ) ) );

		if ( ! $profile ) {
			return new \WP_Error(
				'profile_not_found',
				esc_html__( 'Profile not found.', 'rawtype-order-exporter-woocommerce' ),
				array( 'status' => 404 )
			);
		}

		$config               = is_array( $profile['config'] ) ? $profile['config'] : array();
		$config['profile_id'] = absint( $profile['id'] );
		$config['source']     = 'profile';

		$format_override = $request->get_param( 'format' );
		if ( $format_override ) {
			$config['format'] = sanitize_key( $format_override );
		}

		$result = $this->dispatch_export_config( $config );

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

		return rest_ensure_response( $result );
	}

	/**
	 * Normalize a profile configuration before it is persisted.
	 *
	 * Removes runtime-only fields so saved profiles only contain reusable export
	 * state.
	 *
	 * @param mixed $config Raw profile config payload.
	 * @return array
	 */
	private function normalize_profile_config( $config ) {
		$normalized = $this->normalize_export_config( $config );

		unset( $normalized['source'], $normalized['profile_id'], $normalized['total_count'], $normalized['source_count'], $normalized['export_id'] );

		return $normalized;
	}

	/**
	 * Normalize export configuration payloads from REST or stored profiles.
	 *
	 * @param mixed $config Raw config payload.
	 * @return array
	 */
	private function normalize_export_config( $config ) {
		$normalized                 = Export_Engine::normalize_export_config( $config );
		$normalized['format']       = $this->sanitize_default_format( $normalized['format'] );
		$normalized['cross_entity'] = $this->sanitize_cross_entity_config( isset( $config['cross_entity'] ) ? $config['cross_entity'] : array() );

		if ( isset( $config['calculated_fields'] ) && is_array( $config['calculated_fields'] ) ) {
			$normalized['calculated_fields'] = $this->normalize_calculated_fields( $config['calculated_fields'] );
		}

		return $normalized;
	}

	/**
	 * Normalize calculated field definitions for transport/storage.
	 *
	 * @param array $fields Raw calculated field payload.
	 * @return array
	 */
	private function normalize_calculated_fields( $fields ) {
		$normalized = array();

		foreach ( $fields as $field ) {
			if ( ! is_array( $field ) || empty( $field['key'] ) ) {
				continue;
			}

			$key = sanitize_key( $field['key'] );
			if ( '' === $key ) {
				continue;
			}

			$normalized[] = array(
				'key'     => $key,
				'label'   => isset( $field['label'] ) ? sanitize_text_field( $field['label'] ) : '',
				'formula' => isset( $field['formula'] ) ? sanitize_textarea_field( $field['formula'] ) : '',
			);
		}

		return $normalized;
	}

	/**
	 * Execute a normalized export configuration through the shared export path.
	 *
	 * @param array $config Export configuration.
	 * @return array|\WP_Error
	 */
	private function dispatch_export_config( $config ) {
		if ( null === $this->export_engine ) {
			return new \WP_Error(
				'export_unavailable',
				esc_html__( 'Export engine is not available.', 'rawtype-order-exporter-woocommerce' ),
				array( 'status' => 500 )
			);
		}

		$config = $this->normalize_export_config( $config );

		$counts = $this->export_engine->get_export_counts( $config );
		if ( is_wp_error( $counts ) ) {
			return $counts;
		}

		$total_count  = isset( $counts['total_rows'] ) ? absint( $counts['total_rows'] ) : 0;
		$source_count = isset( $counts['source_count'] ) ? absint( $counts['source_count'] ) : $total_count;

		$config['total_count']  = absint( $total_count );
		$config['source_count'] = absint( $source_count );

		$result = $this->export_engine->export( $config );
		if ( is_wp_error( $result ) ) {
			return $result;
		}

		unset( $result['file_path'] );
		$result['mode'] = 'sync';

		return $result;
	}

	/**
	 * Settings schema for route discovery.
	 *
	 * @return array
	 */
	public function get_settings_schema() {
		return array(
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
			'title'      => 'rawtype_oe_settings',
			'type'       => 'object',
			'properties' => array(
				'default_format' => array(
					'type' => 'string',
					'enum' => array( 'csv', 'xlsx' ),
				),
				'date_format'    => array(
					'type' => 'string',
					'enum' => array( 'Y-m-d', 'd/m/Y', 'm/d/Y' ),
				),
			),
		);
	}

	/**
	 * Columns schema for route discovery.
	 *
	 * @return array
	 */
	public function get_columns_schema() {
		return array(
			'$schema' => 'http://json-schema.org/draft-04/schema#',
			'title'   => 'rawtype_oe_columns',
			'type'    => 'array',
			'items'   => array(
				'type'       => 'object',
				'properties' => array(
					'key'   => array( 'type' => 'string' ),
					'label' => array( 'type' => 'string' ),
					'group' => array( 'type' => 'string' ),
				),
			),
		);
	}

	/**
	 * Get REST API args for settings validation.
	 *
	 * @return array
	 */
	private function get_settings_args() {
		$args = array(
			'default_format' => array(
				'type'              => 'string',
				'required'          => false,
				'enum'              => array( 'csv', 'xlsx' ),
				'sanitize_callback' => array( $this, 'sanitize_default_format' ),
			),
			'date_format'    => array(
				'type'              => 'string',
				'required'          => false,
				'enum'              => array( 'Y-m-d', 'd/m/Y', 'm/d/Y' ),
				'sanitize_callback' => array( $this, 'sanitize_date_format' ),
			),
		);

		/**
		 * Filter settings args to allow Pro extensions.
		 *
		 * @param array $args The REST args.
		 */
		return apply_filters( 'rawtype_oe_rest_settings_args', $args );
	}

	/**
	 * Sanitize default export format.
	 *
	 * @param string $value Raw value.
	 * @return string
	 */
	public function sanitize_default_format( $value ) {
		$value = sanitize_text_field( $value );
		return in_array( $value, array( 'csv', 'xlsx' ), true ) ? $value : 'csv';
	}

	/**
	 * Sanitize date format setting.
	 *
	 * @param string $value Raw value.
	 * @return string
	 */
	public function sanitize_date_format( $value ) {
		$value   = sanitize_text_field( $value );
		$allowed = array( 'Y-m-d', 'd/m/Y', 'm/d/Y' );
		return in_array( $value, $allowed, true ) ? $value : 'Y-m-d';
	}

	/**
	 * Sanitize cross-entity options payload.
	 *
	 * @param mixed $value Raw value.
	 * @return array
	 */
	private function sanitize_cross_entity_config( $value ) {
		if ( ! is_array( $value ) ) {
			return array();
		}

		$primary = isset( $value['primary_entity'] ) ? sanitize_key( $value['primary_entity'] ) : 'orders';
		$related = isset( $value['related_entities'] ) && is_array( $value['related_entities'] )
			? array_values( array_filter( array_map( 'sanitize_key', $value['related_entities'] ) ) )
			: array();

		return array(
			'primary_entity'   => $primary,
			'related_entities' => $related,
		);
	}

	/**
	 * Read settings merged with defaults.
	 *
	 * @return array
	 */
	private function get_saved_settings() {
		$defaults = Activator::get_default_settings();
		$settings = get_option( 'rawtype_oe_settings', array() );

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

		return wp_parse_args( $settings, $defaults );
	}
}
