<?php

namespace Prodigy\includes\models;

use Prodigy\Includes\Prodigy_User;

/**
 * Prodigy order class
 *
 * @version    3.1.8
 * @package    Prodigy
 * @subpackage Prodigy/includes
 */
class Prodigy_Order {

	public const PRODIGY_ORDER_COMPLETED = 1;

	public const PRODIGY_ORDER_IN_PROGRESS = 0;

	public const ORDER_DATA_TABLE = 'prodigy_orders';

	public string $order_table;

	public function __construct() {
		global $wpdb;
		$this->order_table = $wpdb->prefix . self::ORDER_DATA_TABLE;
	}

	/**
	 * @param   string $token
	 *
	 * @return int
	 */
	public static function get_order_number( string $token ): int {
		global $wpdb;
		$result = $wpdb->get_results(
			$wpdb->prepare(
				"select order_number from {$wpdb->prefix}prodigy_orders where token = %s and status <> 1",
				$token
			),
			ARRAY_A
		);

		if ( isset( $result[0] ) ) {
			return json_decode( $result[0]['order_number'], true );
		}
	}

	/**
	 * @param   string $user_session
	 *
	 * @return array|object|\stdClass|null
	 */
	public static function get_last_guest_active_order_data( string $user_session ) {
		global $wpdb;

		return $wpdb->get_row(
			$wpdb->prepare(
				"select * from {$wpdb->prefix}prodigy_orders where local_user_hash = %s and status = 0 and deleted_at is null ",
				$user_session
			),
			ARRAY_A
		);
	}

	/**
	 * @param   string $user_id
	 *
	 * @return array|object|\stdClass|null
	 */
	public static function get_last_user_active_order_data( string $user_id ) {
		global $wpdb;

		return $wpdb->get_row(
			$wpdb->prepare(
				"select * from {$wpdb->prefix}prodigy_orders where customer_id = %s and status = 0 and deleted_at is null ",
				$user_id
			),
			ARRAY_A
		);
	}

	/**
	 * @param   string $order_token
	 * @param   int    $status
	 *
	 * @return bool
	 */
	public function update_status_orders( string $order_token, int $status = self::PRODIGY_ORDER_IN_PROGRESS ): bool {
		global $wpdb;
		$is_update = $wpdb->update(
			$this->order_table,
			array(
				'status'     => $status,
				'updated_at' => gmdate( 'Y-m-d H:i:s', time() ),
			),
			array(
				'token' => $order_token,
			),
			array( '%d', '%s' ),
			array( '%s' )
		);

		return (bool) $is_update;
	}

	/**
	 * @param   string $order_token
	 * @param   string $old_user_hash
	 * @param   string $new_user_hash
	 * @param   int    $user_id
	 *
	 * @return bool
	 */
	public function update_user_order( string $order_token, string $old_user_hash, string $new_user_hash, int $user_id ): bool {
		global $wpdb;
		$is_update = $wpdb->update(
			$this->order_table,
			array(
				'local_user_hash' => $new_user_hash,
				'customer_id'     => $user_id,
				'updated_at'      => gmdate( 'Y-m-d H:i:s', time() ),
			),
			array(
				'local_user_hash' => $old_user_hash,
				'token'           => $order_token,
			),
			array( '%s', '%d', '%s' ),
			array( '%s' )
		);

		return (bool) $is_update;
	}

