<?php
/**
 * Data Source for WooCommerce orders.
 *
 * Adapter between wc_get_orders() and the export engine.
 * Extracts all order fields and normalizes them into flat rows
 * for CSV/XLSX output.
 */

namespace RawTypeOE\Free;

// Prevent direct access.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Provides order data extraction and normalization for export.
 */
class Data_Source_Orders {

	use Data_Source_Trait;

	/** Column keys that belong to expanded item-detail mode. */
	const ITEM_DETAIL_COLUMNS = array( 'item_name', 'item_sku', 'item_qty', 'item_subtotal', 'item_total', 'item_tax' );

	/**
	 * Get all available columns for orders, grouped by category.
	 *
	 * @return array Associative array of field_id => { label, group }.
	 */
	public function get_available_columns() {
		$columns = array(
			// --- Basic order data ---
			'order_id'             => array(
				'label' => __( 'Order ID', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'basic',
			),
			'order_number'         => array(
				'label' => __( 'Order Number', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'basic',
			),
			'status'               => array(
				'label' => __( 'Status', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'basic',
			),
			'customer_id'          => array(
				'label' => __( 'Customer ID', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'basic',
			),
			'date_created'         => array(
				'label' => __( 'Date Created', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'basic',
			),
			'date_modified'        => array(
				'label' => __( 'Date Modified', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'basic',
			),
			'date_paid'            => array(
				'label' => __( 'Date Paid', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'basic',
			),
			'date_completed'       => array(
				'label' => __( 'Date Completed', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'basic',
			),
			'total'                => array(
				'label' => __( 'Total', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'basic',
			),
			'subtotal'             => array(
				'label' => __( 'Subtotal', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'basic',
			),
			'discount_total'       => array(
				'label' => __( 'Discount Total', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'basic',
			),
			'tax_total'            => array(
				'label' => __( 'Tax Total', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'basic',
			),
			'shipping_total'       => array(
				'label' => __( 'Shipping Total', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'basic',
			),
			'payment_method'       => array(
				'label' => __( 'Payment Method', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'basic',
			),
			'payment_method_title' => array(
				'label' => __( 'Payment Method Title', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'basic',
			),
			'transaction_id'       => array(
				'label' => __( 'Transaction ID', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'basic',
			),
			'currency'             => array(
				'label' => __( 'Currency', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'basic',
			),
			'customer_note'        => array(
				'label' => __( 'Customer Note', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'basic',
			),

			// --- Billing ---
			'billing_first_name'   => array(
				'label' => __( 'Billing First Name', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'billing',
			),
			'billing_last_name'    => array(
				'label' => __( 'Billing Last Name', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'billing',
			),
			'billing_company'      => array(
				'label' => __( 'Billing Company', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'billing',
			),
			'billing_email'        => array(
				'label' => __( 'Billing Email', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'billing',
			),
			'billing_phone'        => array(
				'label' => __( 'Billing Phone', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'billing',
			),
			'billing_address_1'    => array(
				'label' => __( 'Billing Address 1', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'billing',
			),
			'billing_address_2'    => array(
				'label' => __( 'Billing Address 2', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'billing',
			),
			'billing_city'         => array(
				'label' => __( 'Billing City', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'billing',
			),
			'billing_state'        => array(
				'label' => __( 'Billing State', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'billing',
			),
			'billing_postcode'     => array(
				'label' => __( 'Billing Postcode', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'billing',
			),
			'billing_country'      => array(
				'label' => __( 'Billing Country', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'billing',
			),

			// --- Shipping ---
			'shipping_first_name'  => array(
				'label' => __( 'Shipping First Name', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'shipping',
			),
			'shipping_last_name'   => array(
				'label' => __( 'Shipping Last Name', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'shipping',
			),
			'shipping_company'     => array(
				'label' => __( 'Shipping Company', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'shipping',
			),
			'shipping_address_1'   => array(
				'label' => __( 'Shipping Address 1', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'shipping',
			),
			'shipping_address_2'   => array(
				'label' => __( 'Shipping Address 2', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'shipping',
			),
			'shipping_city'        => array(
				'label' => __( 'Shipping City', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'shipping',
			),
			'shipping_state'       => array(
				'label' => __( 'Shipping State', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'shipping',
			),
			'shipping_postcode'    => array(
				'label' => __( 'Shipping Postcode', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'shipping',
			),
			'shipping_country'     => array(
				'label' => __( 'Shipping Country', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'shipping',
			),
			'shipping_method'      => array(
				'label' => __( 'Shipping Method', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'shipping',
			),
			'shipping_phone'       => array(
				'label' => __( 'Shipping Phone', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'shipping',
			),

			// --- Line Items (concatenated mode) ---
			'line_items'           => array(
				'label' => __( 'Products', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'items',
			),
			'line_items_qty'       => array(
				'label' => __( 'Quantities', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'items',
			),
			'line_items_total'     => array(
				'label' => __( 'Products Total', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'items',
			),

			// --- Line Items (expanded mode — per item columns) ---
			'item_name'            => array(
				'label' => __( 'Product Name', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'item_detail',
			),
			'item_sku'             => array(
				'label' => __( 'Product SKU', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'item_detail',
			),
			'item_qty'             => array(
				'label' => __( 'Quantity', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'item_detail',
			),
			'item_subtotal'        => array(
				'label' => __( 'Item Subtotal', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'item_detail',
			),
			'item_total'           => array(
				'label' => __( 'Item Total', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'item_detail',
			),
			'item_tax'             => array(
				'label' => __( 'Item Tax', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'item_detail',
			),

			// --- Coupons ---
			'coupons'              => array(
				'label' => __( 'Applied Coupons', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'coupons',
			),
			'coupon_discount'      => array(
				'label' => __( 'Coupon Discount', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'coupons',
			),

			// --- Fees ---
			'fees'                 => array(
				'label' => __( 'Additional Fees', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'fees',
			),
			'fees_total'           => array(
				'label' => __( 'Total Fees', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'fees',
			),

			// --- Refunds ---
			'refund_total'         => array(
				'label' => __( 'Total Refunded', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'refunds',
			),
			'refund_reason'        => array(
				'label' => __( 'Refund Reason', 'rawtype-order-exporter-woocommerce' ),
				'group' => 'refunds',
			),
		);

		/**
		 * Filter available order columns.
		 *
		 * @param array $columns Column definitions.
		 */
		return apply_filters( 'rawtype_oe_columns_orders_data_source', $columns );
	}

	/**
	 * Fetch orders using wc_get_orders (HPOS-safe).
	 *
	 * @param array $args Arguments compatible with wc_get_orders().
	 * @return \WC_Order[] Array of WC_Order objects.
	 */
	public function fetch( $args ) {
		if ( $this->requires_manual_order_resolution( $args ) ) {
			return $this->fetch_resolved_orders( $args );
		}

		// Exclude refunds — OrderRefund lacks methods like get_order_number().
		if ( ! isset( $args['type'] ) ) {
			$args['type'] = 'shop_order';
		}

		// Force deterministic pagination when no explicit sort is requested: MySQL does
		// not guarantee consistent ordering for tied date_created values, which would
		// let rows shuffle between pages and produce duplicates + missing rows on export.
		if ( ! isset( $args['orderby'] ) ) {
			$args['orderby'] = 'ID';
			if ( ! isset( $args['order'] ) ) {
				$args['order'] = 'DESC';
			}
		}

		return wc_get_orders( $args );
	}

	/**
	 * Count orders matching args.
	 *
	 * @param array $args Query arguments.
	 * @return int Total count.
	 */
	public function count( $args ) {
		if ( $this->requires_manual_order_resolution( $args ) ) {
			return Query_Builder::count_orders_for_args( $this->strip_runtime_order_args( $args ) );
		}

		$count_args = array_merge(
			$args,
			array(
				'limit'    => 1,
				'return'   => 'ids',
				'paginate' => true,
				'type'     => 'shop_order',
			)
		);

		$count_result = wc_get_orders( $count_args );

		if ( is_object( $count_result ) && isset( $count_result->total ) ) {
			return absint( $count_result->total );
		}

		return 0;
	}

	/**
	 * Determine whether the query needs manual order ID resolution.
	 *
	 * Some advanced filters are implemented outside wc_get_orders() and must
	 * be resolved before pagination/counting.
	 *
	 * @param array $args Query arguments.
	 * @return bool
	 */
	private function requires_manual_order_resolution( $args ) {
		if ( ! is_array( $args ) ) {
			return false;
		}

		return isset( $args['rawtype_oe_order_total'] ) || isset( $args['rawtype_oe_product_ids'] );
	}

	/**
	 * Fetch orders using a manually resolved ID list.
	 *
	 * @param array $args Query arguments.
	 * @return \WC_Order[]
	 */
	private function fetch_resolved_orders( $args ) {
		$query_args = $this->strip_runtime_order_args( $args );

		$limit  = isset( $args['limit'] ) ? (int) $args['limit'] : 500;
		$offset = $this->resolve_order_offset( $args, $limit );

		if ( -1 === $limit ) {
			$order_ids = Query_Builder::resolve_order_ids_for_args( $query_args );
			if ( $offset > 0 ) {
				$order_ids = array_slice( $order_ids, $offset );
			}
		} else {
			if ( $limit < 1 ) {
				return array();
			}

			$order_ids = Query_Builder::get_order_page_ids_for_args( $query_args, $limit, $offset, 'DESC' );
		}

		if ( empty( $order_ids ) ) {
			return array();
		}

		$batch = wc_get_orders(
			array(
				'include' => $order_ids,
				'type'    => 'shop_order',
				'limit'   => count( $order_ids ),
				'orderby' => 'date',
				'order'   => 'DESC',
			)
		);

		return is_array( $batch ) ? $batch : array();
	}

	/**
	 * Remove runtime-only args before resolving the full order ID set.
	 *
	 * @param array $args Query arguments.
	 * @return array
	 */
	private function strip_runtime_order_args( $args ) {
		$query_args = is_array( $args ) ? $args : array();

		unset(
			$query_args['limit'],
			$query_args['paged'],
			$query_args['offset'],
			$query_args['return'],
			$query_args['columns']
		);

		return $query_args;
	}

	/**
	 * Resolve pagination offset for a manual order result set.
	 *
	 * @param array $args  Query arguments.
	 * @param int   $limit Requested limit.
	 * @return int
	 */
	private function resolve_order_offset( $args, $limit ) {
		if ( isset( $args['offset'] ) ) {
			return max( 0, absint( $args['offset'] ) );
		}

		$paged = isset( $args['paged'] ) ? max( 1, absint( $args['paged'] ) ) : 1;
		if ( $limit < 1 ) {
			return 0;
		}

		return ( $paged - 1 ) * $limit;
	}

	/**
	 * Normalize an array of WC_Order objects into flat rows.
	 *
	 * @param \WC_Order[] $orders    Orders to normalize.
	 * @param array       $columns   Column IDs to include.
	 * @param string      $item_mode 'concatenated' or 'expanded'.
	 * @return array Array of associative arrays (rows).
	 */
	public function normalize( $orders, $columns, $item_mode = 'concatenated' ) {
		$rows            = array();
		$has_item_detail = $this->has_item_detail_columns( $columns );
		$use_expanded    = 'expanded' === $item_mode && $has_item_detail;
		$needs_sku       = empty( $columns ) || in_array( 'item_sku', $columns, true );

		// Batch-load products to avoid N+1 queries in expanded mode.
		$product_map = array();
		if ( $use_expanded && $needs_sku ) {
			$product_ids = array();
			foreach ( $orders as $order ) {
				foreach ( $order->get_items( 'line_item' ) as $item ) {
					$pid = $item->get_product_id();
					$vid = $item->get_variation_id();
					if ( $vid ) {
						$product_ids[ $vid ] = true;
					} elseif ( $pid ) {
						$product_ids[ $pid ] = true;
					}
				}
			}
			if ( ! empty( $product_ids ) ) {
				$ids      = array_keys( $product_ids );
				$products = wc_get_products(
					array(
						'include' => $ids,
						'limit'   => count( $ids ),
					)
				);
				foreach ( $products as $product ) {
					$product_map[ $product->get_id() ] = $product;
				}
			}
		}

		foreach ( $orders as $order ) {
			$base_data = $this->extract_order_data( $order, $columns );

			if ( $use_expanded ) {
				$items = $order->get_items( 'line_item' );

				if ( empty( $items ) ) {
					$row    = $this->filter_columns( $base_data, $columns );
					$rows[] = $row;
				} else {
					foreach ( $items as $item ) {
						$item_data = $this->extract_item_data( $item, $columns, $product_map );
						$merged    = array_merge( $base_data, $item_data );
						$row       = $this->filter_columns( $merged, $columns );
						$rows[]    = $row;
					}
				}
			} else {
				$concatenated = $this->extract_concatenated_items( $order, $columns );
				$merged       = array_merge( $base_data, $concatenated );
				$row          = $this->filter_columns( $merged, $columns );
				$rows[]       = $row;
			}
		}

		return $rows;
	}

	/**
	 * Check whether any item_detail columns are selected.
	 *
	 * @param array $columns Selected column IDs.
	 * @return bool
	 */
	private function has_item_detail_columns( $columns ) {
		foreach ( $columns as $col ) {
			if ( in_array( $col, self::ITEM_DETAIL_COLUMNS, true ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Extract base order data (excludes per-item detail).
	 *
	 * Uses HPOS-safe $order->get_*() methods exclusively.
	 *
	 * @param \WC_Order $order   Order object.
	 * @param array     $columns Selected column IDs.
	 * @return array Associative array of field => value.
	 */
	private function extract_order_data( $order, $columns ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
		$data = array();

		// Basic fields.
		$data['order_id']             = $order->get_id();
		$data['order_number']         = $order->get_order_number();
		$data['status']               = $order->get_status();
		$data['customer_id']          = $order->get_customer_id();
		$data['date_created']         = $this->format_date( $order->get_date_created() );
		$data['date_modified']        = $this->format_date( $order->get_date_modified() );
		$data['date_paid']            = $this->format_date( $order->get_date_paid() );
		$data['date_completed']       = $this->format_date( $order->get_date_completed() );
		$data['total']                = $order->get_total();
		$data['subtotal']             = $order->get_subtotal();
		$data['discount_total']       = $order->get_discount_total();
		$data['tax_total']            = $order->get_total_tax();
		$data['shipping_total']       = $order->get_shipping_total();
		$data['payment_method']       = $order->get_payment_method();
		$data['payment_method_title'] = $order->get_payment_method_title();
		$data['transaction_id']       = $order->get_transaction_id();
		$data['currency']             = $order->get_currency();
		$data['customer_note']        = $order->get_customer_note();

		// Billing fields.
		$data['billing_first_name'] = $order->get_billing_first_name();
		$data['billing_last_name']  = $order->get_billing_last_name();
		$data['billing_company']    = $order->get_billing_company();
		$data['billing_email']      = $order->get_billing_email();
		$data['billing_phone']      = $order->get_billing_phone();
		$data['billing_address_1']  = $order->get_billing_address_1();
		$data['billing_address_2']  = $order->get_billing_address_2();
		$data['billing_city']       = $order->get_billing_city();
		$data['billing_state']      = $order->get_billing_state();
		$data['billing_postcode']   = $order->get_billing_postcode();
		$data['billing_country']    = $order->get_billing_country();

		// Shipping fields.
		$data['shipping_first_name'] = $order->get_shipping_first_name();
		$data['shipping_last_name']  = $order->get_shipping_last_name();
		$data['shipping_company']    = $order->get_shipping_company();
		$data['shipping_address_1']  = $order->get_shipping_address_1();
		$data['shipping_address_2']  = $order->get_shipping_address_2();
		$data['shipping_city']       = $order->get_shipping_city();
		$data['shipping_state']      = $order->get_shipping_state();
		$data['shipping_postcode']   = $order->get_shipping_postcode();
		$data['shipping_country']    = $order->get_shipping_country();
		$data['shipping_phone']      = method_exists( $order, 'get_shipping_phone' ) ? $order->get_shipping_phone() : '';
		if ( empty( $columns ) || in_array( 'shipping_method', $columns, true ) ) {
			$data['shipping_method'] = $this->get_shipping_method( $order );
		}

		// Coupons — fetch coupon items once for both columns.
		if ( empty( $columns ) || in_array( 'coupons', $columns, true ) || in_array( 'coupon_discount', $columns, true ) ) {
			$coupon_items            = $order->get_items( 'coupon' );
			$data['coupons']         = $this->get_coupon_codes_from( $coupon_items );
			$data['coupon_discount'] = $this->get_coupon_discount_from( $coupon_items );
		}

		// Fees — triggers get_fees() DB query; fetch once for both columns.
		if ( empty( $columns ) || in_array( 'fees', $columns, true ) || in_array( 'fees_total', $columns, true ) ) {
			$fees               = $order->get_fees();
			$data['fees']       = $this->get_fee_names_from( $fees );
			$data['fees_total'] = $this->get_fees_total_from( $fees );
		}

		// Refunds — triggers get_refunds() DB query; fetch once for both columns.
		if ( empty( $columns ) || in_array( 'refund_total', $columns, true ) || in_array( 'refund_reason', $columns, true ) ) {
			$refunds               = $order->get_refunds();
			$data['refund_total']  = $this->get_refund_total_from( $refunds );
			$data['refund_reason'] = $this->get_refund_reason_from( $refunds );
		}
		$data = $this->append_custom_meta_data( $data, $order, $columns );

		return $data;
	}

	/**
	 * Extract concatenated line item data for a single order.
	 *
	 * @param \WC_Order $order   Order object.
	 * @param array     $columns Selected column IDs.
	 * @return array
	 */
	private function extract_concatenated_items( $order, $columns ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
		$data = array(
			'line_items'       => '',
			'line_items_qty'   => '',
			'line_items_total' => '',
		);

		$items = $order->get_items( 'line_item' );

		if ( empty( $items ) ) {
			return $data;
		}

		$names  = array();
		$qtys   = array();
		$totals = array();

		foreach ( $items as $item ) {
			$names[]  = $item->get_name();
			$qtys[]   = $item->get_quantity();
			$totals[] = $item->get_total();
		}

		$data['line_items']       = implode( ', ', $names );
		$data['line_items_qty']   = implode( ', ', $qtys );
		$data['line_items_total'] = implode( ', ', $totals );

		return $data;
	}

	/**
	 * Extract individual line item data for expanded mode.
	 *
	 * @param \WC_Order_Item_Product $item        Line item object.
	 * @param array                  $columns     Selected column IDs.
	 * @param array                  $product_map Pre-loaded product map (id => WC_Product).
	 * @return array
	 */
	private function extract_item_data( $item, $columns, $product_map = array() ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
		$sku = '';
		$pid = $item->get_variation_id() ?: $item->get_product_id();

		if ( isset( $product_map[ $pid ] ) ) {
			$sku = $product_map[ $pid ]->get_sku();
		} elseif ( $pid ) {
			$product = $item->get_product();
			if ( $product && is_object( $product ) ) {
				$sku = $product->get_sku();
			}
		}

		return array(
			'item_name'     => $item->get_name(),
			'item_sku'      => $sku,
			'item_qty'      => $item->get_quantity(),
			'item_subtotal' => $item->get_subtotal(),
			'item_total'    => $item->get_total(),
			'item_tax'      => $item->get_total_tax(),
		);
	}

	/**
	 * Get shipping method names for an order.
	 *
	 * @param \WC_Order $order Order object.
	 * @return string Comma-separated shipping method titles.
	 */
	private function get_shipping_method( $order ) {
		$methods = array();
		$items   = $order->get_items( 'shipping' );

		if ( is_array( $items ) ) {
			foreach ( $items as $item ) {
				$methods[] = $item->get_name();
			}
		}

		return implode( ', ', $methods );
	}

	/**
	 * Get coupon codes from a pre-fetched coupon item collection.
	 *
	 * @param array $coupon_items Coupon items from $order->get_items( 'coupon' ).
	 * @return string Comma-separated coupon codes.
	 */
	private function get_coupon_codes_from( $coupon_items ) {
		$codes = array();

		if ( is_array( $coupon_items ) ) {
			foreach ( $coupon_items as $coupon ) {
				$codes[] = $coupon->get_code();
			}
		}

		return implode( ', ', $codes );
	}

	/**
	 * Get total coupon discount from a pre-fetched coupon item collection.
	 *
	 * @param array $coupon_items Coupon items from $order->get_items( 'coupon' ).
	 * @return float Total coupon discount amount.
	 */
	private function get_coupon_discount_from( $coupon_items ) {
		$total = 0;

		if ( is_array( $coupon_items ) ) {
			foreach ( $coupon_items as $coupon ) {
				$total += (float) $coupon->get_discount();
			}
		}

		return $total;
	}

	/**
	 * Get fee names from a pre-fetched fee collection.
	 *
	 * @param array $fees Fee items from $order->get_fees().
	 * @return string Comma-separated fee names.
	 */
	private function get_fee_names_from( $fees ) {
		$names = array();

		if ( is_array( $fees ) ) {
			foreach ( $fees as $fee ) {
				$names[] = $fee->get_name();
			}
		}

		return implode( ', ', $names );
	}

	/**
	 * Get total fees amount from a pre-fetched fee collection.
	 *
	 * @param array $fees Fee items from $order->get_fees().
	 * @return float Total fees amount.
	 */
	private function get_fees_total_from( $fees ) {
		$total = 0;

		if ( is_array( $fees ) ) {
			foreach ( $fees as $fee ) {
				$total += (float) $fee->get_total();
			}
		}

		return $total;
	}

	/**
	 * Get total refunded amount from a pre-fetched refund collection.
	 *
	 * @param array $refunds Refund objects from $order->get_refunds().
	 * @return float Total refunded (negative value).
	 */
	private function get_refund_total_from( $refunds ) {
		$total = 0;

		if ( is_array( $refunds ) ) {
			foreach ( $refunds as $refund ) {
				$total += (float) $refund->get_total();
			}
		}

		return $total;
	}

	/**
	 * Get refund reasons from a pre-fetched refund collection.
	 *
	 * @param array $refunds Refund objects from $order->get_refunds().
	 * @return string Comma-separated refund reasons.
	 */
	private function get_refund_reason_from( $refunds ) {
		$reasons = array();

		if ( is_array( $refunds ) ) {
			foreach ( $refunds as $refund ) {
				$reason = $refund->get_reason();
				if ( ! empty( $reason ) ) {
					$reasons[] = $reason;
				}
			}
		}

		return implode( ', ', $reasons );
	}
}
