<?php
/**
 * Tryb adresu rozliczeniowego na checkoucie (opcja gksef_billing_address_mode).
 *
 * Pozwala wyłączyć osobny adres rozliczeniowy: adres rozliczeniowy = adres dostawy
 * (jeden adres). Działa dwuwarstwowo:
 *  - WARSTWA DANYCH (server-side, uniwersalna): luzuje wymagalność pól adresu billing
 *    i kopiuje shipping → billing na zamówieniu, żeby faktura KSeF zawsze miała adres
 *    nabywcy. Działa w klasycznym checkoucie, Sellkit i WooCommerce Blocks (Store API).
 *  - WARSTWA WYGLĄDU (client-side, adaptery per checkout): chowa UI osobnego adresu
 *    rozliczeniowego (Sellkit / classic / generyczny po filtrze selektorów).
 *
 * Tryb `default` = ZERO ingerencji (żaden filtr/JS się nie podpina).
 *
 * @package KSEF\WordPress\WC
 */

declare(strict_types=1);

namespace KSEF\WordPress\WC;

final class Billing_Address_Mode {

	/** Opcja przechowująca tryb. */
	public const OPTION = 'gksef_billing_address_mode';

	/** Pola ADRESU billing (bez email/telefonu — te zostawiamy nietknięte). */
	private const ADDRESS_FIELDS = [
		'first_name',
		'last_name',
		'company',
		'address_1',
		'address_2',
		'city',
		'state',
		'postcode',
		'country',
	];

	/**
	 * Aktualny tryb: 'default' | 'disabled' | 'default_same'.
	 */
	public static function mode(): string {
		// Zahardkodowane: ZAWSZE „jeden adres" (rozliczeniowy = dostawy) — faktura bez osobnych
		// pól billingowych. Opcja w ustawieniach usunięta; moduł zawsze działa w trybie „disabled".
		return 'disabled';
	}

	public static function register(): void {
		$mode = self::mode();
		if ( 'default' === $mode ) {
			return; // zero ingerencji
		}

		// --- WARSTWA DANYCH (uniwersalna, niezależna od frontendu) ---
		add_filter( 'woocommerce_checkout_fields', [ self::class, 'relax_checkout_fields' ], 1000 );
		add_filter( 'woocommerce_billing_fields', [ self::class, 'relax_billing_fields' ], 1000 );
		// Tryb „disabled": adres rozliczeniowy ZAWSZE = dostawy. Nadpisujemy pola billing
		// wartościami z shipping w POST-data PRZED walidacją — inaczej WooCommerce waliduje
		// format (np. billing_postcode) na surowej/ukrytej wartości i blokuje złożenie zamówienia.
		if ( 'disabled' === $mode ) {
			add_filter( 'woocommerce_checkout_posted_data', [ self::class, 'copy_posted_shipping_to_billing' ], 1000 );
		}
		// Kopiowanie shipping → billing PRZED zapisem zamówienia (faktura KSeF wymaga adresu nabywcy).
		add_action( 'woocommerce_checkout_create_order', [ self::class, 'copy_shipping_to_billing' ], 5, 2 );
		// Blocks / Store API — ta sama operacja przez dedykowany hook.
		add_action( 'woocommerce_store_api_checkout_update_order_from_request', [ self::class, 'copy_shipping_to_billing' ], 5, 2 );

		// --- WARSTWA WYGLĄDU (client-side, adaptery) ---
		add_action( 'wp_enqueue_scripts', [ self::class, 'enqueue_assets' ] );
	}

	/**
	 * Luzuje `required` dla pól adresu billing w tablicy woocommerce_checkout_fields.
	 *
	 * @param array<string,mixed> $fields
	 * @return array<string,mixed>
	 */
	public static function relax_checkout_fields( $fields ) {
		$disabled = ( 'disabled' === self::mode() );
		if ( isset( $fields['billing'] ) && is_array( $fields['billing'] ) ) {
			foreach ( self::ADDRESS_FIELDS as $f ) {
				$key = 'billing_' . $f;
				if ( isset( $fields['billing'][ $key ] ) && is_array( $fields['billing'][ $key ] ) ) {
					$fields['billing'][ $key ]['required'] = false;
					if ( $disabled ) {
						// billing = dostawa — nie waliduj formatu (postcode/email itd.) ukrytych pól.
						$fields['billing'][ $key ]['validate'] = [];
					}
				}
			}
		}
		return $fields;
	}

	/**
	 * Luzuje `required` w woocommerce_billing_fields (używane m.in. w My Account / niektórych motywach).
	 *
	 * @param array<string,mixed> $fields
	 * @return array<string,mixed>
	 */
	public static function relax_billing_fields( $fields ) {
		$disabled = ( 'disabled' === self::mode() );
		if ( is_array( $fields ) ) {
			foreach ( self::ADDRESS_FIELDS as $f ) {
				$key = 'billing_' . $f;
				if ( isset( $fields[ $key ] ) && is_array( $fields[ $key ] ) ) {
					$fields[ $key ]['required'] = false;
					if ( $disabled ) {
						$fields[ $key ]['validate'] = [];
					}
				}
			}
		}
		return $fields;
	}

