<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

class DC_V2_REST {

	const NS = 'order-from-dm/v1';

	public static function init() {
		add_action( 'rest_api_init', array( __CLASS__, 'register_routes' ) );
	}

	public static function can_manage() {
		return current_user_can( 'manage_woocommerce' ) || current_user_can( 'manage_options' );
	}

	public static function register_routes() {
		$admin = array( 'permission_callback' => array( __CLASS__, 'can_manage' ) );

		register_rest_route( self::NS, '/admin/documents', array(
			array( 'methods' => 'GET', 'callback' => array( __CLASS__, 'list_documents' ) ) + $admin,
			array( 'methods' => 'POST', 'callback' => array( __CLASS__, 'save_document' ) ) + $admin,
		) );
		register_rest_route( self::NS, '/admin/dashboard', array(
			'methods'  => 'GET',
			'callback' => array( __CLASS__, 'dashboard' ),
		) + $admin );
		register_rest_route( self::NS, '/admin/documents/(?P<id>\d+)', array(
			array( 'methods' => 'GET', 'callback' => array( __CLASS__, 'get_document' ) ) + $admin,
			array(
				'methods'             => 'DELETE',
				'callback'            => array( __CLASS__, 'delete_document' ),
				'args'                => array(
					'force' => array(
						'type'    => 'boolean',
						'default' => false,
					),
				),
			) + $admin,
		) );
		register_rest_route( self::NS, '/admin/documents/(?P<id>\d+)/clone', array(
			'methods' => 'POST', 'callback' => array( __CLASS__, 'clone_document' ),
		) + $admin );
		register_rest_route( self::NS, '/admin/documents/(?P<id>\d+)/status', array(
			'methods' => 'POST', 'callback' => array( __CLASS__, 'set_document_status' ),
		) + $admin );

		register_rest_route( self::NS, '/admin/settings/last-copy', array(
			'methods' => 'GET', 'callback' => array( __CLASS__, 'get_last_copy' ),
		) + $admin );

		register_rest_route( self::NS, '/admin/products', array(
			'methods' => 'GET', 'callback' => array( __CLASS__, 'search_products' ),
		) + $admin );

		register_rest_route( self::NS, '/admin/field-templates', array(
			array( 'methods' => 'GET', 'callback' => array( __CLASS__, 'list_templates' ) ) + $admin,
			array( 'methods' => 'POST', 'callback' => array( __CLASS__, 'save_template' ) ) + $admin,
		) );

		register_rest_route( self::NS, '/admin/payment-methods', array(
			array( 'methods' => 'GET', 'callback' => array( __CLASS__, 'admin_payment_methods' ) ) + $admin,
			array( 'methods' => 'POST', 'callback' => array( __CLASS__, 'admin_save_bacs' ) ) + $admin,
		) );
		register_rest_route( self::NS, '/admin/settings/appearance', array(
			array( 'methods' => 'GET', 'callback' => array( __CLASS__, 'get_appearance' ) ) + $admin,
			array( 'methods' => 'POST', 'callback' => array( __CLASS__, 'save_appearance' ) ) + $admin,
		) );

		register_rest_route( self::NS, '/admin/feature-request', array(
			'methods' => 'POST', 'callback' => array( 'DC_V2_Feature_Request', 'send' ),
		) + $admin );

		register_rest_route( self::NS, '/admin/ai/generate-link', array(
			'methods' => 'POST', 'callback' => array( __CLASS__, 'ai_generate_link' ),
		) + $admin );

		register_rest_route( self::NS, '/admin/ai/resolve-link', array(
			'methods' => 'POST', 'callback' => array( __CLASS__, 'ai_resolve_link' ),
		) + $admin );

		register_rest_route( self::NS, '/admin/wc-orders/(?P<id>\d+)/preview', array(
			'methods' => 'GET', 'callback' => array( __CLASS__, 'get_wc_order_preview' ),
		) + $admin );

		register_rest_route( self::NS, '/document/(?P<slug>[^/]+)', array(
			'methods' => 'GET', 'callback' => array( __CLASS__, 'public_document' ),
			'permission_callback' => '__return_true',
		) );

		register_rest_route( self::NS, '/order-ticket', array(
			'methods'             => 'POST',
			'callback'            => array( __CLASS__, 'issue_order_ticket' ),
			'permission_callback' => '__return_true',
		) );
		register_rest_route( self::NS, '/orders', array(
			'methods' => 'POST', 'callback' => array( __CLASS__, 'create_order' ),
			'permission_callback' => '__return_true',
		) );
		register_rest_route( self::NS, '/orders/(?P<id>\d+)/payment-status', array(
			'methods'             => 'GET',
			'callback'            => array( __CLASS__, 'order_payment_status' ),
			'permission_callback' => '__return_true',
		) );
		register_rest_route( self::NS, '/orders/(?P<id>\d+)/stripe-confirm', array(
			'methods'             => 'POST',
			'callback'            => array( __CLASS__, 'order_stripe_confirm' ),
			'permission_callback' => '__return_true',
		) );

		register_rest_route( self::NS, '/regions', array(
			'methods' => 'GET', 'callback' => array( __CLASS__, 'get_regions' ),
			'permission_callback' => '__return_true',
		) );
		register_rest_route( self::NS, '/comunas', array(
			'methods' => 'GET', 'callback' => array( __CLASS__, 'get_comunas' ),
			'permission_callback' => '__return_true',
		) );
		register_rest_route( self::NS, '/shipping-config', array(
			'methods' => 'GET', 'callback' => array( __CLASS__, 'get_shipping_config' ),
			'permission_callback' => '__return_true',
		) );
		register_rest_route( self::NS, '/shipping-states', array(
			'methods' => 'GET', 'callback' => array( __CLASS__, 'get_shipping_states' ),
			'permission_callback' => '__return_true',
		) );
		register_rest_route( self::NS, '/shipping-localities', array(
			'methods' => 'GET', 'callback' => array( __CLASS__, 'get_shipping_localities' ),
			'permission_callback' => '__return_true',
		) );
	}

