<?php

namespace Simplyin\Simplyin_W_Plugin;

use WC_Order;

class Tracking_Numbers {
	private const TRACKING_NUMBER_PATTERN           = '/^[0-9A-Z]{10,30}$/';
	private const SHIPMENT_POST_TYPE                = 'shipment';
	private const SHIPMENT_META_INTEGRATION         = '_integration';
	private const SHIPMENT_META_DHL_TRACKING_NUMBER = 'parcel_number';
	private const DHL_INTEGRATIONS                  = array( 'dhl', 'dhl_polska' );


	public function find_tracking_numbers( WC_Order $order ): array {
		$tracking_numbers = $this->get_tracking_numbers_meta( $order );
		$log              = simplyin()
			->get_simplyin_logger();

		if ( empty( $tracking_numbers ) ) {
			$log->log_if_wpdebug_on(
				sprintf(
					'[Tracking_Numbers] [find_tracking_numbers] Not found. Now search for InPost numbers [%s]',
					wp_json_encode(
						array(
							'order_id' => $order->get_id(),
						)
					)
				)
			);
			$tracking_numbers = $this->find_other_tracking_numbers( $order );

			if ( ! empty( $tracking_numbers ) ) {
				$this->update_tracking_numbers_meta(
					$tracking_numbers,
					$order
				);
			}
		}

		$log->log_if_wpdebug_on(
			sprintf(
				'[Tracking_Numbers] [find_tracking_numbers] [%s]',
				wp_json_encode(
					array(
						'result'   => $tracking_numbers,
						'order_id' => $order->get_id(),
					)
				)
			)
		);

		return $tracking_numbers;
	}

	private function build_tracking_numbers_collection(
		array $tracking_numbers,
		string $provider
	): array {
		$tracking_numbers = array_values(
			array_unique(
				$tracking_numbers,
				SORT_REGULAR
			)
		);

		return array_map(
			function ( $tracking_number ) use (
				$provider
			) {
				return array(
					'number'   => $tracking_number,
					'provider' => $provider,
				);
			},
			$tracking_numbers
		);
	}

	private function update_tracking_numbers_meta(
		array $tracking_numbers,
		WC_Order $order
	) {
		$order->update_meta_data(
			'_simplyin_tracking_numbers',
			$tracking_numbers
		);
		$order->save_meta_data();

		simplyin()
			->get_simplyin_logger()
			->log_if_wpdebug_on(
				sprintf(
					'[Tracking_Numbers] [update_tracking_numbers_meta] [%s]',
					wp_json_encode(
						array(
							'result'   => $tracking_numbers,
							'order_id' => $order->get_id(),
						)
					)
				)
			);
	}

	public function update_tracking_numbers(
		array $tracking_numbers,
		string $provider,
		WC_Order $order
	) {

		$tracking_numbers_collection = $this->build_tracking_numbers_collection(
			$tracking_numbers,
			$provider
		);

		$this->update_tracking_numbers_meta(
			$tracking_numbers_collection,
			$order
		);
	}

	private function get_tracking_numbers_meta(
		WC_Order $order
	): array {
		$found = $order->get_meta( '_simplyin_tracking_numbers' );

		if ( is_array( $found ) && ! empty( $found ) ) {
			return $found;
		}

		return array();
	}

