<?php
/**
 * Pole NIP w checkout WooCommerce + zapis do order meta.
 *
 * Jeśli klient poda NIP → faktura traktowana jako firmowa (Podmiot2/DaneIdentyfikacyjne z NIP).
 * Brak NIPu → faktura uproszczona/imienna (Podmiot2/DaneIdentyfikacyjneOsoba — imię/nazwisko).
 *
 * @package KSEF\WordPress\WC
 */

declare(strict_types=1);

namespace KSEF\WordPress\WC;

// phpcs:disable WordPress.Security.NonceVerification -- Wlasne tabele gksef_*: wartosci przez $wpdb->prepare()/esc_like(), nazwy tabel z $wpdb->prefix, paginacja rzutowana (int); nonce weryfikowany w handlerze (check_ajax_referer/check_admin_referer).

final class Checkout_Field {

	private const NIP_FIELD       = 'billing_ksef_nip';
	private const REQUEST_INVOICE = 'billing_ksef_request_invoice';
	private const INVOICE_TYPE    = 'billing_ksef_invoice_type'; // 'company' | 'person'

	public static function register(): void {
		// Pola faktury (checkbox + NIP) renderowane NIEZALEŻNIE od adresu rozliczeniowego — poza
		// grupą billing (po notatkach do zamówienia), żeby przełącznik „adres rozliczeniowy = jak
		// dostawy" (Sellkit/klasyczny) nie chował opcji faktury. Sellkit one-page bez tego hooka
		// pokrywa fallback-injector w JS (wstrzyknięcie przy mailu).
		add_action( 'woocommerce_after_order_notes', [ self::class, 'render_invoice_fields' ] );
		add_action( 'woocommerce_checkout_create_order', [ self::class, 'save_to_order' ], 10, 2 );
		add_filter( 'woocommerce_admin_billing_fields', [ self::class, 'admin_billing_fields' ] );

		// Uniwersalny injector w koszyk/checkout (CartFlows / Blocks / external)
		add_action( 'wp_enqueue_scripts', [ self::class, 'enqueue_injector' ] );

		// Fallback dla zewnętrznych checkoutów które nie uderzają w woocommerce_checkout_create_order:
		// łapiemy ostatecznie przy zapisie zamówienia.
		add_action( 'woocommerce_new_order', [ self::class, 'save_on_new_order' ], 20, 2 );

		// Walidacja: NIP wymagany tylko, gdy zaznaczono „Chcę fakturę VAT" (filtr ksef_checkout_require_nip).
		add_action( 'woocommerce_after_checkout_validation', [ self::class, 'validate_nip' ], 10, 2 );
	}