	public static function get_regions() {
		return rest_ensure_response( DC_V2_Shipping_Countries::get_states( 'CL' ) );
	}

	public static function get_comunas( WP_REST_Request $request ) {
		return rest_ensure_response(
			DC_V2_Shipping_Countries::get_localities( 'CL', sanitize_text_field( $request->get_param( 'region' ) ) )
		);
	}

	public static function get_shipping_config() {
		return rest_ensure_response( DC_V2_Shipping_Countries::get_config() );
	}

	public static function get_shipping_states( WP_REST_Request $request ) {
		return rest_ensure_response(
			DC_V2_Shipping_Countries::get_states( sanitize_text_field( $request->get_param( 'country' ) ) )
		);
	}

	public static function get_shipping_localities( WP_REST_Request $request ) {
		return rest_ensure_response(
			DC_V2_Shipping_Countries::get_localities(
				sanitize_text_field( $request->get_param( 'country' ) ),
				sanitize_text_field( $request->get_param( 'state' ) )
			)
		);
	}

	public static function get_appearance() {
		$global = get_option( 'dc_v2_appearance', dc_v2_default_appearance() );
		return rest_ensure_response( DC_V2_Document_CPT::normalize_appearance( $global ) );
	}

	public static function save_appearance( WP_REST_Request $request ) {
		$body = $request->get_json_params();
		$app  = DC_V2_Document_CPT::normalize_appearance( $body );
		update_option( 'dc_v2_appearance', $app );
		return rest_ensure_response( $app );
	}

	public static function list_templates( WP_REST_Request $request ) {
		if ( $request->get_param( 'recent' ) ) {
			return rest_ensure_response( DC_V2_Field_Template::list_recent( 20 ) );
		}
		return rest_ensure_response(
			DC_V2_Field_Template::list_templates(
				(int) $request->get_param( 'product_id' ),
				(int) $request->get_param( 'variation_id' )
			)
		);
	}

	public static function get_last_copy() {
		$copy = get_option( 'dc_v2_last_copy', dc_v2_default_copy() );
		return rest_ensure_response( DC_V2_Document_CPT::normalize_copy( $copy ) );
	}

	public static function set_document_status( WP_REST_Request $request ) {
		$id = (int) $request['id'];
		$post = get_post( $id );
		if ( ! $post || DC_V2_CPT !== $post->post_type ) {
			return new WP_Error( 'not_found', 'No encontrado.', array( 'status' => 404 ) );
		}
		$body   = $request->get_json_params();
		$status = in_array( $body['status'] ?? '', array( 'active', 'draft', 'trash' ), true ) ? $body['status'] : 'draft';
		if ( 'active' === $status ) {
			$guard = DC_V2_License::publish_guard();
			if ( is_wp_error( $guard ) ) {
				return $guard;
			}
		}
		update_post_meta( $id, '_dc_v2_status', $status );
		$p = self::document_payload( $id );
		return is_wp_error( $p ) ? $p : rest_ensure_response( $p );
	}

	public static function save_template( WP_REST_Request $request ) {
		$body = $request->get_json_params();
		$name = sanitize_text_field( $body['name'] ?? '' );
		if ( ! $name ) {
			return new WP_Error( 'missing_name', 'Nombre obligatorio.', array( 'status' => 400 ) );
		}
		$fields = self::sanitize_fields( $body['fields'] ?? array() );
		$id = DC_V2_Field_Template::save_template(
			$name,
			(int) ( $body['product_id'] ?? 0 ),
			(int) ( $body['variation_id'] ?? 0 ),
			$fields
		);
		if ( is_wp_error( $id ) ) {
			return $id;
		}
		return rest_ensure_response( DC_V2_Field_Template::get_template( $id ) );
	}

