<?php
/**
 * Address Validation module.
 *
 * @package Kramar
 * @since   1.0.0
 */

declare( strict_types=1 );

namespace Kramar\Modules\Checkout;

// Prevent direct access.
defined( 'ABSPATH' ) || exit;

/**
 * Address Validation module.
 *
 * Validates that billing and shipping addresses contain a house number (at least
 * one digit). Prevents incomplete addresses that cause delivery failures.
 *
 * @since 1.0.0
 */
final class Address_Validation extends \Kramar\Module {

	/**
	 * {@inheritDoc}
	 */
	public function get_slug(): string {
		return 'address-validation';
	}

	/**
	 * {@inheritDoc}
	 */
	public function get_category(): string {
		return 'checkout';
	}

	/**
	 * {@inheritDoc}
	 */
	public function get_name(): string {
		return __( 'Address Validation', 'kramar' );
	}

	/**
	 * {@inheritDoc}
	 */
	public function get_description(): string {
		return __( 'Require a house number in the address field at checkout and My Account. Inline validation on Classic checkout, server-side on Blocks checkout.', 'kramar' );
	}

	/**
	 * {@inheritDoc}
	 */
	public function get_default_settings(): array {
		return [
			'enabled'   => false,
			'countries' => 'NL,BE,DE',
		];
	}

	/**
	 * {@inheritDoc}
	 */
	public function sanitize_settings( array $input ): array {
		$raw = sanitize_text_field( $input['countries'] ?? 'NL,BE,DE' );
		$codes = implode( ',', array_filter( array_map( 'trim', explode( ',', strtoupper( $raw ) ) ) ) );

		return [
			'enabled'   => ! empty( $input['enabled'] ),
			'countries' => '' !== $codes ? $codes : 'NL,BE,DE',
		];
	}

	/**
	 * {@inheritDoc}
	 */
	protected function run(): void {
		add_action( 'woocommerce_after_checkout_validation', [ $this, 'validate_checkout' ], 10, 2 );
		add_action( 'woocommerce_checkout_validate_order_before_payment', [ $this, 'validate_blocks_checkout' ], 10, 2 );
		add_action( 'woocommerce_after_save_address_validation', [ $this, 'validate_account_address' ], 10, 3 );
		add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_checkout_assets' ] );
	}

	/**
	 * Validate addresses at checkout.
	 *
	 * @since 1.0.0
	 *
	 * @param array<string, mixed> $data   Checkout posted data.
	 * @param \WP_Error            $errors Error collector.
	 */
	public function validate_checkout( array $data, \WP_Error $errors ): void {
		$countries = $this->get_country_list();

		if ( isset( $data['billing_country'], $data['billing_address_1'] ) && in_array( $data['billing_country'], $countries, true ) ) {
			if ( ! $this->has_digit( $data['billing_address_1'] ) ) {
				$errors->add(
					'kramar_billing_house_number',
					__( 'Please include your house number in the billing address.', 'kramar' )
				);
			}
		}

		$ship_different = ! empty( $data['ship_to_different_address'] );
		if ( $ship_different && isset( $data['shipping_country'], $data['shipping_address_1'] ) && in_array( $data['shipping_country'], $countries, true ) ) {
			if ( ! $this->has_digit( $data['shipping_address_1'] ) ) {
				$errors->add(
					'kramar_shipping_house_number',
					__( 'Please include your house number in the shipping address.', 'kramar' )
				);
			}
		}
	}

	/**
	 * Validate addresses in the Blocks checkout (Store API).
	 *
	 * @since 1.0.0
	 *
	 * @param \WC_Order $order  The order object with address data already set.
	 * @param \WP_Error $errors Validation error collector.
	 */
	public function validate_blocks_checkout( \WC_Order $order, \WP_Error $errors ): void {
		$countries = $this->get_country_list();

		if ( in_array( $order->get_billing_country(), $countries, true ) ) {
			if ( ! $this->has_digit( $order->get_billing_address_1() ) ) {
				$errors->add(
					'kramar_billing_house_number',
					__( 'Please include your house number in the billing address.', 'kramar' )
				);
			}
		}

		if ( $order->has_shipping_address() && in_array( $order->get_shipping_country(), $countries, true ) ) {
			if ( ! $this->has_digit( $order->get_shipping_address_1() ) ) {
				$errors->add(
					'kramar_shipping_house_number',
					__( 'Please include your house number in the shipping address.', 'kramar' )
				);
			}
		}
	}

	/**
	 * Validate address on My Account save.
	 *
	 * @since 1.0.0
	 *
	 * @param int                  $user_id      User ID.
	 * @param string               $address_type 'billing' or 'shipping'.
	 * @param array<string, mixed> $address      Address fields.
	 */
	public function validate_account_address( int $user_id, string $address_type, array $address ): void {
		$country = $address[ $address_type . '_country' ] ?? '';
		$street  = $address[ $address_type . '_address_1' ] ?? '';

		if ( '' !== $country && in_array( $country, $this->get_country_list(), true ) && ! $this->has_digit( $street ) ) {
			wc_add_notice(
				__( 'Please include your house number in the address field.', 'kramar' ),
				'error'
			);
		}
	}

	/**
	 * Check if a string contains at least one digit.
	 *
	 * @since 1.0.0
	 *
	 * @param string $value Value to check.
	 * @return bool True when at least one digit is present.
	 */
	private function has_digit( string $value ): bool {
		return (bool) preg_match( '/\d/', $value );
	}

	/**
	 * Enqueue checkout address validation script on the checkout page.
	 *
	 * @since 1.0.0
	 */
	public function enqueue_checkout_assets(): void {
		if ( ! is_checkout() ) {
			return;
		}
		wp_enqueue_script(
			'kramar-address-validation',
			KRAMAR_URL . 'assets/js/address-validation.js',
			[],
			KRAMAR_VERSION,
			true
		);
		wp_localize_script(
			'kramar-address-validation',
			'kramarAddressValidation',
			[
				'countries' => $this->get_country_list(),
				'msg'       => __( 'Please include your house number in the address.', 'kramar' ),
			]
		);
	}

	/**
	 * Get the configured country codes as an array.
	 *
	 * @since 1.0.0
	 *
	 * @return array<int, string> Array of uppercase country codes.
	 */
	private function get_country_list(): array {
		$raw = (string) $this->plugin->get_setting( $this->get_category(), $this->get_slug(), 'countries' );
		return array_filter( array_map( 'trim', explode( ',', strtoupper( $raw ) ) ) );
	}

	/**
	 * {@inheritDoc}
	 */
	public function render_extra_settings( string $option_name ): void {
		$countries = (string) $this->plugin->get_setting( $this->get_category(), $this->get_slug(), 'countries' );
		?>
		<div class="kramar-module__extra">
			<p>
				<label for="kramar-address-countries"><?php esc_html_e( 'Validate for countries', 'kramar' ); ?></label>
				<input
					type="text"
					id="kramar-address-countries"
					name="<?php echo esc_attr( $option_name ); ?>[checkout][address-validation][countries]"
					value="<?php echo esc_attr( $countries ); ?>"
					class="regular-text"
				/>
				<span class="description"><?php esc_html_e( 'Comma-separated ISO country codes. Default: NL,BE,DE — countries where house numbers are always numeric.', 'kramar' ); ?></span>
			</p>
		</div>
		<?php
	}
}