	/**
	 * Czy zamówienie/żądanie jest firmowe (NIP)? Wtedy adres rozliczeniowy = dane firmy (GUS),
	 * a nie adres dostawy — więc kopiowanie dostawa→billing musi być pominięte.
	 */
	private static function is_business_order_ctx( \WC_Order $order ): bool {
		$nip_meta = preg_replace( '/\D+/', '', (string) $order->get_meta( '_billing_nip' ) );
		if ( 10 === strlen( (string) $nip_meta ) ) {
			return true;
		}
		if ( 'business' === (string) $order->get_meta( '_ksef_customer_type' ) ) {
			return true;
		}
		// Na etapie create_order (prio 5) meta NIP może nie być jeszcze zapisane — czytamy POST.
		$nip_post = preg_replace( '/\D+/', '', sanitize_text_field( wp_unslash( (string) ( $_POST['billing_ksef_nip'] ?? '' ) ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
		return ! empty( $_POST['billing_ksef_request_invoice'] ) && 10 === strlen( (string) $nip_post ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
	}

	/**
	 * Kopiuje adres dostawy → adres rozliczeniowy na zamówieniu, jeśli billing jest pusty,
	 * a shipping wypełniony. Dla produktów wirtualnych (brak shipping) nie nadpisuje niczego.
	 * Wspólne dla checkoutu klasycznego/Sellkit (woocommerce_checkout_create_order) i
	 * Blocks/Store API (woocommerce_store_api_checkout_update_order_from_request).
	 *
	 * @param mixed $order WC_Order.
	 * @param mixed $ctx   $data (classic) lub $request (Store API) — nieużywane.
	 */
	public static function copy_shipping_to_billing( $order, $ctx = null ): void {
		if ( ! $order instanceof \WC_Order ) {
			return;
		}
		$disabled = ( 'disabled' === self::mode() );
		// Zamówienie firmowe (NIP) → billing = dane firmy (ustawiane z GUS w Checkout_Field),
		// NIE nadpisuj adresem dostawy.
		if ( self::is_business_order_ctx( $order ) ) {
			return;
		}
		foreach ( self::ADDRESS_FIELDS as $f ) {
			$bget = 'get_billing_' . $f;
			$sget = 'get_shipping_' . $f;
			$bset = 'set_billing_' . $f;
			if ( ! method_exists( $order, $bget ) || ! method_exists( $order, $sget ) || ! method_exists( $order, $bset ) ) {
				continue;
			}
			$bval = trim( (string) $order->{$bget}() );
			$sval = trim( (string) $order->{$sget}() );
			// „disabled": billing ZAWSZE = shipping (overwrite). „default_same": tylko gdy billing pusty.
			// Produkty wirtualne (brak shipping) — nie nadpisujemy.
			if ( '' !== $sval && ( $disabled || '' === $bval ) ) {
				$order->{$bset}( $sval );
			}
		}
	}

	/**
	 * Tryb „disabled": nadpisuje pola billing wartościami z shipping w POST-data checkoutu
	 * PRZED walidacją WooCommerce (woocommerce_checkout_posted_data), żeby walidacja formatu
	 * (postcode/email itd.) widziała poprawny adres dostawy, a nie surową/ukrytą wartość billing.
	 * Kopiuje tylko, gdy shipping ma wartość (produkty wirtualne bez dostawy — bez zmian).
	 *
	 * @param mixed $data Tablica POST-data checkoutu.
	 * @return mixed
	 */
	public static function copy_posted_shipping_to_billing( $data ) {
		if ( ! is_array( $data ) ) {
			return $data;
		}
		// Zamówienie firmowe (NIP) → adres rozliczeniowy = dane firmy (z GUS), NIE dostawa.
		$nip = preg_replace( '/\D+/', '', (string) ( $data['billing_ksef_nip'] ?? '' ) );
		if ( ! empty( $data['billing_ksef_request_invoice'] ) && 10 === strlen( (string) $nip ) ) {
			return $data;
		}
		foreach ( self::ADDRESS_FIELDS as $f ) {
			$bkey = 'billing_' . $f;
			$skey = 'shipping_' . $f;
			if ( isset( $data[ $skey ] ) && '' !== trim( (string) $data[ $skey ] ) ) {
				$data[ $bkey ] = $data[ $skey ];
			}
		}
		return $data;
	}

	/**
	 * Wgrywa skrypt adapterów wyglądu (tylko gdy tryb != default). Skrypt sam wykrywa
	 * typ checkoutu (Sellkit / classic / generyczny) i jest idempotentny.
	 */
	public static function enqueue_assets(): void {
		if ( is_admin() ) {
			return;
		}
		// Nie ładuj w edytorze/podglądzie Elementora — frontowy obserwator DOM wieszał edytor.
		if ( class_exists( __NAMESPACE__ . '\\Checkout_Field' ) && Checkout_Field::is_page_builder_editor() ) {
			return;
		}
		wp_enqueue_script(
			'ksef-billing-mode',
			KSEF_WORDPRESS_URL . 'assets/js/billing-address-mode.js',
			[],
			defined( 'KSEF_WORDPRESS_VERSION' ) ? KSEF_WORDPRESS_VERSION : '1.0',
			true
		);
		/**
		 * Dodatkowe selektory CSS do ukrycia sekcji adresu rozliczeniowego w nietypowych
		 * checkoutach (CartFlows/FunnelKit/CheckoutWC itd.) — bez edycji rdzenia.
		 *
		 * @param string[] $selectors
		 */
		$selectors = apply_filters( 'ksef_billing_hide_selectors', [] );
		$selectors = is_array( $selectors ) ? array_values( array_filter( array_map( 'strval', $selectors ) ) ) : [];
		wp_localize_script( 'ksef-billing-mode', 'KSEF_BILLING_MODE', [
			'mode'           => self::mode(),
			'hide_selectors' => $selectors,
		] );
	}
}