	public static function search_products( WP_REST_Request $request ) {
		$term = sanitize_text_field( $request->get_param( 'search' ) );
		if ( ! $term ) {
			return rest_ensure_response( array() );
		}

		$product_ids = array();

		if ( is_numeric( $term ) ) {
			$by_id = wc_get_product( (int) $term );
			if ( $by_id && 'publish' === $by_id->get_status() ) {
				$product_ids[] = $by_id->is_type( 'variation' ) ? $by_id->get_parent_id() : $by_id->get_id();
			}
		}

		$title_q = new WP_Query(
			array(
				'post_type'              => 'product',
				'post_status'            => 'publish',
				'posts_per_page'         => 25,
				'orderby'                => 'title',
				'order'                  => 'ASC',
				's'                      => $term,
				'fields'                 => 'ids',
				'no_found_rows'          => true,
				'update_post_meta_cache' => false,
				'update_post_term_cache' => false,
			)
		);
		$product_ids = array_merge( $product_ids, $title_q->posts );

		$sku_q = new WP_Query(
			array(
				'post_type'              => array( 'product', 'product_variation' ),
				'post_status'            => 'publish',
				'posts_per_page'         => 25,
				'meta_query'             => array(
					array(
						'key'     => '_sku',
						'value'   => $term,
						'compare' => 'LIKE',
					),
				),
				'fields'                 => 'ids',
				'no_found_rows'          => true,
				'update_post_meta_cache' => false,
				'update_post_term_cache' => false,
			)
		);
		foreach ( $sku_q->posts as $post_id ) {
			$match = wc_get_product( $post_id );
			if ( ! $match || 'publish' !== $match->get_status() ) {
				continue;
			}
			$product_ids[] = $match->is_type( 'variation' ) ? $match->get_parent_id() : $match->get_id();
		}

		$product_ids = array_values( array_unique( array_filter( $product_ids ) ) );
		$product_ids = array_slice( $product_ids, 0, 25 );

		$out = array();
		foreach ( $product_ids as $product_id ) {
			$p = wc_get_product( $product_id );
			if ( ! $p || 'publish' !== $p->get_status() || $p->is_type( 'variation' ) ) {
				continue;
			}
			if ( $p->is_type( 'variable' ) ) {
				$item = self::product_row( $p );
				$item['type'] = 'variable';
				$item['attribute_axes'] = self::variable_attribute_axes( $p );
				$item = array_merge( $item, self::variable_picker_meta( $p ) );
				$item['variations'] = array();
				foreach ( $p->get_children() as $vid ) {
					$v = wc_get_product( $vid );
					if ( $v && 'publish' === $v->get_status() ) {
						$item['variations'][] = self::product_row( $v );
					}
				}
				$out[] = $item;
			} else {
				$row = self::product_row( $p );
				$row['type'] = 'simple';
				$out[] = $row;
			}
		}
		return rest_ensure_response( $out );
	}

	private static function variable_attribute_axes( WC_Product_Variable $product ) {
		$axes = array();
		foreach ( $product->get_variation_attributes() as $label => $options ) {
			$axes[] = array(
				'key'     => 'attribute_' . sanitize_title( $label ),
				'label'   => $label,
				'options' => array_values( array_filter( (array) $options ) ),
			);
		}
		return $axes;
	}

	private static function variable_picker_meta( WC_Product_Variable $product ) {
		$axes            = self::variable_attribute_axes( $product );
		$all_keys        = array_column( $axes, 'key' );
		$constrained     = array();
		$published_count = 0;

		foreach ( $product->get_children() as $vid ) {
			$v = wc_get_product( $vid );
			if ( ! $v || 'publish' !== $v->get_status() ) {
				continue;
			}
			$published_count++;
			foreach ( $v->get_variation_attributes() as $key => $val ) {
				if ( '' !== $val && null !== $val ) {
					$constrained[ $key ] = true;
				}
			}
		}

		$required_keys = array_values( array_intersect( $all_keys, array_keys( $constrained ) ) );
		$optional_keys = array_values( array_diff( $all_keys, $required_keys ) );

		$picker_mode = 'full';
		if ( $published_count <= 1 && empty( $required_keys ) ) {
			$picker_mode = 'direct';
		} elseif ( 1 === count( $required_keys ) ) {
			$picker_mode = 'single_axis';
		}

		return array(
			'picker_mode'   => $picker_mode,
			'required_axes' => $required_keys,
			'optional_axes' => $optional_keys,
		);
	}