	private function find_other_tracking_numbers( WC_Order $order ): array {
		$id  = $order->get_id();
		$log = simplyin()
			->get_simplyin_logger();

		$log->log_if_wpdebug_on(
			sprintf(
				'[Tracking_Numbers] [find_inpost_tracking_numbers] [%s]',
				wp_json_encode(
					array(
						'collect:start #' => $id,
					)
				)
			)
		);

		$found = array();

		if ( $raw = $order->get_meta( '_shipx_shipment_object', true ) ) {

			if ( is_object( $raw ) ) {
				$shipment = $raw;
			} else {
				// allowed_classes:true is intentional because _shipx_shipment_object is provided by InPost ShipX.
				// A strict allowlist requires stable ShipX class names; false would break dynamic method dispatch.
				$shipment = @unserialize( $raw, array( 'allowed_classes' => true ) );
			}

			if ( $shipment && is_object( $shipment ) ) {

				$internal   = null;
				$gettersInt = array( 'get_internal_data', 'getInternalData' );
				foreach ( $gettersInt as $g ) {
					if ( method_exists( $shipment, $g ) ) {
						$internal = $shipment->$g();
						break;
					}
				}
				$internal ??= $shipment->internal_data ?? null;

				if ( $internal && is_object( $internal ) ) {
					foreach (
						array(
							'get_tracking_number',
							'getTrackingNumber',
						) as $g
					) {
						if ( method_exists( $internal, $g ) ) {
							$tn = $internal->$g();
							break;
						}
					}
					$tn ??= $internal->tracking_number ?? null;

					if ( is_string( $tn ) && $this->is_valid_tracking_number( $tn ) ) {
						$found[] = array(
							'number'   => $tn,
							'provider' => 'inpost',
						);

						$log->log_if_wpdebug_on(
							sprintf(
								'[Tracking_Numbers] [find_inpost_tracking_numbers] [%s]',
								wp_json_encode(
									array(
										'collect:found shipx' => $tn,
									)
								)
							)
						);
					}
				}
			}
		}

		foreach ( $order->get_items( 'shipping' ) as $item ) {
			foreach (
				array(
					'_paczkomat_shipment_id',
					'_paczkomat_tracking_number',
				) as $key
			) {
				if ( $tn = $item->get_meta( $key, true ) ) {
					if ( $this->is_valid_tracking_number( $tn ) ) {
						$found[] = array(
							'number'   => $tn,
							'provider' => 'inpost',
						);

						$log->log_if_wpdebug_on(
							sprintf(
								'[Tracking_Numbers] [find_inpost_tracking_numbers] [%s]',
								wp_json_encode(
									array(
										'collect:found paczkomat' => $tn,
									)
								)
							)
						);
					}
				}
			}
		}

		if ( $items = $order->get_meta(
			'_wc_shipment_tracking_items',
			true
		) ) {
			foreach ( (array) $items as $i ) {
				if ( ! empty( $i['tracking_number'] ) && $this->is_valid_tracking_number( (string) $i['tracking_number'] ) ) {
					$found[] = array(
						'number'   => $i['tracking_number'],
						'provider' => $i['carrier_slug'] ?? '',
					);

					$log->log_if_wpdebug_on(
						sprintf(
							'[Tracking_Numbers] [find_inpost_tracking_numbers] [%s]',
							wp_json_encode(
								array(
									'collect:found wc_ship' => $i['tracking_number'],
								)
							)
						)
					);

				}
			}
		}

		$found = array_merge( $found, $this->find_dhl_tracking_numbers_from_shipments( $order ) );

		$found = array_values( array_unique( $found, SORT_REGULAR ) );

		$log->log_if_wpdebug_on(
			sprintf(
				'[Tracking_Numbers] [find_inpost_tracking_numbers] [%s]',
				wp_json_encode(
					array(
						'collect:result' => $found,
					)
				)
			)
		);

		return $found;
	}

	private function find_dhl_tracking_numbers_from_shipments( WC_Order $order ): array {
		$results = array();
		$posts   = get_posts(
			array(
				'post_type'      => self::SHIPMENT_POST_TYPE,
				'post_parent'    => $order->get_id(),
				'post_status'    => 'any',
				'posts_per_page' => -1,
				'fields'         => 'ids',
			)
		);

		if ( empty( $posts ) || ! is_array( $posts ) ) {
			return $results;
		}

		foreach ( $posts as $shipment_id ) {
			$integration = (string) get_post_meta( (int) $shipment_id, self::SHIPMENT_META_INTEGRATION, true );
			if ( ! in_array( $integration, self::DHL_INTEGRATIONS, true ) ) {
				continue;
			}

			$tracking_number = (string) get_post_meta( (int) $shipment_id, self::SHIPMENT_META_DHL_TRACKING_NUMBER, true );
			if ( '' === $tracking_number ) {
				continue;
			}

			if ( $this->is_valid_tracking_number( $tracking_number ) ) {
				$results[] = array(
					'number'   => $tracking_number,
					'provider' => 'dhl',
				);
			}
		}

		return $results;
	}

	private function is_valid_tracking_number( string $tracking_number ): bool {
		return 1 === preg_match( self::TRACKING_NUMBER_PATTERN, $tracking_number );
	}
}
