<?php
/**
 * Class Noesia Cs Order Repository.
 *
 * @package Noesia_Commerce_Suite
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class implementation.
 */
class Noesia_CS_Order_Repository {
	/**
	 * Handles the related plugin operation.
	 */
	public function is_available() {
		return class_exists( 'WooCommerce' ) && function_exists( 'wc_get_orders' );
	}

	/**
	 * Handles the related plugin operation.
	 *
	 * @param mixed $limit Limit.
	 */
	public function get_recent_orders( $limit = 12 ) {
		if ( ! $this->is_available() ) {
			return array();
		}

		return wc_get_orders(
			array(
				'limit'    => max( 1, min( 500, absint( $limit ) ) ),
				'orderby'  => 'date',
				'order'    => 'DESC',
				'return'   => 'objects',
			)
		);
	}

	/**
	 * Handles the related plugin operation.
	 *
	 * @param mixed $date_after Date after.
	 * @param mixed $limit Limit.
	 */
	public function get_orders_since( $date_after, $limit = 500 ) {
		if ( ! $this->is_available() ) {
			return array();
		}

		$args = array(
			'limit'    => max( 1, min( 1000, absint( $limit ) ) ),
			'orderby'  => 'date',
			'order'    => 'DESC',
			'return'   => 'objects',
		);

		if ( $date_after ) {
			$args['date_created'] = '>' . sanitize_text_field( $date_after );
		}

		return wc_get_orders( $args );
	}

	/**
	 * Handles the related plugin operation.
	 *
	 * @param mixed $date_after Date after.
	 * @param mixed $date_before Date before.
	 * @param mixed $limit Limit.
	 */
	public function get_orders_between( $date_after, $date_before, $limit = 500 ) {
		if ( ! $this->is_available() ) {
			return array();
		}

		$args = array(
			'limit'    => max( 1, min( 2000, absint( $limit ) ) ),
			'orderby'  => 'date',
			'order'    => 'DESC',
			'return'   => 'objects',
		);

		if ( $date_after && $date_before ) {
			$args['date_created'] = sanitize_text_field( $date_after ) . '...' . sanitize_text_field( $date_before );
		} elseif ( $date_after ) {
			$args['date_created'] = '>' . sanitize_text_field( $date_after );
		} elseif ( $date_before ) {
			$args['date_created'] = '<' . sanitize_text_field( $date_before );
		}

		return wc_get_orders( $args );
	}

	/**
	 * Handles the related plugin operation.
	 *
	 * @param mixed $limit Limit.
	 */
	public function get_orders_for_snapshot( $limit = 500 ) {
		return $this->get_recent_orders( $limit );
	}

	/**
	 * Handles the related plugin operation.
	 *
	 * @param mixed $date_before Date before.
	 * @param mixed $limit Limit.
	 */
	public function get_orders_for_reorder_queue( $date_before, $limit = 250 ) {
		if ( ! $this->is_available() ) {
			return array();
		}

		$args = array(
			'limit'    => max( 1, min( 1000, absint( $limit ) ) ),
			'orderby'  => 'date',
			'order'    => 'DESC',
			'status'   => array( 'wc-completed', 'wc-processing' ),
			'return'   => 'objects',
		);

		if ( $date_before ) {
			$args['date_created'] = '<' . sanitize_text_field( $date_before );
		}

		return wc_get_orders( $args );
	}

	/**
	 * Handles the related plugin operation.
	 *
	 * @param mixed $meta_key Meta key.
	 * @param mixed $token_hash Token hash.
	 */
	public function find_order_by_token_hash( $meta_key, $token_hash ) {
		return $this->find_order_by_meta_value( $meta_key, $token_hash, 'Token hash lookup' );
	}

	/**
	 * Finds a single order by an exact meta value.
	 *
	 * @param mixed  $meta_key Meta key.
	 * @param mixed  $meta_value Meta value.
	 * @param string $reason Reason for PHPCS context.
	 */
	public function find_order_by_meta_value( $meta_key, $meta_value, $reason = 'Order meta lookup' ) {
		if ( ! $this->is_available() || ! $meta_key || '' === (string) $meta_value ) {
			return null;
		}

		$meta_key   = sanitize_key( $meta_key );
		$meta_value = sanitize_text_field( $meta_value );

		$orders = wc_get_orders(
			array(
				'limit'      => 1,
				'return'     => 'objects',
				'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Public Order Care token lookup is exact, limited to one order, and required for customer access links.
					array(
						'key'     => $meta_key,
						'value'   => $meta_value,
						'compare' => '=',
					),
				),
			)
		);

		if ( ! empty( $orders ) && is_a( $orders[0], 'WC_Order' ) ) {
			return $orders[0];
		}

		$order_id = $this->find_order_id_by_meta_value_direct( $meta_key, $meta_value );
		if ( ! $order_id ) {
			return null;
		}

		$order = wc_get_order( $order_id );
		return is_a( $order, 'WC_Order' ) ? $order : null;
	}

	/**
	 * Finds one order ID by exact meta value using WooCommerce storage tables as a fallback.
	 *
	 * @param string $meta_key Meta key.
	 * @param string $meta_value Meta value.
	 */
	private function find_order_id_by_meta_value_direct( $meta_key, $meta_value ) {
		global $wpdb;

		if ( ! $wpdb || ! $meta_key || '' === $meta_value ) {
			return 0;
		}

		$lookup_tables = array(
			array(
				'table'     => $wpdb->prefix . 'wc_orders_meta',
				'id_column' => 'order_id',
			),
			array(
				'table'     => $wpdb->postmeta,
				'id_column' => 'post_id',
			),
		);

		foreach ( $lookup_tables as $lookup ) {
			$table     = (string) $lookup['table'];
			$id_column = (string) $lookup['id_column'];

			if ( ! $this->table_exists( $table ) ) {
				continue;
			}

			// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table and column names are internal allow-listed values above.
			$order_id = $wpdb->get_var(
				$wpdb->prepare(
					"SELECT {$id_column} FROM {$table} WHERE meta_key = %s AND meta_value = %s LIMIT 1",
					$meta_key,
					$meta_value
				)
			);
			// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

			if ( $order_id ) {
				return absint( $order_id );
			}
		}

		return 0;
	}

	/**
	 * Checks whether a database table exists.
	 *
	 * @param string $table Table name.
	 */
	private function table_exists( $table ) {
		global $wpdb;

		if ( ! $wpdb || '' === $table ) {
			return false;
		}

		$found = $wpdb->get_var(
			$wpdb->prepare(
				'SHOW TABLES LIKE %s',
				$wpdb->esc_like( $table )
			)
		);

		return $found === $table;
	}
}