	private static function product_row( WC_Product $p ) {
		$image_id  = $p->get_image_id();
		$image_url = $image_id ? wp_get_attachment_image_url( $image_id, 'thumbnail' ) : wc_placeholder_img_src();
		$row = array(
			'id'    => $p->get_id(),
			'name'  => $p->get_name(),
			'sku'   => $p->get_sku(),
			'price' => $p->get_price(),
			'image' => $image_url,
		);
		if ( $p->is_type( 'variation' ) ) {
			$row['parent_id'] = $p->get_parent_id();
			$row['attributes'] = $p->get_variation_attributes();
		}
		return $row;
	}

	private static function post_iso_date( $mysql_gmt ) {
		if ( ! $mysql_gmt || '0000-00-00 00:00:00' === $mysql_gmt ) {
			return '';
		}
		$ts = strtotime( $mysql_gmt . ' UTC' );
		return $ts ? gmdate( 'c', $ts ) : '';
	}

	public static function document_payload( $id, $wc_orders_map = null ) {
		$post = get_post( $id );
		if ( ! $post || DC_V2_CPT !== $post->post_type ) {
			return new WP_Error( 'not_found', 'Documento no encontrado.', array( 'status' => 404 ) );
		}
		$meta = DC_V2_Document_CPT::get_meta( $id );
		$global_appearance = DC_V2_Document_CPT::normalize_appearance(
			get_option( 'dc_v2_appearance', dc_v2_default_appearance() )
		);
		$lines_out = array();
		foreach ( $meta['lines'] as $line ) {
			$payload = DC_V2_Document_CPT::line_product_payload( $line );
			if ( $payload ) {
				$pid = (int) ( $line['product_id'] ?? 0 );
				$fields = is_array( $line['fields'] ?? null ) ? $line['fields'] : array();
				if ( class_exists( 'DC_V2_Product_Fields', false ) && $pid ) {
					$fields = DC_V2_Product_Fields::merge_into_line_fields( $fields, $pid );
				}
				$has_product_fields = class_exists( 'DC_V2_Product_Fields', false ) && $pid && DC_V2_Product_Fields::get_fields( $pid );
				$payload['skip_fields'] = ! empty( $line['skip_fields'] ) && ! $has_product_fields;
				$payload['fields'] = $payload['skip_fields'] ? array() : $fields;
				$lines_out[] = $payload;
			}
		}
		$wc_orders = array();
		if ( is_array( $wc_orders_map ) && isset( $wc_orders_map[ (int) $id ] ) ) {
			$wc_orders = $wc_orders_map[ (int) $id ];
		} elseif ( null === $wc_orders_map ) {
			$wc_orders = DC_V2_Order::linked_orders_for_document( (int) $id );
		}
		return array(
			'id'               => (int) $id,
			'title'            => $post->post_title,
			'slug'             => $meta['slug'],
			'status'           => $meta['status'],
			'copy'             => $meta['copy'],
			'appearance'       => $global_appearance,
			'lines'            => $lines_out,
			'payment_methods'  => $meta['payment_methods'],
			'url'              => $meta['slug'] ? DC_V2_Document_CPT::public_url( $meta['slug'] ) : '',
			'created_at' => self::post_iso_date( $post->post_date_gmt ),
			'modified_at'=> self::post_iso_date( $post->post_modified_gmt ) ?: self::post_iso_date( $post->post_date_gmt ),
			'wc_orders'  => $wc_orders,
		);
	}

	public static function ai_generate_link( WP_REST_Request $request ) {
		if ( ! class_exists( 'DC_V2_AI_Generate_Link', false ) ) {
			return new WP_Error( 'ai_unavailable', __( 'AI link generation is not available.', 'order-from-dm' ), array( 'status' => 503 ) );
		}
		$body = $request->get_json_params();
		return DC_V2_AI_Generate_Link::generate(
			(string) ( $body['prompt'] ?? '' ),
			! empty( $body['autoPublish'] )
		);
	}

	public static function ai_resolve_link( WP_REST_Request $request ) {
		if ( ! class_exists( 'DC_V2_AI_Generate_Link', false ) ) {
			return new WP_Error( 'ai_unavailable', __( 'AI link generation is not available.', 'order-from-dm' ), array( 'status' => 503 ) );
		}
		$body   = $request->get_json_params();
		$choice = (string) ( $body['choice'] ?? '' );
		if ( ! in_array( $choice, array( 'reuse', 'new', 'cancel' ), true ) ) {
			return new WP_Error( 'bad_choice', __( 'Pick a link or create a new one.', 'order-from-dm' ), array( 'status' => 400 ) );
		}
		return DC_V2_AI_Generate_Link::resolve(
			(string) ( $body['token'] ?? '' ),
			$choice,
			(int) ( $body['documentId'] ?? 0 )
		);
	}

	public static function get_wc_order_preview( WP_REST_Request $request ) {
		$html = DC_V2_Order::get_preview_html( (int) $request['id'] );
		if ( is_wp_error( $html ) ) {
			return $html;
		}
		return rest_ensure_response( array( 'html' => $html ) );
	}

