<?php
/**
 * VAT and PO fields on both checkouts.
 *
 * @package Harbor_EU_EInvoicing_For_WooCommerce
 */

defined( 'ABSPATH' ) || exit;

class HEIN_Checkout_Fields {

	private const VAT_FIELD = 'hein/vat_number';
	private const PO_FIELD  = 'hein/po_reference';
	private const DISMISSED = '_hein_block_api_notice_dismissed';

	public static function init(): void {
		add_action( 'woocommerce_init', [ __CLASS__, 'register_block_fields' ] );
		add_filter( 'woocommerce_checkout_fields', [ __CLASS__, 'classic_fields' ] );
		add_action( 'woocommerce_after_checkout_validation', [ __CLASS__, 'validate_classic' ], 10, 2 );
		add_action( 'woocommerce_checkout_create_order', [ __CLASS__, 'save_classic' ], 10, 2 );
		add_action( 'woocommerce_set_additional_field_value', [ __CLASS__, 'mirror_block_field' ], 10, 4 );
	}

	/**
	 * The Additional Checkout Fields API is WC 8.9+, while the block checkout has been the default
	 * since 8.3, so the gap gets a notice rather than a silently missing field. Neither field is
	 * ever required: that would break every B2C order
	 */
	public static function register_block_fields(): void {
		if ( ! function_exists( 'woocommerce_register_additional_checkout_field' ) ) {
			add_action( 'admin_notices', [ __CLASS__, 'block_api_notice' ] );
			add_action( 'admin_post_hein_dismiss_block_notice', [ __CLASS__, 'dismiss_block_api_notice' ] );

			return;
		}

		woocommerce_register_additional_checkout_field(
			[
				'id'                => self::VAT_FIELD,
				'label'             => __( 'VAT number', 'harbor-eu-einvoicing-for-woocommerce' ),
				'optionalLabel'     => __( 'VAT number (optional)', 'harbor-eu-einvoicing-for-woocommerce' ),
				'location'          => 'contact',
				'type'              => 'text',
				'required'          => false,
				'sanitize_callback' => [ __CLASS__, 'sanitize_block_field' ],
				'validate_callback' => [ __CLASS__, 'validate_block_field' ],
				'attributes'        => [
					'autocomplete'   => 'off',
					'autocapitalize' => 'characters',
					'maxLength'      => '20',
					'title'          => __( 'Your EU VAT number, including the country prefix', 'harbor-eu-einvoicing-for-woocommerce' ),
					'pattern'        => '[A-Za-z0-9 .-]{4,20}',
				],
			]
		);

		woocommerce_register_additional_checkout_field(
			[
				'id'                => self::PO_FIELD,
				'label'             => __( 'Purchase order reference', 'harbor-eu-einvoicing-for-woocommerce' ),
				'optionalLabel'     => __( 'Purchase order reference (optional)', 'harbor-eu-einvoicing-for-woocommerce' ),
				'location'          => 'order',
				'type'              => 'text',
				'required'          => false,
				'sanitize_callback' => [ __CLASS__, 'sanitize_block_field' ],
				'validate_callback' => [ __CLASS__, 'validate_block_field' ],
				'attributes'        => [
					'autocomplete' => 'off',
					'maxLength'    => '60',
					'title'        => __( 'Your own reference for this purchase', 'harbor-eu-einvoicing-for-woocommerce' ),
				],
			]
		);

		do_action( 'hein_register_checkout_fields' );
	}

	public static function sanitize_block_field( $value, $field = null ): string {
		$value = sanitize_text_field( (string) $value );

		return self::VAT_FIELD === self::field_id( $field ) ? HEIN_VAT::normalize( $value ) : trim( $value );
	}

	public static function validate_block_field( $value, $field = null ): ?WP_Error {
		if ( self::VAT_FIELD !== self::field_id( $field ) || self::vat_acceptable( (string) $value ) ) {
			return null;
		}

		return new WP_Error( 'hein_invalid_vat', self::vat_error() );
	}

	/**
	 * Registered through the documented filter, never echoed on woocommerce_after_order_notes:
	 * WooCommerce's $posted_data contains only REGISTERED fields, so an echoed input would lose the
	 * VAT number on every classic order, and reading $_POST instead is a nonce-verification error
	 */
	public static function classic_fields( array $fields ): array {
		$fields['order']['hein_vat_number'] = [
			'label'             => __( 'VAT number', 'harbor-eu-einvoicing-for-woocommerce' ),
			'required'          => false,
			'class'             => [ 'form-row-wide' ],
			'priority'          => 100,
			'autocomplete'      => 'off',
			'custom_attributes' => [ 'maxlength' => '20' ],
		];

		$fields['order']['hein_po_reference'] = [
			'label'             => __( 'Purchase order reference', 'harbor-eu-einvoicing-for-woocommerce' ),
			'required'          => false,
			'class'             => [ 'form-row-wide' ],
			'priority'          => 110,
			'autocomplete'      => 'off',
			'custom_attributes' => [ 'maxlength' => '60' ],
		];

		return $fields;
	}

	public static function validate_classic( array $data, $errors ): void {
		if ( ! is_a( $errors, 'WP_Error' ) || self::vat_acceptable( (string) ( $data['hein_vat_number'] ?? '' ) ) ) {
			return;
		}

		$errors->add( 'hein_invalid_vat', self::vat_error() );
	}

	public static function save_classic( WC_Order $order, array $data ): void {
		self::save(
			$order,
			isset( $data['hein_vat_number'] ) ? (string) $data['hein_vat_number'] : null,
			isset( $data['hein_po_reference'] ) ? (string) $data['hein_po_reference'] : null
		);
	}

