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

/**
 * Product-level OFDM fields (WooCommerce product data tab) + remote editor loader.
 */
class DC_V2_Product_Fields {

	const META_KEY = '_dc_v2_product_fields';

	public static function init() {
		add_filter( 'woocommerce_product_data_tabs', array( __CLASS__, 'product_tab' ) );
		add_action( 'woocommerce_product_data_panels', array( __CLASS__, 'product_panel' ) );
		add_action( 'woocommerce_process_product_meta', array( __CLASS__, 'save_product_meta' ) );
		add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_product_assets' ) );
		add_action( 'rest_api_init', array( __CLASS__, 'register_routes' ) );
		add_action( 'wp_ajax_dc_v2_get_product_fields', array( __CLASS__, 'ajax_get' ) );
		add_action( 'wp_ajax_dc_v2_save_product_fields', array( __CLASS__, 'ajax_save' ) );
		add_action( 'woocommerce_before_add_to_cart_button', array( __CLASS__, 'render_storefront_fields' ), 12 );
		add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_storefront_assets' ) );
		add_filter( 'woocommerce_add_to_cart_validation', array( __CLASS__, 'validate_storefront_fields' ), 10, 3 );
		add_filter( 'woocommerce_add_cart_item_data', array( __CLASS__, 'add_cart_item_data' ), 10, 3 );
		add_filter( 'woocommerce_get_item_data', array( __CLASS__, 'display_cart_item_data' ), 10, 2 );
		add_action( 'woocommerce_checkout_create_order_line_item', array( __CLASS__, 'add_order_item_meta' ), 10, 4 );
		add_filter( 'woocommerce_get_cart_item_from_session', array( __CLASS__, 'hydrate_cart_item_from_session' ), 10, 2 );
		add_filter( 'woocommerce_order_item_display_meta_value', array( __CLASS__, 'display_order_item_meta_value' ), 10, 3 );
		add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_cart_display_assets' ) );
	}

	public static function register_routes() {
		$admin = array( 'permission_callback' => array( 'DC_V2_REST', 'can_manage' ) );
		register_rest_route(
			DC_V2_REST::NS,
			'/admin/product-fields/(?P<id>\d+)',
			array(
				array(
					'methods'  => 'GET',
					'callback' => array( __CLASS__, 'rest_get' ),
				) + $admin,
				array(
					'methods'  => 'POST',
					'callback' => array( __CLASS__, 'rest_save' ),
				) + $admin,
			)
		);
	}

	public static function get_fields( $product_id ) {
		$product_id = (int) $product_id;
		if ( $product_id <= 0 ) {
			return array();
		}
		$raw = get_post_meta( $product_id, self::META_KEY, true );
		if ( ! is_array( $raw ) ) {
			return array();
		}
		$fields = DC_V2_REST::sanitize_fields( $raw );
		foreach ( $fields as &$f ) {
			$f['origin'] = 'product';
			$f['locked'] = true;
		}
		unset( $f );
		return $fields;
	}

	public static function save_fields( $product_id, $fields ) {
		$product_id = (int) $product_id;
		if ( $product_id <= 0 ) {
			return new WP_Error( 'invalid_product', 'Invalid product', array( 'status' => 400 ) );
		}
		$clean = DC_V2_REST::sanitize_fields( $fields );
		// Persist without locked flags; origin is implied by product meta.
		$store = array();
		foreach ( $clean as $f ) {
			unset( $f['locked'], $f['origin'] );
			$store[] = $f;
		}
		update_post_meta( $product_id, self::META_KEY, $store );
		return $store;
	}

	public static function rest_get( WP_REST_Request $request ) {
		$id = (int) $request['id'];
		return rest_ensure_response(
			array(
				'product_id' => $id,
				'fields'     => self::get_fields( $id ),
			)
		);
	}

	public static function rest_save( WP_REST_Request $request ) {
		$id     = (int) $request['id'];
		$body   = $request->get_json_params();
		$fields = is_array( $body['fields'] ?? null ) ? $body['fields'] : array();
		$saved  = self::save_fields( $id, $fields );
		if ( is_wp_error( $saved ) ) {
			return $saved;
		}
		return rest_ensure_response(
			array(
				'product_id' => $id,
				'fields'     => self::get_fields( $id ),
			)
		);
	}

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

	public static function ajax_get() {
		if ( ! self::ajax_can_manage() ) {
			wp_send_json_error( array( 'message' => 'forbidden' ), 403 );
		}
		check_ajax_referer( 'dc_v2_product_fields', 'nonce' );
		$id = isset( $_POST['product_id'] ) ? (int) $_POST['product_id'] : 0;
		if ( $id <= 0 ) {
			wp_send_json_error( array( 'message' => 'invalid_product' ), 400 );
		}
		wp_send_json_success(
			array(
				'product_id' => $id,
				'fields'     => self::get_fields( $id ),
			)
		);
	}