	/**
	 * Dashboard aggregate: every OFDM Woo order across all links.
	 */
	public static function dashboard() {
		return rest_ensure_response(
			array(
				'orders' => DC_V2_Order::all_linked_order_payloads( 2000 ),
			)
		);
	}

	public static function list_documents() {
		$q = new WP_Query( array(
			'post_type' => DC_V2_CPT, 'post_status' => 'publish',
			'posts_per_page' => 500, 'orderby' => 'date', 'order' => 'DESC',
		) );
		$out = array();
		$ids = array_map(
			static function ( $post ) {
				return (int) $post->ID;
			},
			$q->posts
		);
		$wc_orders_map = DC_V2_Order::linked_orders_map_for_documents( $ids );
		foreach ( $q->posts as $post ) {
			$p = self::document_payload( $post->ID, $wc_orders_map );
			if ( ! is_wp_error( $p ) ) {
				$out[] = $p;
			}
		}
		return rest_ensure_response( $out );
	}

	public static function get_document( WP_REST_Request $request ) {
		$p = self::document_payload( (int) $request['id'] );
		return is_wp_error( $p ) ? $p : rest_ensure_response( $p );
	}

	public static function save_document( WP_REST_Request $request ) {
		$body  = $request->get_json_params();
		$title = sanitize_text_field( $body['title'] ?? '' );
		if ( ! $title ) {
			return new WP_Error( 'missing_title', 'Título obligatorio.', array( 'status' => 400 ) );
		}
		$lines = self::sanitize_lines( $body['lines'] ?? array() );
		if ( ! $lines ) {
			return new WP_Error( 'missing_lines', 'Agrega al menos un producto.', array( 'status' => 400 ) );
		}
		$status  = in_array( $body['status'] ?? '', array( 'active', 'draft' ), true ) ? $body['status'] : 'draft';
		if ( 'active' === $status ) {
			$guard = DC_V2_License::publish_guard();
			if ( is_wp_error( $guard ) ) {
				return $guard;
			}
			foreach ( $lines as $line ) {
				if ( empty( $line['skip_fields'] ) && empty( $line['fields'] ) ) {
					return new WP_Error(
						'missing_fields',
						'Cada producto debe tener campos configurados o marcar "Sin personalización".',
						array( 'status' => 400 )
					);
				}
			}
		}
		$id = (int) ( $body['id'] ?? 0 );
		if ( $id ) {
			$post = get_post( $id );
			if ( ! $post || DC_V2_CPT !== $post->post_type ) {
				return new WP_Error( 'not_found', 'No encontrado.', array( 'status' => 404 ) );
			}
			wp_update_post( array( 'ID' => $id, 'post_title' => $title ) );
			$slug = get_post_meta( $id, '_dc_v2_slug', true );
		} else {
			$id = wp_insert_post( array(
				'post_type' => DC_V2_CPT, 'post_title' => $title, 'post_status' => 'publish',
			), true );
			if ( is_wp_error( $id ) ) {
				return $id;
			}
			$slug = '';
		}
		$slug_in = sanitize_title( $body['slug'] ?? '' );
		$slug = $slug_in ? DC_V2_Document_CPT::unique_slug( $slug_in, $id ) : ( $slug ?: DC_V2_Document_CPT::unique_slug( $title, $id ) );

		foreach ( $lines as $line ) {
			if ( empty( $line['skip_fields'] ) && ! empty( $line['fields'] ) && ! empty( $body['save_templates'] ) ) {
				$tpl_name = sanitize_text_field( $line['template_name'] ?? '' );
				if ( $tpl_name ) {
					DC_V2_Field_Template::save_template(
						$tpl_name,
						(int) $line['product_id'],
						(int) ( $line['variation_id'] ?? 0 ),
						$line['fields']
					);
				}
			}
		}

		DC_V2_Document_CPT::save_meta( $id, array(
			'slug'             => $slug,
			'status'           => $status,
			'copy'             => DC_V2_Document_CPT::normalize_copy( $body['copy'] ?? array() ),
			'appearance'       => DC_V2_Document_CPT::normalize_appearance( $body['appearance'] ?? array() ),
			'lines'            => $lines,
			'payment_methods'  => $body['payment_methods'] ?? array(),
		) );
		update_option( 'dc_v2_last_copy', DC_V2_Document_CPT::normalize_copy( $body['copy'] ?? array() ) );

		$p = self::document_payload( $id );
		return is_wp_error( $p ) ? $p : rest_ensure_response( $p );
	}