	/**
	 * The documented Blocks seam: it passes the value in, so it needs no assumption about
	 * WooCommerce's internal persist ordering, and it also fires on admin and REST edits
	 */
	public static function mirror_block_field( string $key, $value, string $group, $wc_object ): void {
		if ( ! $wc_object instanceof WC_Order ) {
			return;
		}

		if ( self::VAT_FIELD === $key ) {
			self::save( $wc_object, (string) $value, null );
		} elseif ( self::PO_FIELD === $key ) {
			self::save( $wc_object, null, (string) $value );
		}
	}

	/**
	 * The single writer. It never calls $order->save(): every caller is on a path where WooCommerce
	 * (or HEIN_Order_Actions) persists the order immediately afterwards
	 */
	public static function save( WC_Order $order, ?string $vat, ?string $po ): void {
		$blocks = function_exists( 'woocommerce_register_additional_checkout_field' );

		if ( null !== $vat ) {
			$country   = (string) $order->get_billing_country();
			$canonical = HEIN_VAT::canonical( $vat, $country );

			// The admin metabox has no validity gate of its own, and canonical() invents a country
			// prefix for bare digits, so unchecked junk would be stored looking like a real VAT id
			if ( ! HEIN_VAT::is_valid_for( $canonical, $country ) ) {
				$canonical = HEIN_VAT::normalize( $vat );
			}

			$order->update_meta_data( '_hein_vat_number', $canonical );

			if ( $blocks ) {
				$order->update_meta_data( '_wc_other/' . self::VAT_FIELD, $canonical );
			}
		}

		if ( null !== $po ) {
			$reference = sanitize_text_field( $po );

			$order->update_meta_data( '_hein_po_reference', $reference );

			if ( $blocks ) {
				$order->update_meta_data( '_wc_other/' . self::PO_FIELD, $reference );
			}
		}
	}

	/**
	 * The Blocks API writes its OWN meta key, so these are the only readers in the plugin and they
	 * fall back to it: an order placed on the block checkout, imported, or created over REST must
	 * resolve to the same value as a classic one
	 */
	public static function vat( WC_Order $order ): string {
		return self::read( $order, '_hein_vat_number', '_wc_other/' . self::VAT_FIELD );
	}

	public static function po( WC_Order $order ): string {
		return self::read( $order, '_hein_po_reference', '_wc_other/' . self::PO_FIELD );
	}

	/**
	 * Derived on read: a _hein_vat_valid meta snapshot goes stale the moment an admin edits the
	 * number, and never exists at all on an imported or REST-created order
	 */
	public static function is_vat_valid( WC_Order $order ): bool {
		return HEIN_VAT::is_valid_for( self::vat( $order ), (string) $order->get_billing_country() );
	}

	/**
	 * WooCommerce 8.0 to 8.8 is a supported configuration, so this is a standing condition rather
	 * than an error: shown only where it is actionable, and dismissed for good once acknowledged
	 */
	public static function block_api_notice(): void {
		if ( ! current_user_can( 'manage_woocommerce' ) || ! self::notice_screen() ) {
			return;
		}

		if ( get_user_meta( get_current_user_id(), self::DISMISSED, true ) ) {
			return;
		}

		$dismiss = wp_nonce_url(
			admin_url( 'admin-post.php?action=hein_dismiss_block_notice' ),
			'hein_dismiss_block_notice'
		);

		echo '<div class="notice notice-warning"><p>'
			. esc_html__(
				'Harbor EU E-Invoicing needs WooCommerce 8.9 or later to show the VAT number field on the block checkout. Update WooCommerce, or use the shortcode checkout.',
				'harbor-eu-einvoicing-for-woocommerce'
			)
			. ' <a href="' . esc_url( $dismiss ) . '">'
			. esc_html__( 'Dismiss this notice.', 'harbor-eu-einvoicing-for-woocommerce' )
			. '</a></p></div>';
	}

	public static function dismiss_block_api_notice(): void {
		check_admin_referer( 'hein_dismiss_block_notice' );

		if ( current_user_can( 'manage_woocommerce' ) ) {
			update_user_meta( get_current_user_id(), self::DISMISSED, 1 );
		}

		$referer = wp_get_referer();

		wp_safe_redirect( $referer ? $referer : admin_url() );
		exit;
	}

	private static function read( WC_Order $order, string $key, string $fallback ): string {
		$value = trim( (string) $order->get_meta( $key ) );

		return '' !== $value ? $value : trim( (string) $order->get_meta( $fallback ) );
	}

	private static function notice_screen(): bool {
		$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;

		if ( ! $screen ) {
			return false;
		}

		$screens = [ 'plugins', 'woocommerce_page_hein-settings', 'shop_order' ];

		if ( function_exists( 'wc_get_page_screen_id' ) ) {
			$screens[] = wc_get_page_screen_id( 'shop-order' );
		}

		return in_array( $screen->id, $screens, true );
	}

	private static function field_id( $field ): string {
		return is_array( $field ) ? (string) ( $field['id'] ?? '' ) : '';
	}

	/**
	 * Bare digits are accepted: canonical() prepends the billing country's prefix, and a number that
	 * still fails is dropped at generation rather than blocking the checkout
	 */
	private static function vat_acceptable( string $value ): bool {
		$vat = HEIN_VAT::normalize( $value );

		if ( '' === $vat || ! preg_match( '/^[A-Z]{2}/', $vat ) ) {
			return true;
		}

		return HEIN_VAT::is_valid_format( $vat );
	}

	private static function vat_error(): string {
		return __(
			'Enter a valid EU VAT number, for example DE123456789.',
			'harbor-eu-einvoicing-for-woocommerce'
		);
	}
}