	public static function ajax_save() {
		if ( ! self::ajax_can_manage() ) {
			wp_send_json_error( array( 'message' => 'forbidden' ), 403 );
		}
		check_ajax_referer( 'dc_v2_product_fields', 'nonce' );
		$id = isset( $_POST['product_id'] ) ? (int) $_POST['product_id'] : 0;
		if ( $id <= 0 ) {
			wp_send_json_error( array( 'message' => 'invalid_product' ), 400 );
		}
		$raw = isset( $_POST['fields'] ) ? wp_unslash( $_POST['fields'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		if ( is_string( $raw ) ) {
			$decoded = json_decode( $raw, true );
		} elseif ( is_array( $raw ) ) {
			$decoded = $raw;
		} else {
			$decoded = array();
		}
		if ( ! is_array( $decoded ) ) {
			wp_send_json_error( array( 'message' => 'invalid_fields' ), 400 );
		}
		$saved = self::save_fields( $id, $decoded );
		if ( is_wp_error( $saved ) ) {
			wp_send_json_error( array( 'message' => $saved->get_error_message() ), 400 );
		}
		wp_send_json_success(
			array(
				'product_id' => $id,
				'fields'     => self::get_fields( $id ),
			)
		);
	}

	public static function product_tab( $tabs ) {
		$tabs['dc_v2_ofdm_fields'] = array(
			'label'    => __( 'OFDM Fields', 'order-from-dm' ),
			'target'   => 'dc_v2_ofdm_fields_data',
			'class'    => array( 'dc_v2_ofdm_fields_tab' ),
			'priority' => 75,
		);
		return $tabs;
	}

	public static function product_panel() {
		global $post;
		$product_id = $post ? (int) $post->ID : 0;
		$existing   = self::get_fields( $product_id );
		$store      = array();
		foreach ( $existing as $f ) {
			unset( $f['locked'], $f['origin'] );
			$store[] = $f;
		}
		?>
		<div id="dc_v2_ofdm_fields_data" class="panel woocommerce_options_panel hidden">
			<div class="dc-v2-product-fields-panel" data-product-id="<?php echo esc_attr( (string) $product_id ); ?>">
				<input
					type="hidden"
					id="dc_v2_product_fields_json"
					name="_dc_v2_product_fields_json"
					value="<?php echo esc_attr( wp_json_encode( $store ) ); ?>"
				/>
				<div id="dc-v2-product-fields-root"></div>
				<p class="dc-v2-product-fields-status" id="dc-v2-product-fields-status" hidden></p>
			</div>
		</div>
		<?php
	}

	public static function save_product_meta( $product_id ) {
		$product_id = (int) $product_id;
		if ( $product_id <= 0 ) {
			return;
		}
		if ( ! isset( $_POST['_dc_v2_product_fields_json'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- WC product save already verifies.
			return;
		}
		$raw = wp_unslash( $_POST['_dc_v2_product_fields_json'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		if ( ! is_string( $raw ) || $raw === '' ) {
			return;
		}
		$decoded = json_decode( $raw, true );
		if ( ! is_array( $decoded ) ) {
			return;
		}
		self::save_fields( $product_id, $decoded );
	}

	public static function enqueue_product_assets( $hook ) {
		if ( ! in_array( $hook, array( 'post.php', 'post-new.php' ), true ) ) {
			return;
		}
		$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
		if ( ! $screen || 'product' !== $screen->post_type ) {
			return;
		}
		if ( ! current_user_can( 'manage_woocommerce' ) ) {
			return;
		}

		$runtime = DC_V2_Fields_Editor_Runtime::resolve();
		wp_register_style( 'dc-v2-product-fields-tab', false, array(), DC_V2_VERSION );
		wp_enqueue_style( 'dc-v2-product-fields-tab' );
		wp_add_inline_style(
			'dc-v2-product-fields-tab',
			'#woocommerce-product-data ul.wc-tabs li.dc_v2_ofdm_fields_tab a{color:#7c3aed}'
			. '#woocommerce-product-data ul.wc-tabs li.dc_v2_ofdm_fields_tab a::before{color:#7c3aed!important;content:"\\f473"!important;font-family:Dashicons!important}'
			. '#woocommerce-product-data ul.wc-tabs li.dc_v2_ofdm_fields_tab.active a{background:linear-gradient(135deg,rgba(139,92,246,.16),rgba(168,85,247,.22));color:#5b21b6;border-left-color:#7c3aed}'
			. '.dc-v2-product-fields-panel{padding:16px 18px 24px}'
			. '.dc-v2-product-fields-status{margin-top:12px;font-weight:600}'
			. '.dc-v2-product-fields-status.is-ok{color:#166534}'
			. '.dc-v2-product-fields-status.is-err{color:#b91c1c}'
		);

		// Deep-link from Order From DM composer: post.php?post=ID&action=edit&dc_v2_tab=ofdm_fields
		wp_add_inline_script(
			'jquery',
			'jQuery(function($){try{var q=new URLSearchParams(window.location.search);if(q.get("dc_v2_tab")!=="ofdm_fields")return;var open=function(){var $a=$("#woocommerce-product-data ul.wc-tabs li.dc_v2_ofdm_fields_tab a");if(!$a.length)return false;$a.trigger("click");var $box=$("#woocommerce-product-data");if($box.length){$("html,body").animate({scrollTop:Math.max(0,$box.offset().top-48)},220);}return true;};if(open())return;var n=0;var t=setInterval(function(){n+=1;if(open()||n>40)clearInterval(t);},50);}catch(e){}});'
		);

		if ( empty( $runtime['ready'] ) || empty( $runtime['url'] ) ) {
			wp_add_inline_script(
				'jquery',
				'jQuery(function($){$("#dc-v2-product-fields-status").prop("hidden",false).text('
				. wp_json_encode( __( 'OFDM Fields editor is temporarily unavailable. Check the Order From DM connection and reload.', 'order-from-dm' ) )
				. ');});'
			);
			return;
		}

		wp_enqueue_script(
			'dc-v2-fields-editor',
			$runtime['url'],
			array(),
			$runtime['ver'],
			true
		);
		wp_localize_script(
			'dc-v2-fields-editor',
			'DCV2FieldsEditorBoot',
			array(
				'mode'     => 'product',
				'restUrl'  => esc_url_raw( rest_url( DC_V2_REST::NS ) ),
				'nonce'    => wp_create_nonce( 'wp_rest' ),
				'ajaxUrl'  => admin_url( 'admin-ajax.php' ),
				'ajaxNonce'=> wp_create_nonce( 'dc_v2_product_fields' ),
				'i18n'     => self::editor_i18n(),
				'locale'   => dc_v2_plugin_locale(),
			)
		);
		wp_add_inline_script(
			'dc-v2-fields-editor',
			'(function(){function boot(){var root=document.getElementById("dc-v2-product-fields-root");var panel=document.querySelector(".dc-v2-product-fields-panel");if(!root||!panel||!window.DCV2FieldsEditor)return;var id=parseInt(panel.getAttribute("data-product-id")||"0",10);window.DCV2FieldsEditor.mountProduct(root,{productId:id,boot:window.DCV2FieldsEditorBoot});}if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",boot);else boot();})();',
			'after'
		);
	}

	public static function editor_i18n() {
		return array(
			'title'            => __( 'OFDM Fields', 'order-from-dm' ),
			'lead'             => __( 'Fields defined here are always attached when this product is added to an Order From DM link, and they also show on the normal WooCommerce product page for regular cart purchases. They can only be removed here — not from the link composer.', 'order-from-dm' ),
			'infoAria'         => __( 'About OFDM Fields', 'order-from-dm' ),
			'addField'         => __( 'Add field', 'order-from-dm' ),
			'selectType'       => __( 'Select field type', 'order-from-dm' ),
			'save'             => __( 'Save changes', 'order-from-dm' ),
			'cancel'           => __( 'Cancel', 'order-from-dm' ),
			'edit'             => __( 'Edit', 'order-from-dm' ),
			'delete'           => __( 'Delete', 'order-from-dm' ),
			'back'             => __( 'Back', 'order-from-dm' ),
			'saveField'        => __( 'Save field', 'order-from-dm' ),
			'saving'           => __( 'Saving…', 'order-from-dm' ),
			'fieldLabel'       => __( 'Label', 'order-from-dm' ),
			'fieldLabelHint'   => __( 'What the customer sees in chat.', 'order-from-dm' ),
			'placeholder'      => __( 'Placeholder (optional)', 'order-from-dm' ),
			'options'          => __( 'Options (one per line)', 'order-from-dm' ),
			'optional'         => __( 'Optional field', 'order-from-dm' ),
			'optionalShort'    => __( 'optional', 'order-from-dm' ),
			'noFields'         => __( 'No fields yet. Add the details you need from customers.', 'order-from-dm' ),
			'unnamed'          => __( 'Unnamed field', 'order-from-dm' ),
			'reorder'          => __( 'Reorder', 'order-from-dm' ),
			'productBadge'     => __( 'From product', 'order-from-dm' ),
			'editFromProduct'  => __( 'Edit from product', 'order-from-dm' ),
			'lockedHint'       => __( 'Locked — edit or remove only in the product OFDM Fields tab.', 'order-from-dm' ),
			'saved'            => __( 'Fields saved.', 'order-from-dm' ),
			'saveError'        => __( 'Could not save fields.', 'order-from-dm' ),
			'loadError'        => __( 'Could not load fields.', 'order-from-dm' ),
			'saveProductFirst' => __( 'Save the product once, then add OFDM fields.', 'order-from-dm' ),
			'type_text'        => __( 'Short text', 'order-from-dm' ),
			'type_textarea'    => __( 'Long text', 'order-from-dm' ),
			'type_dropdown'    => __( 'Dropdown', 'order-from-dm' ),
			'type_radio'       => __( 'Single choice', 'order-from-dm' ),
			'type_image'       => __( 'Photo', 'order-from-dm' ),
			'customerFields'   => __( 'Customer fields', 'order-from-dm' ),
			'productLabel'     => __( 'Product', 'order-from-dm' ),
		);
	}

	/**
	 * Merge product defaults (locked) ahead of link-only fields.
	 *
	 * @param array $line_fields Existing line fields.
	 * @param int   $product_id Product ID.
	 * @return array
	 */
	public static function merge_into_line_fields( $line_fields, $product_id ) {
		$product_fields = self::get_fields( $product_id );
		$line_fields    = is_array( $line_fields ) ? $line_fields : array();

		$product_keys = array();
		foreach ( $product_fields as $pf ) {
			$product_keys[ $pf['key'] ] = true;
		}

		$link_only = array();
		foreach ( $line_fields as $lf ) {
			$key = sanitize_key( $lf['key'] ?? '' );
			if ( ! $key ) {
				continue;
			}
			$origin = (string) ( $lf['origin'] ?? '' );
			// Drop stale copies of product fields; keep true link-only fields.
			if ( isset( $product_keys[ $key ] ) || 'product' === $origin || ! empty( $lf['locked'] ) ) {
				continue;
			}
			$lf['origin'] = 'link';
			$lf['locked'] = false;
			$link_only[]  = $lf;
		}

		return array_merge( $product_fields, $link_only );
	}

	public static function enqueue_storefront_assets() {
		if ( ! is_product() ) {
			return;
		}
		$product_id = (int) get_queried_object_id();
		if ( $product_id <= 0 || ! self::get_fields( $product_id ) ) {
			return;
		}
		wp_register_style( 'dc-v2-product-fields-storefront', false, array(), DC_V2_VERSION );
		wp_enqueue_style( 'dc-v2-product-fields-storefront' );
		wp_add_inline_style(
			'dc-v2-product-fields-storefront',
			/* Avoid width:100% + padding overflow inside WC block cart grid (min-content columns). */
			'form.cart:has(.dc-v2-storefront-fields){max-width:100%;min-width:0;box-sizing:border-box}'
			. '.wp-block-woocommerce-add-to-cart-form:has(.dc-v2-storefront-fields){max-width:100%;min-width:0;overflow-x:clip}'
			. '.dc-v2-storefront-fields{display:block;margin:0 0 1.1rem;padding:1rem 1.1rem;border:1px solid rgba(124,58,237,.22);border-radius:16px;background:linear-gradient(180deg,rgba(139,92,246,.08),rgba(255,255,255,.9));box-sizing:border-box;max-width:100%;min-width:0;width:auto;overflow-x:clip}'
			. '.dc-v2-storefront-fields__title{margin:0 0 .85rem;font-size:1rem;font-weight:700;color:#4c1d95;overflow-wrap:anywhere}'
			. '.dc-v2-storefront-fields__list{list-style:none;margin:0;padding:0;display:grid;gap:.75rem;min-width:0;max-width:100%}'
			. '.dc-v2-storefront-fields__item{display:grid;gap:.35rem;min-width:0;max-width:100%}'
			. '.dc-v2-storefront-fields__label{font-size:.82rem;font-weight:650;color:#1a1428;overflow-wrap:anywhere}'
			. '.dc-v2-storefront-fields__req{color:#7c3aed}'
			. '.dc-v2-storefront-fields__control{display:block;width:100%;max-width:100%;min-width:0;box-sizing:border-box;border:1px solid rgba(26,20,40,.14);border-radius:12px;padding:.7rem .85rem;font-size:16px;background:#fff;color:#1a1428}'
			. '.dc-v2-storefront-fields__control:focus{outline:2px solid rgba(124,58,237,.35);outline-offset:1px;border-color:#7c3aed}'
			. '.dc-v2-storefront-fields__radios{display:grid;gap:.45rem;min-width:0;max-width:100%}'
			. '.dc-v2-storefront-fields__radio{display:flex;align-items:center;gap:.55rem;font-size:.92rem;color:#1a1428;min-width:0}'
			. '.dc-v2-storefront-fields__radio span{min-width:0;overflow-wrap:anywhere}'
			. '.dc-v2-storefront-fields__file{font-size:.88rem;max-width:100%}'
			. '.dc-v2-storefront-fields__type{font-size:.72rem;color:#7c3aed;font-weight:650;text-transform:uppercase;letter-spacing:.04em}'
			. '.dc-v2-storefront-fields__preview{display:block;margin-top:.55rem;width:72px;height:72px;object-fit:cover;border-radius:12px;border:1px solid rgba(26,20,40,.1);background:#f6f4fb;box-shadow:0 1px 2px rgba(26,20,40,.06)}'
			. '.dc-v2-storefront-fields__compress{margin:.35rem 0 0;font-size:.78rem;color:#6d28d9}'
			. 'form.cart .single_add_to_cart_button:disabled{opacity:.45;cursor:not-allowed;pointer-events:none}'
		);
		$fields = self::get_fields( $product_id );
		$needs_file = false;
		$has_required = false;
		foreach ( $fields as $f ) {
			$type = (string) ( $f['type'] ?? '' );
			if ( 'image' === $type ) {
				$needs_file = true;
				$has_required = true;
			}
			if ( ! empty( $f['required'] ) ) {
				$has_required = true;
			}
		}
		$script = self::storefront_inline_script( $needs_file, $has_required );
		wp_register_script( 'dc-v2-product-fields-storefront', false, array(), DC_V2_VERSION, true );
		wp_enqueue_script( 'dc-v2-product-fields-storefront' );
		wp_add_inline_script( 'dc-v2-product-fields-storefront', $script );
	}

	/**
	 * Client-side image resize (canvas) + required-field ATC gate.
	 */
	private static function storefront_inline_script( $needs_file, $has_required ) {
		$needs_file    = $needs_file ? 'true' : 'false';
		$has_required  = $has_required ? 'true' : 'false';
		$compress_lbl  = wp_json_encode( __( 'Optimizing photo…', 'order-from-dm' ) );
		return <<<JS
(function(){
  var NEEDS_FILE={$needs_file};
  var HAS_REQUIRED={$has_required};
  var COMPRESS_LABEL={$compress_lbl};
  function ready(fn){if(document.readyState!=="loading")fn();else document.addEventListener("DOMContentLoaded",fn);}
  function escapeName(n){return String(n).replace(/\\\\/g,"\\\\\\\\").replace(/"/g,'\\\\"');}
  function requiredFilled(section){
    var reqs=section.querySelectorAll("[required]");var seen={};
    for(var i=0;i<reqs.length;i++){
      var el=reqs[i];
      if(el.type==="radio"){if(seen[el.name])continue;seen[el.name]=1;
        if(!section.querySelector('input[type="radio"][name="'+escapeName(el.name)+'"]:checked'))return false;continue;}
      if(el.type==="file"){if(!el.files||!el.files.length)return false;continue;}
      if(!String(el.value||"").trim())return false;
    }
    return true;
  }
  function isCompressing(section){
    return !!section.querySelector('.dc-v2-storefront-fields__file[data-compressing="1"]');
  }
  function sync(form){
    var section=form.querySelector(".dc-v2-storefront-fields");
    if(!section)return;
    var ok=(!HAS_REQUIRED||requiredFilled(section))&&!isCompressing(section);
    form.querySelectorAll('button[type="submit"], .single_add_to_cart_button').forEach(function(btn){
      btn.disabled=!ok;btn.setAttribute("aria-disabled",ok?"false":"true");
    });
  }
  function setStatus(input,text){
    var item=input.closest(".dc-v2-storefront-fields__item");
    if(!item)return;
    var el=item.querySelector(".dc-v2-storefront-fields__compress");
    if(!text){if(el)el.remove();return;}
    if(!el){el=document.createElement("p");el.className="dc-v2-storefront-fields__compress";item.appendChild(el);}
    el.textContent=text;
  }
  function setPreview(input,url){
    var item=input.closest(".dc-v2-storefront-fields__item");
    if(!item)return;
    var img=item.querySelector(".dc-v2-storefront-fields__preview");
    if(!url){if(img)img.remove();return;}
    if(!img){img=document.createElement("img");img.className="dc-v2-storefront-fields__preview";img.alt="";item.appendChild(img);}
    img.src=url;
  }
  function replaceInputFile(input,file){
    try{var dt=new DataTransfer();dt.items.add(file);input.files=dt.files;return true;}catch(e){return false;}
  }
  function compressImageFile(file,done){
    if(!file||!/^image\\//.test(file.type||"")){done(file);return;}
    if(file.size<350*1024&&(file.type==="image/jpeg"||file.type==="image/webp")){done(file);return;}
    var reader=new FileReader();
    reader.onload=function(){
      var img=new Image();
      img.onload=function(){
        var max=1280,w=img.width,h=img.height;
        if(w>max||h>max){
          if(w>=h){h=Math.round(h*max/w);w=max;}
          else{w=Math.round(w*max/h);h=max;}
        }
        var canvas=document.createElement("canvas");
        canvas.width=w;canvas.height=h;
        canvas.getContext("2d").drawImage(img,0,0,w,h);
        canvas.toBlob(function(blob){
          if(!blob||blob.size>=file.size){done(file);return;}
          var base=(file.name||"photo").replace(/\\.[^.]+$/,"");
          done(new File([blob],base+".jpg",{type:"image/jpeg",lastModified:Date.now()}));
        },"image/jpeg",0.82);
      };
      img.onerror=function(){done(file);};
      img.src=reader.result;
    };
    reader.onerror=function(){done(file);};
    reader.readAsDataURL(file);
  }
  function bindImageCompression(form){
    form.querySelectorAll(".dc-v2-storefront-fields__file").forEach(function(input){
      input.addEventListener("change",function(){
        var file=input.files&&input.files[0];
        if(!file){setPreview(input,"");setStatus(input,"");sync(form);return;}
        input.setAttribute("data-compressing","1");
        setStatus(input,COMPRESS_LABEL);
        setPreview(input,"");
        sync(form);
        compressImageFile(file,function(out){
          if(out&&out!==file){replaceInputFile(input,out);}
          var ready=input.files&&input.files[0]?input.files[0]:out;
          if(ready){
            var url=URL.createObjectURL(ready);
            setPreview(input,url);
          }
          input.removeAttribute("data-compressing");
          setStatus(input,"");
          sync(form);
        });
      });
    });
  }
  ready(function(){
    if(NEEDS_FILE){
      document.querySelectorAll("form.cart").forEach(function(f){f.setAttribute("enctype","multipart/form-data");});
    }
    document.querySelectorAll("form.cart").forEach(function(form){
      var section=form.querySelector(".dc-v2-storefront-fields");
      if(!section)return;
      if(NEEDS_FILE)bindImageCompression(form);
      if(HAS_REQUIRED||NEEDS_FILE){
        sync(form);
        section.addEventListener("input",function(){sync(form);});
        section.addEventListener("change",function(){sync(form);});
        form.addEventListener("submit",function(e){
          if(isCompressing(section)||(HAS_REQUIRED&&!requiredFilled(section))){
            e.preventDefault();sync(form);
          }
        });
      }
    });
  });
})();
JS;
	}

	/**
	 * Interactive OFDM fields inside the add-to-cart form.
	 */
	public static function render_storefront_fields() {
		global $product;
		if ( ! $product || ! is_a( $product, 'WC_Product' ) ) {
			return;
		}
		$product_id = (int) $product->get_id();
		$fields     = self::get_fields( $product_id );
		if ( ! $fields ) {
			return;
		}

		$type_labels = array(
			'text'     => __( 'Short text', 'order-from-dm' ),
			'textarea' => __( 'Long text', 'order-from-dm' ),
			'dropdown' => __( 'Dropdown', 'order-from-dm' ),
			'radio'    => __( 'Single choice', 'order-from-dm' ),
			'image'    => __( 'Photo', 'order-from-dm' ),
		);
		$posted = isset( $_POST['dc_v2_field'] ) && is_array( $_POST['dc_v2_field'] ) ? wp_unslash( $_POST['dc_v2_field'] ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		?>
		<section class="dc-v2-storefront-fields" aria-label="<?php echo esc_attr__( 'Order From DM fields', 'order-from-dm' ); ?>">
			<h3 class="dc-v2-storefront-fields__title"><?php echo esc_html__( 'Customization details', 'order-from-dm' ); ?></h3>
			<ul class="dc-v2-storefront-fields__list">
				<?php foreach ( $fields as $field ) : ?>
					<?php
					$key   = sanitize_key( $field['key'] ?? '' );
					if ( ! $key ) {
						continue;
					}
					$type  = (string) ( $field['type'] ?? 'text' );
					$label = (string) ( $field['label'] ?? '' );
					$req   = ! empty( $field['required'] ) || 'image' === $type;
					$ph    = (string) ( $field['placeholder'] ?? '' );
					$opts  = is_array( $field['options'] ?? null ) ? $field['options'] : array();
					$name  = 'dc_v2_field[' . $key . ']';
					$id    = 'dc_v2_field_' . $key;
					$val   = isset( $posted[ $key ] ) ? (string) $posted[ $key ] : '';
					$label_text = $label !== '' ? $label : __( 'Unnamed field', 'order-from-dm' );
					?>
					<li class="dc-v2-storefront-fields__item">
						<div class="dc-v2-storefront-fields__type"><?php echo esc_html( $type_labels[ $type ] ?? $type ); ?></div>
						<label class="dc-v2-storefront-fields__label" for="<?php echo esc_attr( $id ); ?>">
							<?php echo esc_html( $label_text ); ?>
							<?php if ( $req ) : ?>
								<span class="dc-v2-storefront-fields__req" aria-hidden="true">*</span>
							<?php endif; ?>
						</label>
						<?php if ( 'textarea' === $type ) : ?>
							<textarea
								class="dc-v2-storefront-fields__control"
								id="<?php echo esc_attr( $id ); ?>"
								name="<?php echo esc_attr( $name ); ?>"
								rows="3"
								placeholder="<?php echo esc_attr( $ph ); ?>"
								<?php echo $req ? 'required' : ''; ?>
							><?php echo esc_textarea( $val ); ?></textarea>
						<?php elseif ( 'dropdown' === $type ) : ?>
							<select
								class="dc-v2-storefront-fields__control"
								id="<?php echo esc_attr( $id ); ?>"
								name="<?php echo esc_attr( $name ); ?>"
								<?php echo $req ? 'required' : ''; ?>
							>
								<option value=""><?php echo esc_html__( 'Select…', 'order-from-dm' ); ?></option>
								<?php foreach ( $opts as $opt ) : ?>
									<option value="<?php echo esc_attr( (string) $opt ); ?>" <?php selected( $val, (string) $opt ); ?>><?php echo esc_html( (string) $opt ); ?></option>
								<?php endforeach; ?>
							</select>
						<?php elseif ( 'radio' === $type ) : ?>
							<div class="dc-v2-storefront-fields__radios" role="radiogroup" aria-labelledby="<?php echo esc_attr( $id ); ?>">
								<span id="<?php echo esc_attr( $id ); ?>" class="screen-reader-text"><?php echo esc_html( $label_text ); ?></span>
								<?php foreach ( $opts as $oi => $opt ) : ?>
									<?php $oid = $id . '_' . $oi; ?>
									<label class="dc-v2-storefront-fields__radio" for="<?php echo esc_attr( $oid ); ?>">
										<input
											type="radio"
											id="<?php echo esc_attr( $oid ); ?>"
											name="<?php echo esc_attr( $name ); ?>"
											value="<?php echo esc_attr( (string) $opt ); ?>"
											<?php checked( $val, (string) $opt ); ?>
											<?php echo $req ? 'required' : ''; ?>
										/>
										<span><?php echo esc_html( (string) $opt ); ?></span>
									</label>
								<?php endforeach; ?>
							</div>
						<?php elseif ( 'image' === $type ) : ?>
							<input
								class="dc-v2-storefront-fields__control dc-v2-storefront-fields__file"
								id="<?php echo esc_attr( $id ); ?>"
								type="file"
								name="dc_v2_field_file[<?php echo esc_attr( $key ); ?>]"
								accept="image/*"
								<?php echo $req ? 'required' : ''; ?>
							/>
						<?php else : ?>
							<input
								class="dc-v2-storefront-fields__control"
								id="<?php echo esc_attr( $id ); ?>"
								type="text"
								name="<?php echo esc_attr( $name ); ?>"
								value="<?php echo esc_attr( $val ); ?>"
								placeholder="<?php echo esc_attr( $ph ); ?>"
								<?php echo $req ? 'required' : ''; ?>
							/>
						<?php endif; ?>
					</li>
				<?php endforeach; ?>
			</ul>
		</section>
		<?php
	}

	public static function validate_storefront_fields( $passed, $product_id, $quantity ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter
		if ( ! $passed ) {
			return $passed;
		}
		$fields = self::get_fields( (int) $product_id );
		if ( ! $fields ) {
			return $passed;
		}
		$posted = isset( $_POST['dc_v2_field'] ) && is_array( $_POST['dc_v2_field'] ) ? wp_unslash( $_POST['dc_v2_field'] ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$files  = isset( $_FILES['dc_v2_field_file'] ) && is_array( $_FILES['dc_v2_field_file'] ) ? $_FILES['dc_v2_field_file'] : array(); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized

		foreach ( $fields as $field ) {
			$key  = sanitize_key( $field['key'] ?? '' );
			$type = (string) ( $field['type'] ?? 'text' );
			$req  = ! empty( $field['required'] ) || 'image' === $type;
			if ( ! $key || ! $req ) {
				continue;
			}
			$label = (string) ( $field['label'] ?? __( 'Field', 'order-from-dm' ) );
			if ( 'image' === $type ) {
				$error = isset( $files['error'][ $key ] ) ? (int) $files['error'][ $key ] : UPLOAD_ERR_NO_FILE;
				if ( UPLOAD_ERR_OK !== $error ) {
					wc_add_notice(
						sprintf(
							/* translators: %s: field label */
							__( 'Please upload a photo for “%s”.', 'order-from-dm' ),
							$label
						),
						'error'
					);
					return false;
				}
				continue;
			}
			$val = isset( $posted[ $key ] ) ? trim( (string) $posted[ $key ] ) : '';
			if ( $val === '' ) {
				wc_add_notice(
					sprintf(
						/* translators: %s: field label */
						__( 'Please fill in “%s”.', 'order-from-dm' ),
						$label
					),
					'error'
				);
				return false;
			}
		}
		return $passed;
	}

	public static function add_cart_item_data( $cart_item_data, $product_id, $variation_id ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter
		$fields = self::get_fields( (int) $product_id );
		if ( ! $fields ) {
			return $cart_item_data;
		}
		$posted = isset( $_POST['dc_v2_field'] ) && is_array( $_POST['dc_v2_field'] ) ? wp_unslash( $_POST['dc_v2_field'] ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$values = array();

		foreach ( $fields as $field ) {
			$key  = sanitize_key( $field['key'] ?? '' );
			$type = (string) ( $field['type'] ?? 'text' );
			if ( ! $key ) {
				continue;
			}
			$label = (string) ( $field['label'] ?? $key );
			if ( 'image' === $type ) {
				$attachment_id = self::handle_storefront_upload( $key );
				if ( $attachment_id ) {
					$url = wp_get_attachment_url( $attachment_id );
					$values[] = array(
						'key'           => $key,
						'label'         => $label,
						'type'          => 'image',
						'value'         => $url ? $url : (string) $attachment_id,
						'attachment_id' => (int) $attachment_id,
					);
				}
				continue;
			}
			$raw = isset( $posted[ $key ] ) ? (string) $posted[ $key ] : '';
			$val = sanitize_text_field( $raw );
			if ( 'textarea' === $type ) {
				$val = sanitize_textarea_field( $raw );
			}
			if ( $val === '' ) {
				continue;
			}
			$values[] = array(
				'key'   => $key,
				'label' => $label,
				'type'  => $type,
				'value' => $val,
			);
		}

		if ( $values ) {
			$cart_item_data['dc_v2_fields'] = $values;
			// Unique cart line when customization differs.
			$cart_item_data['dc_v2_fields_hash'] = md5( wp_json_encode( $values ) );
		}
		return $cart_item_data;
	}

	private static function handle_storefront_upload( $key ) {
		if ( empty( $_FILES['dc_v2_field_file']['name'][ $key ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
			return 0;
		}
		$file = array(
			'name'     => isset( $_FILES['dc_v2_field_file']['name'][ $key ] ) ? $_FILES['dc_v2_field_file']['name'][ $key ] : '', // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
			'type'     => isset( $_FILES['dc_v2_field_file']['type'][ $key ] ) ? $_FILES['dc_v2_field_file']['type'][ $key ] : '', // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
			'tmp_name' => isset( $_FILES['dc_v2_field_file']['tmp_name'][ $key ] ) ? $_FILES['dc_v2_field_file']['tmp_name'][ $key ] : '', // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
			'error'    => isset( $_FILES['dc_v2_field_file']['error'][ $key ] ) ? (int) $_FILES['dc_v2_field_file']['error'][ $key ] : UPLOAD_ERR_NO_FILE, // phpcs:ignore WordPress.Security.NonceVerification.Missing
			'size'     => isset( $_FILES['dc_v2_field_file']['size'][ $key ] ) ? (int) $_FILES['dc_v2_field_file']['size'][ $key ] : 0, // phpcs:ignore WordPress.Security.NonceVerification.Missing
		);
		if ( UPLOAD_ERR_OK !== $file['error'] || $file['tmp_name'] === '' ) {
			return 0;
		}
		require_once ABSPATH . 'wp-admin/includes/file.php';
		require_once ABSPATH . 'wp-admin/includes/media.php';
		require_once ABSPATH . 'wp-admin/includes/image.php';

		$upload = wp_handle_upload(
			$file,
			array(
				'test_form' => false,
				'mimes'     => array(
					'jpg|jpeg|jpe' => 'image/jpeg',
					'png'          => 'image/png',
					'gif'          => 'image/gif',
					'webp'         => 'image/webp',
				),
			)
		);
		if ( isset( $upload['error'] ) || empty( $upload['file'] ) ) {
			return 0;
		}
		$attachment = array(
			'post_mime_type' => $upload['type'],
			'post_title'     => sanitize_file_name( wp_basename( $upload['file'] ) ),
			'post_content'   => '',
			'post_status'    => 'inherit',
		);
		$attach_id = wp_insert_attachment( $attachment, $upload['file'] );
		if ( is_wp_error( $attach_id ) || ! $attach_id ) {
			return 0;
		}
		$meta = wp_generate_attachment_metadata( $attach_id, $upload['file'] );
		wp_update_attachment_metadata( $attach_id, $meta );
		return (int) $attach_id;
	}

	/**
	 * Small rounded thumbnail HTML for cart / checkout / order meta.
	 */
	public static function image_thumb_html( $url, $label = '' ) {
		$url = esc_url( $url );
		if ( ! $url ) {
			return '';
		}
		$alt = $label !== '' ? $label : __( 'Uploaded photo', 'order-from-dm' );
		return sprintf(
			'<span class="dc-v2-cart-field-thumb-wrap"><a href="%1$s" target="_blank" rel="noopener noreferrer"><img class="dc-v2-cart-field-thumb" src="%1$s" alt="%2$s" loading="lazy" decoding="async" width="56" height="56" style="display:block;width:56px;height:56px;object-fit:cover;border-radius:10px;border:1px solid rgba(26,20,40,.1);background:#f6f4fb;" /></a></span>',
			$url,
			esc_attr( $alt )
		);
	}

	public static function enqueue_cart_display_assets() {
		if ( ! function_exists( 'is_cart' ) || ( ! is_cart() && ! is_checkout() ) ) {
			return;
		}
		wp_register_style( 'dc-v2-cart-field-thumbs', false, array(), DC_V2_VERSION );
		wp_enqueue_style( 'dc-v2-cart-field-thumbs' );
		wp_add_inline_style(
			'dc-v2-cart-field-thumbs',
			'.dc-v2-cart-field-thumb-wrap{display:inline-block;line-height:0;vertical-align:middle}'
			. '.dc-v2-cart-field-thumb{display:block;width:56px;height:56px;object-fit:cover;border-radius:10px;border:1px solid rgba(26,20,40,.1);background:#f6f4fb;box-shadow:0 1px 2px rgba(26,20,40,.06)}'
			. '.woocommerce-checkout-review-order-table .dc-v2-cart-field-thumb,'
			. '.woocommerce-cart-form .dc-v2-cart-field-thumb{width:56px;height:56px}'
			. '.wc-block-components-product-details__value .dc-v2-cart-field-thumb{width:48px;height:48px;border-radius:8px}'
		);
	}

	public static function display_cart_item_data( $item_data, $cart_item ) {
		if ( empty( $cart_item['dc_v2_fields'] ) || ! is_array( $cart_item['dc_v2_fields'] ) ) {
			return $item_data;
		}
		foreach ( $cart_item['dc_v2_fields'] as $field ) {
			$label = (string) ( $field['label'] ?? '' );
			$value = (string) ( $field['value'] ?? '' );
			$type  = (string) ( $field['type'] ?? '' );
			if ( $label === '' ) {
				continue;
			}
			if ( 'image' === $type ) {
				$url = '';
				if ( ! empty( $field['attachment_id'] ) ) {
					$url = (string) wp_get_attachment_image_url( (int) $field['attachment_id'], 'thumbnail' );
					if ( ! $url ) {
						$url = (string) wp_get_attachment_url( (int) $field['attachment_id'] );
					}
				}
				if ( ! $url && $value !== '' ) {
					$url = $value;
				}
				if ( ! $url ) {
					continue;
				}
				$thumb = self::image_thumb_html( $url, $label );
				$item_data[] = array(
					'key'     => $label,
					'value'   => __( 'Photo attached', 'order-from-dm' ),
					'display' => $thumb,
				);
				continue;
			}
			if ( $value === '' ) {
				continue;
			}
			$item_data[] = array(
				'key'   => $label,
				'value' => $value,
			);
		}
		return $item_data;
	}

	public static function add_order_item_meta( $item, $cart_item_key, $values, $order ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter
		if ( empty( $values['dc_v2_fields'] ) || ! is_array( $values['dc_v2_fields'] ) ) {
			return;
		}
		foreach ( $values['dc_v2_fields'] as $field ) {
			$label = (string) ( $field['label'] ?? '' );
			$value = (string) ( $field['value'] ?? '' );
			$type  = (string) ( $field['type'] ?? '' );
			if ( $label === '' ) {
				continue;
			}
			if ( 'image' === $type ) {
				if ( $value === '' && ! empty( $field['attachment_id'] ) ) {
					$value = (string) wp_get_attachment_url( (int) $field['attachment_id'] );
				}
				if ( $value === '' ) {
					continue;
				}
				$item->add_meta_data( $label, $value, true );
				continue;
			}
			if ( $value === '' ) {
				continue;
			}
			$item->add_meta_data( $label, $value, true );
		}
		$item->add_meta_data( '_dc_v2_fields', $values['dc_v2_fields'], true );
	}

	/**
	 * Show image thumbs instead of raw URLs on order received / emails / admin.
	 */
	public static function display_order_item_meta_value( $display_value, $meta, $item ) {
		if ( ! $meta instanceof WC_Meta_Data || ! $item instanceof WC_Order_Item_Product ) {
			return $display_value;
		}
		$fields = $item->get_meta( '_dc_v2_fields', true );
		if ( ! is_array( $fields ) || ! $fields ) {
			return $display_value;
		}
		$value = (string) $meta->value;
		$url   = '';
		foreach ( $fields as $field ) {
			if ( ( $field['type'] ?? '' ) !== 'image' ) {
				continue;
			}
			$field_val = (string) ( $field['value'] ?? '' );
			$att_id    = (int) ( $field['attachment_id'] ?? 0 );
			$att_url   = $att_id ? (string) wp_get_attachment_url( $att_id ) : '';
			if ( $value !== $field_val && ( ! $att_url || $value !== $att_url ) ) {
				continue;
			}
			if ( $att_id ) {
				$url = (string) wp_get_attachment_image_url( $att_id, 'thumbnail' );
				if ( ! $url ) {
					$url = $att_url;
				}
			}
			if ( ! $url ) {
				$url = $field_val !== '' ? $field_val : $value;
			}
			break;
		}
		if ( ! $url ) {
			return $display_value;
		}
		$thumb = self::image_thumb_html( $url, (string) $meta->key );
		return $thumb ? $thumb : $display_value;
	}

	public static function hydrate_cart_item_from_session( $cart_item, $values ) {
		if ( isset( $values['dc_v2_fields'] ) ) {
			$cart_item['dc_v2_fields'] = $values['dc_v2_fields'];
		}
		if ( isset( $values['dc_v2_fields_hash'] ) ) {
			$cart_item['dc_v2_fields_hash'] = $values['dc_v2_fields_hash'];
		}
		return $cart_item;
	}
}