	public static function delete_document( WP_REST_Request $request ) {
		$id = (int) $request['id'];
		$post = get_post( $id );
		if ( ! $post || DC_V2_CPT !== $post->post_type ) {
			return new WP_Error( 'not_found', 'No encontrado.', array( 'status' => 404 ) );
		}
		$force = rest_sanitize_boolean( $request->get_param( 'force' ) );
		if ( ! $force ) {
			update_post_meta( $id, '_dc_v2_status', 'trash' );
			$p = self::document_payload( $id );
			return is_wp_error( $p ) ? $p : rest_ensure_response( array( 'trashed' => true, 'document' => $p ) );
		}
		$orders = DC_V2_Order::linked_orders_for_document( $id );
		if ( ! empty( $orders ) ) {
			return new WP_Error(
				'has_orders',
				__( 'Links with completed purchases cannot be permanently deleted.', 'order-from-dm' ),
				array( 'status' => 409 )
			);
		}
		wp_delete_post( $id, true );
		return rest_ensure_response( array( 'deleted' => true ) );
	}

	public static function clone_document( WP_REST_Request $request ) {
		$id = (int) $request['id'];
		$post = get_post( $id );
		if ( ! $post || DC_V2_CPT !== $post->post_type ) {
			return new WP_Error( 'not_found', 'No encontrado.', array( 'status' => 404 ) );
		}
		$meta = DC_V2_Document_CPT::get_meta( $id );
		$new_id = wp_insert_post( array(
			'post_type' => DC_V2_CPT,
			'post_title' => $post->post_title . ' (copia)',
			'post_status' => 'publish',
		), true );
		if ( is_wp_error( $new_id ) ) {
			return $new_id;
		}
		$slug = DC_V2_Document_CPT::unique_slug( $post->post_title . '-copia', $new_id );
		$meta['status'] = 'draft';
		$meta['slug'] = $slug;
		DC_V2_Document_CPT::save_meta( $new_id, $meta );
		$p = self::document_payload( $new_id );
		return is_wp_error( $p ) ? $p : rest_ensure_response( $p );
	}

	public static function sanitize_lines( $lines ) {
		if ( ! is_array( $lines ) ) {
			return array();
		}
		$out = array();
		foreach ( $lines as $line ) {
			$pid = (int) ( $line['product_id'] ?? 0 );
			if ( ! $pid || ! wc_get_product( $pid ) ) {
				continue;
			}
			$product_fields = class_exists( 'DC_V2_Product_Fields', false )
				? DC_V2_Product_Fields::get_fields( $pid )
				: array();
			$skip = ! empty( $line['skip_fields'] ) && ! $product_fields;
			$fields = array();
			if ( ! $skip ) {
				$fields = self::sanitize_fields( $line['fields'] ?? array() );
				if ( class_exists( 'DC_V2_Product_Fields', false ) ) {
					$fields = DC_V2_Product_Fields::merge_into_line_fields( $fields, $pid );
				}
			}
			$out[] = array(
				'product_id'          => $pid,
				'variation_id'        => (int) ( $line['variation_id'] ?? 0 ),
				'quantity'            => max( 1, (int) ( $line['quantity'] ?? 1 ) ),
				'skip_fields'         => $skip,
				'fields'              => $fields,
				'selected_attributes' => self::sanitize_selected_attributes( $line['selected_attributes'] ?? array() ),
			);
		}
		return $out;
	}

	public static function sanitize_selected_attributes( $attrs ) {
		if ( ! is_array( $attrs ) ) {
			return array();
		}
		$out = array();
		foreach ( $attrs as $label => $value ) {
			$label = sanitize_text_field( $label );
			$value = sanitize_text_field( $value );
			if ( $label && $value ) {
				$out[ $label ] = $value;
			}
		}
		return $out;
	}

	public static function sanitize_fields( $fields ) {
		if ( ! is_array( $fields ) ) {
			return array();
		}
		$allowed = array( 'text', 'textarea', 'dropdown', 'radio', 'image' );
		$out = array();
		foreach ( $fields as $f ) {
			$type = sanitize_key( $f['type'] ?? 'text' );
			if ( ! in_array( $type, $allowed, true ) ) {
				continue;
			}
			$key = sanitize_key( $f['key'] ?? $f['id'] ?? '' );
			if ( ! $key ) {
				$key = 'fld_' . wp_generate_password( 8, false );
			}
			$item = array(
				'key'         => $key,
				'type'        => $type,
				'label'       => sanitize_text_field( $f['label'] ?? '' ),
				'placeholder' => sanitize_text_field( $f['placeholder'] ?? '' ),
				'required'    => 'image' === $type ? true : ! empty( $f['required'] ),
				'options'     => array(),
				'origin'      => ( isset( $f['origin'] ) && 'product' === $f['origin'] ) ? 'product' : 'link',
				'locked'      => false,
			);
			$item['locked'] = ! empty( $f['locked'] ) || 'product' === $item['origin'];
			if ( in_array( $type, array( 'dropdown', 'radio' ), true ) && ! empty( $f['options'] ) ) {
				foreach ( (array) $f['options'] as $opt ) {
					$opt = sanitize_text_field( $opt );
					if ( $opt !== '' ) {
						$item['options'][] = $opt;
					}
				}
			}
			$out[] = $item;
		}
		return $out;
	}