	/**
	 * @param  int $user_id
	 *
	 * @return mixed
	 */
	public function get_active_user_order( int $user_id ) {
		global $wpdb;

		$result = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT * FROM {$this->order_table} WHERE customer_id = %d  AND deleted_at IS NULL",
				$user_id
			),
			ARRAY_A
		);

		return $result;
	}

	/**
	 * @param   int $user_id
	 *
	 * @return bool
	 */
	public function close_active_user_order( int $user_id ): bool {
		global $wpdb;

		return $wpdb->query(
			$wpdb->prepare(
				"UPDATE {$this->order_table}
				 SET deleted_at = %s
				 WHERE customer_id = %d
				 AND deleted_at IS NULL",
				gmdate( 'Y-m-d H:i:s' ),
				$user_id
			)
		);
	}

	/**
	 * @param   string $order_token
	 * @param   string $user_session
	 * @param   array  $data
	 *
	 * @return bool
	 */
	public function update_order_data( string $order_token, string $user_session, array $data ): bool {
		global $wpdb;
		$is_update = $wpdb->update(
			$this->order_table,
			array(
				'includes'   => json_encode( $data, true ),
				'updated_at' => gmdate( 'Y-m-d H:i:s', time() ),
			),
			array(
				// Token is unique, do not rely on local_user_hash (it can be empty or rotated).
				'token' => $order_token,
			),
			array( '%s', '%s' ),
			array( '%s' )
		);

		return (bool) $is_update;
	}

	/**
	 * @param   array  $response
	 * @param   string $user_session
	 *
	 * @return string
	 */
	public function create_order( array $response, string $user_session ): string {
		global $wpdb;
		$result       = '';
		$order_number = $response['data']['id'] ?? null;
		$token        = $response['data']['attributes']['token'] ?? false;

		$data_query = array(
			'order_number' => $order_number,
			'token'        => $token,
			'status'       => $this->get_order_status( $token ),
			'created_at'   => gmdate( 'Y-m-d H:i:s', time() ),
			'includes'     => wp_json_encode( $response ),
		);

		if ( Prodigy_User::is_logged_in() ) {
			$data_query['customer_id'] = $user_session;
		} else {
			$data_query['local_user_hash'] = $user_session;
		}

		$external_checkout_id = Prodigy_User::get_external_checkout_id();
		if ( $external_checkout_id !== null ) {
			$data_query['external_checkout_id'] = $external_checkout_id;
		}

		if ( isset( $order_number ) && $token ) {
			$result = $wpdb->insert(
				$this->order_table,
				$data_query,
				array_fill( 0, count( $data_query ), '%s' )
			);
		}

		if ( $result ) {
			return $token;
		}
	}

	/**
	 * @param   string $token
	 *
	 * @return int
	 */
	public function get_order_status( string $token ): int {
		global $wpdb;
		$completed_status = self::PRODIGY_ORDER_COMPLETED;

		$status = $wpdb->get_results(
			$wpdb->prepare(
				"select * from {$wpdb->prefix}prodigy_orders where token = %s and status <> %d",
				$token,
				$completed_status
			),
			ARRAY_A
		);

		return (int) $status;
	}

	/**
	 * @param   string $token
	 *
	 * @return mixed
	 */
	public function get_in_process_order_for_token( string $token ) {
		global $wpdb;
		$result = $wpdb->get_results(
			$wpdb->prepare(
				"select * from {$wpdb->prefix}prodigy_orders where token = %s and status = %d",
				$token,
				self::PRODIGY_ORDER_IN_PROGRESS
			),
			ARRAY_A
		);

		if ( ! empty( $result ) ) {
			return $result[0];
		}

		return false;
	}

	/**
	 * @param   string $user_session
	 *
	 * @return mixed|null
	 */
	public function get_order_token( string $user_session ) {
		global $wpdb;
		$table_name = $this->order_table;

		$like = '%' . $wpdb->prefix . 'prodigy_orders%';
		if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $like ) ) == $table_name ) {
			if ( Prodigy_User::is_logged_in() ) {
				$external_checkout_id = Prodigy_User::get_external_checkout_id();

				if ( $external_checkout_id !== null ) {
					$result = $wpdb->get_results(
						$wpdb->prepare(
							"select token from {$wpdb->prefix}prodigy_orders where customer_id = %s and external_checkout_id = %s and status <> 1 and deleted_at IS NULL ORDER BY created_at DESC LIMIT 1",
							$user_session,
							$external_checkout_id
						),
						ARRAY_A
					);
				} else {
					$result = $wpdb->get_results(
						$wpdb->prepare(
							"select token from {$wpdb->prefix}prodigy_orders where customer_id = %s and status <> 1 and deleted_at IS NULL ORDER BY created_at DESC LIMIT 1",
							$user_session
						),
						ARRAY_A
					);
				}
			} else {
				$result = $wpdb->get_results(
					$wpdb->prepare(
						"select token from {$wpdb->prefix}prodigy_orders where local_user_hash = %s and status <> 1 and deleted_at IS NULL ORDER BY created_at DESC LIMIT 1",
						$user_session
					),
					ARRAY_A
				);
			}

			return $result[0]['token'] ?? null;
		}

		return null;
	}

	/**
	 * @param   string $order_token
	 *
	 * @return bool
	 */
	public function delete_local_order( string $order_token ): bool {
		global $wpdb;
		$is_update = $wpdb->update(
			$this->order_table,
			array(
				'deleted_at' => gmdate( 'Y-m-d H:i:s', time() ),
			),
			array(
				'token' => $order_token,
			),
			array( '%s' ),
			array( '%s' )
		);

		return (bool) $is_update;
	}

	/**
	 * @param   string $order_token
	 *
	 * @return bool
	 */
	public function is_deleted_order( string $order_token ): bool {
		global $wpdb;
		$result = $wpdb->get_row(
			$wpdb->prepare(
				"select deleted_at from {$wpdb->prefix}prodigy_orders where token = %s",
				$order_token
			),
			ARRAY_A
		);

		return ! empty( $result['deleted_at'] );
	}

	/**
	 * @param   string $order_token
	 *
	 * @return array
	 */
	public function get_order_data( string $order_token ): array {
		global $wpdb;

		// Table name uses prefix + literal (no user input); %i not available before WP 6.2.
		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
		$results = $wpdb->get_row(
			$wpdb->prepare(
				'SELECT includes FROM ' . $wpdb->prefix . 'prodigy_orders WHERE token = %s',
				$order_token
			)
		);

		return json_decode( $results->includes ?? '', true );
	}
}