	/**
	 * Walidacja NIP-u na checkoucie: wymagany (10 cyfr) tylko, gdy zaznaczono „Chcę fakturę VAT".
	 * Można wyłączyć filtrem ksef_checkout_require_nip (np. by dopuścić faktury imienne bez NIP).
	 *
	 * @param array<string,mixed> $data   Dane checkoutu (nieużywane — czytamy $_POST).
	 * @param mixed               $errors WP_Error checkoutu.
	 */
	public static function validate_nip( $data = [], $errors = null ): void {
		if ( empty( $_POST[ self::REQUEST_INVOICE ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- walidacja checkoutu WC (nonce po stronie WC).
			return; // checkbox niezaznaczony — NIP nieobowiązkowy
		}
		// Faktura na osobę fizyczną (imienna) — NIP niewymagany.
		$type = sanitize_text_field( wp_unslash( $_POST[ self::INVOICE_TYPE ] ?? 'company' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
		if ( 'person' === $type ) {
			return;
		}
		if ( ! (bool) apply_filters( 'ksef_checkout_require_nip', true ) ) {
			return;
		}
		$nip = preg_replace( '/\D+/', '', (string) sanitize_text_field( wp_unslash( $_POST[ self::NIP_FIELD ] ?? '' ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
		$msg = '';
		if ( '' === (string) $nip ) {
			$msg = __( 'Zaznaczyłeś „Chcę otrzymać fakturę VAT" — podaj NIP firmy (10 cyfr).', 'prostafaktura-ksef' );
		} elseif ( strlen( (string) $nip ) !== 10 ) {
			$msg = __( 'NIP musi mieć 10 cyfr.', 'prostafaktura-ksef' );
		}
		if ( '' === $msg ) {
			return;
		}
		if ( $errors instanceof \WP_Error ) {
			$errors->add( 'ksef_nip', $msg );
		} elseif ( function_exists( 'wc_add_notice' ) ) {
			wc_add_notice( $msg, 'error' );
		}
	}

	/**
	 * Wgrywa uniwersalny JS który wstrzykuje pole NIP gdziekolwiek znajdzie checkout.
	 */
	/**
	 * Czy bieżące żądanie to edytor/podgląd page-buildera (Elementor)? Skrypty checkoutu z
	 * MutationObserverem nie mogą się tam ładować — reagują na mutacje DOM edytora i go wieszają.
	 */
	public static function is_page_builder_editor(): bool {
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- tylko detekcja kontekstu (podgląd Elementora).
		if ( isset( $_GET['elementor-preview'] ) || isset( $_GET['elementor_library'] ) || isset( $_GET['preview_id'] ) ) {
			return true;
		}
		if ( class_exists( '\\Elementor\\Plugin' ) ) {
			$ep = \Elementor\Plugin::$instance;
			if ( isset( $ep->preview ) && method_exists( $ep->preview, 'is_preview_mode' ) && $ep->preview->is_preview_mode() ) {
				return true;
			}
			if ( isset( $ep->editor ) && method_exists( $ep->editor, 'is_edit_mode' ) && $ep->editor->is_edit_mode() ) {
				return true;
			}
		}
		return false;
	}

	public static function enqueue_injector(): void {
		// Tylko frontend (nie admin), nie w panelu logowania
		if ( is_admin() ) {
			return;
		}
		// Nie ładuj w edytorze/podglądzie Elementora — frontowy obserwator DOM wieszał edytor.
		if ( self::is_page_builder_editor() ) {
			return;
		}
		// Skrypt jest mały i idempotentny — wgrywamy globalnie. JS sam wykryje czy strona ma checkout.
		wp_enqueue_script(
			'ksef-checkout-nip-inject',
			KSEF_WORDPRESS_URL . 'assets/js/checkout-nip-inject.js',
			[],
			defined( 'KSEF_WORDPRESS_VERSION' ) ? KSEF_WORDPRESS_VERSION : '1.0',
			true
		);
		// CSS pól faktury na checkoucie (dawniej inline znacznik w treści formularza) —
		// ukrywa dopisek „(opcjonalne)" przy naszych polach. Teraz przez frontowy enqueue (head).
		wp_register_style( 'ksef-checkout-fields', false, [], defined( 'KSEF_WORDPRESS_VERSION' ) ? KSEF_WORDPRESS_VERSION : '1.0' );
		wp_enqueue_style( 'ksef-checkout-fields' );
		wp_add_inline_style( 'ksef-checkout-fields', '#billing_ksef_request_invoice_field .optional,#billing_ksef_invoice_type_field .optional,#billing_ksef_nip_field .optional{display:none}' );
		// GUS (auto-uzupełnianie danych firmy z rejestru po NIP) to funkcja wersji Pro
		// (wymaga zewnętrznej usługi/proxy GUS) — w wersji darmowej wyłączona.
		wp_localize_script( 'ksef-checkout-nip-inject', 'KSEF_CHECKOUT_INJECT', [
			'label_checkbox' => __( 'Chcę otrzymać fakturę VAT', 'prostafaktura-ksef' ),
			'label_type'         => __( 'Rodzaj faktury', 'prostafaktura-ksef' ),
			'label_type_company' => __( 'Na firmę (z NIP)', 'prostafaktura-ksef' ),
			'label_type_person'  => __( 'Na osobę fizyczną (bez NIP)', 'prostafaktura-ksef' ),
			'label_person_other'  => __( 'Faktura na inne dane niż dane zamówienia', 'prostafaktura-ksef' ),
			'label_person_name'   => __( 'Imię i nazwisko', 'prostafaktura-ksef' ),
			'label_person_street' => __( 'Ulica i numer', 'prostafaktura-ksef' ),
			'label_person_zip'    => __( 'Kod pocztowy', 'prostafaktura-ksef' ),
			'label_person_city'   => __( 'Miejscowość', 'prostafaktura-ksef' ),
			'label_nip'      => __( 'Wpisz numer NIP', 'prostafaktura-ksef' ),
			'placeholder'    => __( '10 cyfr, bez kresek', 'prostafaktura-ksef' ),
			'ajax_url'       => admin_url( 'admin-ajax.php' ),
			'gus_enabled'    => 0,
		] );
	}

	/**
	 * Bezpiecznik: gdy zamówienie powstaje przez zewnętrzny checkout (CartFlows, Store API),
	 * sprawdź $_POST i zapisz NIP/flag jeśli przyszły, a nie były obsłużone przez natywny filter.
	 */
	public static function save_on_new_order( int $order_id, $order = null ): void {
		if ( ! $order instanceof \WC_Order ) {
			$order = wc_get_order( $order_id );
		}
		if ( ! $order || $order->get_meta( '_ksef_request_invoice' ) ) {
			return; // już zapisane
		}
		$request = ! empty( $_POST[ self::REQUEST_INVOICE ] ) ? 1 : 0;
		$nip     = preg_replace( '/\D+/', '', (string) sanitize_text_field( wp_unslash( $_POST[ self::NIP_FIELD ] ?? '' ) ) );
		$type    = sanitize_text_field( wp_unslash( $_POST[ self::INVOICE_TYPE ] ?? 'company' ) );
		if ( ! $request && '' === $nip ) {
			return;
		}
		$order->update_meta_data( '_ksef_request_invoice', $request );
		// Firma = wybrano „na firmę" + poprawny NIP. „Na osobę fizyczną" → faktura imienna (private).
		$business = ( $request && 'person' !== $type && strlen( (string) $nip ) === 10 );
		// NIP zapisujemy TYLKO gdy zaznaczono „Chcę fakturę VAT".
		if ( $business ) {
			$order->update_meta_data( '_billing_nip', $nip );
			$order->update_meta_data( '_ksef_customer_type', 'business' );
		} else {
			$order->delete_meta_data( '_billing_nip' );
			$order->update_meta_data( '_ksef_customer_type', 'private' );
		}
		if ( $business ) {
			self::save_buyer_meta( $order, true );
		} elseif ( $request && 'person' === $type ) {
			self::save_person_buyer_meta( $order );
		} else {
			self::save_buyer_meta( $order, false );
		}
		$order->save();
	}

	/**
	 * Renderuje pola „Chcę fakturę VAT" + NIP POZA grupą billing (hook woocommerce_after_order_notes).
	 * Pola mają te same id/name (billing_ksef_*), więc zapis/walidacja i JS (bindPhpFields:
	 * ukrywanie NIP + GUS) działają bez zmian. Idempotentne — renderuje tylko raz na żądanie.
	 *
	 * @param mixed $checkout Instancja WC_Checkout (z hooka) — do repopulacji po błędzie walidacji.
	 */
	public static function render_invoice_fields( $checkout = null ): void {
		static $done = false;
		if ( $done ) {
			return;
		}
		$done = true;
		if ( ! $checkout instanceof \WC_Checkout && function_exists( 'WC' ) && WC()->checkout() ) {
			$checkout = WC()->checkout();
		}
		$get = static function ( $key ) use ( $checkout ) {
			return $checkout instanceof \WC_Checkout ? $checkout->get_value( $key ) : '';
		};
		echo '<div id="ksef-invoice-fields" class="ksef-invoice-fields">';
		woocommerce_form_field(
			self::REQUEST_INVOICE,
			[
				'type'     => 'checkbox',
				'label'    => __( 'Chcę otrzymać fakturę VAT', 'prostafaktura-ksef' ),
				'class'    => [ 'form-row-wide' ],
				'priority' => 10,
			],
			$get( self::REQUEST_INVOICE )
		);
		woocommerce_form_field(
			self::INVOICE_TYPE,
			[
				'type'     => 'select',
				'label'    => __( 'Rodzaj faktury', 'prostafaktura-ksef' ),
				'options'  => [
					'company' => __( 'Na firmę (z NIP)', 'prostafaktura-ksef' ),
					'person'  => __( 'Na osobę fizyczną (bez NIP)', 'prostafaktura-ksef' ),
				],
				'class'    => [ 'form-row-wide' ],
				'priority' => 15,
			],
			$get( self::INVOICE_TYPE ) ?: 'company'
		);
		woocommerce_form_field(
			self::NIP_FIELD,
			[
				'type'              => 'text',
				'label'             => __( 'Wpisz numer NIP', 'prostafaktura-ksef' ),
				'placeholder'       => __( '10 cyfr, bez kresek', 'prostafaktura-ksef' ),
				'class'             => [ 'form-row-wide' ],
				'priority'          => 20,
				'custom_attributes' => [
					'maxlength'  => '10',
					'pattern'    => '\d{10}',
					'inputmode'  => 'numeric',
				],
			],
			$get( self::NIP_FIELD )
		);
		// Faktura imienna (osoba fizyczna) — opcja innych danych niż dane zamówienia. Pokazywane
		// przez JS gdy „Rodzaj faktury" = osoba fizyczna; pola — gdy zaznaczono „inne dane".
		$pv  = static function ( $k ) use ( $get ) { return esc_attr( (string) $get( $k ) ); };
		$inp = 'style="width:100%;margin-bottom:6px;padding:8px 10px;border:1px solid #c3c4c7;border-radius:4px;"';
		$zc  = 'padding:8px 10px;border:1px solid #c3c4c7;border-radius:4px;';
		$html  = '<div id="ksef-person-fields" class="form-row form-row-wide" style="display:none;margin-top:6px;">';
		$html .= '<label style="display:flex;align-items:center;gap:8px;cursor:pointer;font-size:14px;"><input type="checkbox" id="ksef-person-other" name="billing_ksef_person_other" value="1"' . checked( $get( 'billing_ksef_person_other' ), '1', false ) . '> <span>' . esc_html__( 'Faktura na inne dane niż dane zamówienia', 'prostafaktura-ksef' ) . '</span></label>';
		$html .= '<div id="ksef-person-other-fields" style="display:none;margin-top:8px;">';
		$html .= '<input type="text" name="billing_ksef_person_name" value="' . $pv( 'billing_ksef_person_name' ) . '" placeholder="' . esc_attr__( 'Imię i nazwisko', 'prostafaktura-ksef' ) . '" ' . $inp . '>';
		$html .= '<input type="text" name="billing_ksef_person_street" value="' . $pv( 'billing_ksef_person_street' ) . '" placeholder="' . esc_attr__( 'Ulica i numer', 'prostafaktura-ksef' ) . '" ' . $inp . '>';
		$html .= '<div style="display:flex;gap:6px;">';
		$html .= '<input type="text" name="billing_ksef_person_zip" value="' . $pv( 'billing_ksef_person_zip' ) . '" placeholder="' . esc_attr__( 'Kod pocztowy', 'prostafaktura-ksef' ) . '" style="flex:0 0 40%;' . $zc . '">';
		$html .= '<input type="text" name="billing_ksef_person_city" value="' . $pv( 'billing_ksef_person_city' ) . '" placeholder="' . esc_attr__( 'Miejscowość', 'prostafaktura-ksef' ) . '" style="flex:1;' . $zc . '">';
		$html .= '</div></div></div>';
		echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- statyczny HTML; wartości przez esc_attr/esc_html__, checked() bezpieczne.
		echo '</div>';
	}

	/**
	 * Zapis NIP-u oraz flagi "request invoice" do order meta po złożeniu zamówienia.
	 *
	 * @param \WC_Order               $order   Order object.
	 * @param array<string, mixed>   $data    Posted data.
	 */
	public static function save_to_order( $order, $data ): void {
		$request = ! empty( $_POST[ self::REQUEST_INVOICE ] ) ? 1 : 0;
		$nip     = preg_replace( '/\D+/', '', (string) sanitize_text_field( wp_unslash( $_POST[ self::NIP_FIELD ] ?? '' ) ) );
		$type    = sanitize_text_field( wp_unslash( $_POST[ self::INVOICE_TYPE ] ?? 'company' ) );

		$order->update_meta_data( '_ksef_request_invoice', $request );
		// Firma = wybrano „na firmę" + poprawny NIP. „Na osobę fizyczną" → faktura imienna (private).
		$business = ( $request && 'person' !== $type && strlen( (string) $nip ) === 10 );
		// NIP zapisujemy TYLKO gdy zaznaczono „Chcę fakturę VAT" — inaczej nie tworzymy faktury firmowej przez przypadek.
		if ( $business ) {
			$order->update_meta_data( '_billing_nip', $nip );
			$order->update_meta_data( '_ksef_customer_type', 'business' );
		} else {
			$order->delete_meta_data( '_billing_nip' );
			$order->update_meta_data( '_ksef_customer_type', 'private' );
		}
		// Dane do faktury: firma → dane firmowe (GUS); osoba „inne dane" → wpisane dane osoby;
		// osoba „takie same" → dane zamówienia (czyścimy nadpisania).
		if ( $business ) {
			self::save_buyer_meta( $order, true );
		} elseif ( $request && 'person' === $type ) {
			self::save_person_buyer_meta( $order );
		} else {
			self::save_buyer_meta( $order, false );
		}
	}

	/**
	 * Zapis danych nabywcy dla faktury imiennej (osoba fizyczna). Gdy zaznaczono „inne dane do
	 * faktury" i podano nazwę — zapisuje je do _ksef_buyer_* (faktura użyje ich, bez NIP).
	 * Inaczej czyści _ksef_buyer_* (faktura użyje danych zamówienia).
	 */
	private static function save_person_buyer_meta( \WC_Order $order ): void {
		$other = ! empty( $_POST['billing_ksef_person_other'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
		$name  = sanitize_text_field( wp_unslash( $_POST['billing_ksef_person_name'] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
		if ( ! $other || '' === trim( (string) $name ) ) {
			foreach ( [ '_ksef_buyer_name', '_ksef_buyer_street', '_ksef_buyer_zip', '_ksef_buyer_city' ] as $mk ) {
				$order->delete_meta_data( $mk );
			}
			return;
		}
		$order->update_meta_data( '_ksef_buyer_name', (string) $name );
		$order->update_meta_data( '_ksef_buyer_street', sanitize_text_field( wp_unslash( $_POST['billing_ksef_person_street'] ?? '' ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
		$order->update_meta_data( '_ksef_buyer_zip', sanitize_text_field( wp_unslash( $_POST['billing_ksef_person_zip'] ?? '' ) ) );       // phpcs:ignore WordPress.Security.NonceVerification.Missing
		$order->update_meta_data( '_ksef_buyer_city', sanitize_text_field( wp_unslash( $_POST['billing_ksef_person_city'] ?? '' ) ) );     // phpcs:ignore WordPress.Security.NonceVerification.Missing
	}

	/**
	 * Zapis ręcznie potwierdzonych/poprawionych danych firmy do faktury (pre-fill z GUS na
	 * checkoucie, ale klient mógł poprawić). Zapisywane tylko dla faktury firmowej i gdy
	 * podano nazwę — inaczej czyszczone (faktura użyje danych z GUS jak dotąd).
	 */
	private static function save_buyer_meta( \WC_Order $order, bool $business ): void {
		$map = [
			'_ksef_buyer_name'   => 'billing_ksef_buyer_name',
			'_ksef_buyer_street' => 'billing_ksef_buyer_street',
			'_ksef_buyer_zip'    => 'billing_ksef_buyer_zip',
			'_ksef_buyer_city'   => 'billing_ksef_buyer_city',
		];
		$name = $business ? sanitize_text_field( wp_unslash( $_POST['billing_ksef_buyer_name'] ?? '' ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
		if ( '' === trim( (string) $name ) ) {
			foreach ( array_keys( $map ) as $mk ) {
				$order->delete_meta_data( $mk );
			}
			return;
		}
		foreach ( $map as $mk => $pk ) {
			$order->update_meta_data( $mk, sanitize_text_field( wp_unslash( $_POST[ $pk ] ?? '' ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
		}

		// Zamówienie firmowe: ustaw pola BILLING zamówienia na dane firmy (z GUS) — panel WC
		// „Dane do faktury", is_business_order i metabox pokazują firmę. Adres DOSTAWY (shipping)
		// pozostaje osobny i nietknięty (np. punkt InPost).
		$street = sanitize_text_field( wp_unslash( $_POST['billing_ksef_buyer_street'] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
		$zip    = sanitize_text_field( wp_unslash( $_POST['billing_ksef_buyer_zip'] ?? '' ) );    // phpcs:ignore WordPress.Security.NonceVerification.Missing
		$city   = sanitize_text_field( wp_unslash( $_POST['billing_ksef_buyer_city'] ?? '' ) );   // phpcs:ignore WordPress.Security.NonceVerification.Missing
		$order->set_billing_company( (string) $name );
		if ( '' !== $street ) {
			$order->set_billing_address_1( $street );
		}
		if ( '' !== $zip ) {
			$order->set_billing_postcode( $zip );
		}
		if ( '' !== $city ) {
			$order->set_billing_city( $city );
		}
		$order->set_billing_country( 'PL' );
	}

	/**
	 * Pokazuje pole NIP w panelu admina (edytuj zamówienie → sekcja billing).
	 *
	 * @param array<string,array<string,string>> $fields
	 * @return array<string,array<string,string>>
	 */
	public static function admin_billing_fields( array $fields ): array {
		$fields['nip'] = [
			'label' => __( 'NIP', 'prostafaktura-ksef' ),
			'show'  => true,
		];
		return $fields;
	}

	public static function get_nip_from_order( \WC_Order $order ): string {
		$nip = (string) $order->get_meta( '_billing_nip' );
		if ( $nip === '' ) {
			$nip = (string) $order->get_meta( '_vat_number' ); // fallback popularny w innych pluginach
		}
		return preg_replace( '/\D+/', '', $nip ) ?? '';
	}

	public static function is_business_order( \WC_Order $order ): bool {
		// Firma = poprawny NIP (10 cyfr) LUB jawnie oznaczony typ „business". NIE wymagamy
		// billing_company — przy „jeden adres = dostawa" pole bywa puste, a nazwa firmy jest
		// w _ksef_buyer_name (z GUS). Inaczej zamówienie firmowe byłoby błędnie traktowane jako B2C.
		$nip = self::get_nip_from_order( $order );
		if ( strlen( $nip ) === 10 ) {
			return true;
		}
		if ( 'business' === (string) $order->get_meta( '_ksef_customer_type' ) ) {
			return true;
		}
		return false;
	}

	public static function customer_requested_invoice( \WC_Order $order ): bool {
		return (bool) $order->get_meta( '_ksef_request_invoice' )
			|| self::is_business_order( $order );
	}
}