	public static function public_document( WP_REST_Request $request ) {
		$post = DC_V2_Document_CPT::find_by_slug( sanitize_title( $request['slug'] ) );
		if ( ! $post ) {
			return new WP_Error( 'not_found', 'No encontrado.', array( 'status' => 404 ) );
		}
		$meta = DC_V2_Document_CPT::get_meta( $post->ID );
		if ( 'active' !== $meta['status'] && ! $request->get_param( 'preview' ) ) {
			return new WP_Error( 'inactive', 'Inactivo.', array( 'status' => 400 ) );
		}
		$appearance = DC_V2_Document_CPT::normalize_appearance(
			get_option( 'dc_v2_appearance', dc_v2_default_appearance() )
		);
		$lines = array();
		foreach ( $meta['lines'] as $line ) {
			$p = DC_V2_Document_CPT::line_product_payload( $line );
			if ( $p ) {
				$pid = (int) ( $line['product_id'] ?? 0 );
				$fields = is_array( $line['fields'] ?? null ) ? $line['fields'] : array();
				if ( class_exists( 'DC_V2_Product_Fields', false ) && $pid ) {
					$fields = DC_V2_Product_Fields::merge_into_line_fields( $fields, $pid );
				}
				$has_product_fields = class_exists( 'DC_V2_Product_Fields', false ) && $pid && DC_V2_Product_Fields::get_fields( $pid );
				$p['skip_fields'] = ! empty( $line['skip_fields'] ) && ! $has_product_fields;
				$p['fields'] = $p['skip_fields'] ? array() : $fields;
				$lines[] = $p;
			}
		}
		return rest_ensure_response( array(
			'slug'             => $meta['slug'],
			'copy'             => $meta['copy'],
			'appearance'       => $appearance,
			'lines'            => $lines,
			'payment_methods'  => DC_V2_Payment_Methods::public_methods( $meta['payment_methods'] ),
		) );
	}

	public static function admin_payment_methods() {
		return rest_ensure_response( array(
			'methods' => DC_V2_Payment_Methods::list_available(),
		) );
	}

	public static function admin_save_bacs( WP_REST_Request $request ) {
		$body = $request->get_json_params();
		$ok   = DC_V2_Payment_Methods::save_bacs_account(
			is_array( $body['account'] ?? null ) ? $body['account'] : array(),
			(string) ( $body['instructions'] ?? '' )
		);
		if ( is_wp_error( $ok ) ) {
			return $ok;
		}
		return rest_ensure_response( array(
			'saved'   => true,
			'methods' => DC_V2_Payment_Methods::list_available(),
		) );
	}

	public static function issue_order_ticket( WP_REST_Request $request ) {
		$body = $request->get_json_params();
		$slug = sanitize_title( (string) ( $body['slug'] ?? '' ) );
		$post = DC_V2_Document_CPT::find_by_slug( $slug );
		if ( ! $post ) {
			return new WP_Error( 'not_found', 'Pedido no encontrado.', array( 'status' => 404 ) );
		}
		$meta = DC_V2_Document_CPT::get_meta( $post->ID );
		if ( 'active' !== $meta['status'] ) {
			return new WP_Error( 'inactive', 'Pedido inactivo.', array( 'status' => 400 ) );
		}

		$ent = DC_V2_License::entitlement();
		if ( empty( $ent['allowed'] ) ) {
			return new WP_Error(
				'limit_reached',
				__( 'This store reached its free plan limit. Upgrade to Order From DM Pro to keep receiving orders.', 'order-from-dm' ),
				array( 'status' => 402, 'upgrade_url' => $ent['upgrade_url'] )
			);
		}

		if ( ! DC_V2_Order_Ticket::required() ) {
			return rest_ensure_response(
				array(
					'ok'         => true,
					'ticket'     => '',
					'expires_at' => 0,
					'skipped'    => true,
				)
			);
		}

		$issued = DC_V2_Order_Ticket::issue( $slug );
		if ( is_wp_error( $issued ) ) {
			return $issued;
		}

		return rest_ensure_response(
			array(
				'ok'         => true,
				'ticket'     => $issued['ticket'],
				'expires_at' => $issued['expires_at'],
			)
		);
	}

	public static function create_order( WP_REST_Request $request ) {
		$body = $request->get_json_params();
		// Canonical checkout slug is _dc_v2_slug (URL /cfg.slug), not WP post_name.
		$slug = sanitize_title( (string) ( $body['slug'] ?? '' ) );
		$post = DC_V2_Document_CPT::find_by_slug( $slug );
		if ( ! $post ) {
			return new WP_Error( 'not_found', 'Pedido no encontrado.', array( 'status' => 404 ) );
		}
		if ( $slug === '' ) {
			$slug = sanitize_title( (string) ( get_post_meta( $post->ID, '_dc_v2_slug', true ) ?: '' ) );
		}
		$meta = DC_V2_Document_CPT::get_meta( $post->ID );
		if ( 'active' !== $meta['status'] ) {
			return new WP_Error( 'inactive', 'Pedido inactivo.', array( 'status' => 400 ) );
		}

		$ent = DC_V2_License::entitlement();
		if ( empty( $ent['allowed'] ) ) {
			return new WP_Error(
				'limit_reached',
				__( 'This store reached its free plan limit. Upgrade to Order From DM Pro to keep receiving orders.', 'order-from-dm' ),
				array( 'status' => 402, 'upgrade_url' => $ent['upgrade_url'] )
			);
		}

		if ( DC_V2_Order_Ticket::required() ) {
			$ticket_ok = DC_V2_Order_Ticket::consume( $slug, (string) ( $body['order_ticket'] ?? '' ) );
			if ( is_wp_error( $ticket_ok ) ) {
				return $ticket_ok;
			}
		}

		$line_values_in = $body['line_values'] ?? array();
		$line_values = array();
		foreach ( $meta['lines'] as $li => $line ) {
			$qty = max( 1, (int) ( $line['quantity'] ?? 1 ) );
			$line_values[ $li ] = array();
			if ( ! empty( $line['skip_fields'] ) ) {
				for ( $u = 0; $u < $qty; $u++ ) {
					$line_values[ $li ][ $u ] = array();
				}
				continue;
			}
			$fields = $line['fields'] ?? array();
			for ( $u = 0; $u < $qty; $u++ ) {
				$raw = $line_values_in[ $li ][ $u ] ?? $line_values_in[ $li ] ?? array();
				$clean = array();
				foreach ( $fields as $field ) {
					$key = $field['key'] ?? '';
					$label = $field['label'] ?? $key;
					$val = $raw[ $key ] ?? null;
					if ( 'image' === $field['type'] ) {
						if ( empty( $val ) ) {
							return new WP_Error( 'validation', sprintf( 'Falta imagen en %s.', $label ), array( 'status' => 400 ) );
						}
						$up = DC_V2_Order::upload_image_data( $val, 'line-' . $li );
						if ( is_wp_error( $up ) ) {
							return $up;
						}
						$clean[ $label ] = $up;
					} else {
						$str = is_string( $val ) ? trim( $val ) : '';
						if ( ! empty( $field['required'] ) && $str === '' ) {
							return new WP_Error( 'validation', sprintf( 'Campo obligatorio: %s', $label ), array( 'status' => 400 ) );
						}
						$clean[ $label ] = sanitize_text_field( $str );
					}
				}
				$line_values[ $li ][ $u ] = $clean;
			}
		}

		$shipping = DC_V2_Shipping_Countries::normalize_input( $body['shipping'] ?? array() );
		$valid    = DC_V2_Shipping_Countries::validate( $shipping );
		if ( is_wp_error( $valid ) ) {
			return $valid;
		}

		$payment_method = DC_V2_Payment_Methods::validate_choice(
			$body['payment_method'] ?? '',
			$meta['payment_methods']
		);
		if ( is_wp_error( $payment_method ) ) {
			return $payment_method;
		}

		$result = DC_V2_Order::create_from_payload( array(
			'document_id'    => $post->ID,
			'slug'           => $slug,
			'lines'          => $meta['lines'],
			'line_values'    => $line_values,
			'shipping'       => $shipping,
			'payment_method' => $payment_method,
		) );
		if ( is_wp_error( $result ) ) {
			return $result;
		}
		DC_V2_License::consume();
		return rest_ensure_response( $result );
	}

	/**
	 * Public payment status after returning from Stripe / PayPal / Mercado Pago.
	 */
	public static function order_payment_status( WP_REST_Request $request ) {
		$key = (string) $request->get_param( 'key' );
		if ( ! $key ) {
			return new WP_Error( 'forbidden', 'Missing order key.', array( 'status' => 403 ) );
		}
		$result = DC_V2_Order::public_payment_status( (int) $request['id'], $key );
		if ( is_wp_error( $result ) ) {
			return $result;
		}
		return rest_ensure_response( $result );
	}

	/**
	 * Confirm in-chat Stripe Payment Element after client-side confirmPayment.
	 */
	public static function order_stripe_confirm( WP_REST_Request $request ) {
		$body = $request->get_json_params();
		$key  = (string) ( $body['key'] ?? $request->get_param( 'key' ) );
		if ( ! $key ) {
			return new WP_Error( 'forbidden', 'Missing order key.', array( 'status' => 403 ) );
		}
		$result = DC_V2_Payment_Methods::confirm_stripe_elements_payment( (int) $request['id'], $key );
		if ( is_wp_error( $result ) ) {
			return $result;
		}
		return rest_ensure_response( $result );
	}
}
