<?php
/**
 * A class to deliver order-related topics to a webhook.
 *
 * @since 3.0.0
 */

declare( strict_types=1 );
namespace FeData\FastEvents\Hooks\Webhooks\Topic;

use FeData\FastEvents\Hooks\Webhooks\AbstractWebhook;
use FeData\FastEvents\Payment\PaymentFactory;
use stdClass;

defined( 'ABSPATH' ) || exit;


/**
 * A class to deliver order-related topics to a webhook.
 */
final class OrderTopic extends AbstractWebhook {

	/**
	 * Dispatch 'order.created', 'order.updated', 'order.refunded', 'order.deleted'.
	 *
	 * @since 3.0.0
	 *
	 * @param string   $dispatch Subtopic to dispatch ('created', 'updated', 'refunded', 'deleted').
	 * @param stdClass $webhook  Detailed information on the webhook.
	 * @param mixed    $arg      Event identifier (int).
	 */
	public function dispatch( string $dispatch, stdClass $webhook, mixed $arg ): void {
		$this->{$dispatch . '_order'}( $webhook, $arg );
	}


	/**
	 * Webhook for adding a new order.
	 *
	 * @since 1.0
	 * @since 2.3.0 Use fast_events_json_encode.
	 *
	 * @param stdClass $webhook     Detailed information on the webhook.
	 * @param int      $order_id    Event identifier.
	 */
	private function created_order( stdClass $webhook, int $order_id ): void {

		$this->webhook = $webhook;

		$order = $this->get_order( $order_id );
		if ( is_array( $order ) ) {
			$this->logger->error( 'Webhook error', $order );
			return;
		}

		$fields    = array();
		$tickets   = array();
		$discounts = array();
		$inp       = json_decode( $order->input_fields );
		if ( null !== $inp ) {

			// Add the text fields.
			foreach ( $inp->fields as $text_field ) {
				$tmp['name']  = $text_field->name;
				$tmp['value'] = $text_field->value;
				$fields[]     = $tmp;
			}

			// Add the ticket fields.
			unset( $tmp );
			foreach ( $inp->tickets as $ticket_info ) {
				if ( 0 !== $ticket_info->count ) {
					$tmp['name']       = $ticket_info->name;
					$tmp['price']      = (float) $ticket_info->price;
					$tmp['vat']        = (float) $ticket_info->vat;
					$tmp['is_counted'] = (bool) $ticket_info->cflag;
					$tmp['number']     = $ticket_info->count;
					$tickets[]         = $tmp;
				}
			}

			// Add the discount fields.
			if ( ! empty( $inp->discounts ) ) {
				unset( $tmp );
				foreach ( $inp->discounts as $discount ) {
					$tmp['code']   = $discount->code;
					$tmp['amount'] = (float) $discount->amount;
					$discounts[]   = $tmp;
				}
			}
		}

		$this->deliver(
			array(
				'id'             => $order_id,
				'event_id'       => (int) $order->event_id,
				'event_date'     => $order->event_date,
				'payment_id'     => $order->payment_id,
				'payment_status' => $order->payment_status,
				'custom_status'  => $order->custom_status,
				'created_at'     => $order->created_at,
				'uid'            => $order->uid,
				'name'           => $order->name,
				'email'          => $order->email,
				'input_fields'   => array(
					'fields'    => $fields,
					'tickets'   => $tickets,
					'discounts' => $discounts,
				),
				'num_tickets'    => (int) $order->num_tickets,
				'total'          => (float) $order->total,
				'discount'       => (float) $order->discount,
				'ip_address'     => $order->ip_address,
			)
		);
	}


	/**
	 * Webhook for updating an order.
	 *
	 * @since 1.0
	 * @since 2.3.0 Use fast_events_json_encode.
	 *
	 * @param stdClass $webhook     Detailed information on the webhook.
	 * @param int      $order_id    Event identifier.
	 */
	private function updated_order( stdClass $webhook, int $order_id ): void {

		$this->webhook = $webhook;

		$order = $this->get_order( $order_id );
		if ( is_array( $order ) ) {
			$this->logger->error( 'Webhook error', $order );
			return;
		}

		$this->deliver(
			array(
				'id'            => $order_id,
				'custom_status' => $order->custom_status,
				'name'          => $order->name,
				'email'         => $order->email,
			)
		);
	}


	/**
	 * Webhook for refunding an order.
	 *
	 * @since 1.0
	 * @since 2.3.0 Use fast_events_json_encode.
	 * @since 2.4.0 Get User-Agent from utility
	 *
	 * @param stdClass $webhook     Detailed information on the webhook.
	 * @param int      $order_id    Refund order_id.
	 */
	private function refunded_order( stdClass $webhook, int $order_id ): void {
		global $wpdb;
		global $fevt_container;

		$this->webhook = $webhook;

		// Get the order.
		$order = $this->get_order( $order_id );
		if ( is_array( $order ) ) {
			$this->logger->error( 'Webhook error', $order );
			return;
		}

		// Look up the event.
		$event = $wpdb->get_row( $wpdb->prepare( "SELECT saas_user_id, test_payments_needed FROM {$wpdb->prefix}fe_events WHERE id = %d", $order->event_id ) );
		if ( empty( $event ) ) {
			$wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->prefix}fe_event_webhooks SET pending=GREATEST(0, pending-1) WHERE id = %d", (int) $this->webhook->id ) );
			$this->logger->error(
				'Event not found',
				array(
					'type'       => 'Webhook',
					'order_id'   => $order_id,
					'event_id'   => (int) $order->event_id,
					'webhook-id' => (int) $this->webhook->id,
					'name'       => $this->webhook->name,
					'url'        => $this->webhook->url,
					'topic'      => $this->webhook->topic,
					'data'       => $order,
				)
			);
			return;
		}

		// Check with the Payment provider for refund details.
		$provider = $fevt_container->get( PaymentFactory::class )->get_payment( $order->payment_id, (int) $event->saas_user_id, $order, (bool) $event->test_payments_needed );
		if ( is_wp_error( $provider ) ) {
			$wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->prefix}fe_event_webhooks SET pending=GREATEST(0, pending-1) WHERE id = %d", (int) $this->webhook->id ) );
			$this->logger->error(
				'Payment ID not found',
				array(
					'type'       => 'Webhook',
					'order_id'   => $order_id,
					'event_id'   => (int) $order->event_id,
					'webhook-id' => (int) $this->webhook->id,
					'name'       => $this->webhook->name,
					'url'        => $this->webhook->url,
					'topic'      => $this->webhook->topic,
					'data'       => $order,
				)
			);
			return;
		}

		// Loop through all refunds.
		$refunds = array();
		foreach ( $provider['refunds'] as $refund ) {
			$refunds[] = array(
				'refund_amount'   => (float) $refund['amount'],
				'refund_currency' => $refund['currency'],
				'refund_date'     => $refund['date'],
			);
		}

		$this->deliver(
			array(
				'id'      => $order_id,
				'refunds' => $refunds,
			)
		);
	}


	/**
	 * Webhook for deleting an order.
	 *
	 * @since 1.0
	 *
	 * @param stdClass $webhook     Detailed information on the webhook.
	 * @param int      $order_id    Order identifier.
	 */
	private function deleted_order( stdClass $webhook, int $order_id ): void {

		$this->webhook = $webhook;

		$this->deliver( array( 'id' => $order_id ) );
	}


	/**
	 * Order lookup.
	 *
	 * @since 1.0
	 *
	 * @param int $order_id    Order identifier.
	 *
	 * @return stdClass|array
	 */
	private function get_order( int $order_id ): array|stdClass {
		global $wpdb;

		$order = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}fe_event_orders WHERE id=%d", $order_id ) );
		if ( null === $order ) {
			$wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->prefix}fe_event_webhooks SET pending=GREATEST(0, pending-1) WHERE id = %d", (int) $this->webhook->id ) );
			return array(
				'type'       => 'Webhook',
				'webhook-id' => (int) $this->webhook->id,
				'name'       => $this->webhook->name,
				'url'        => $this->webhook->url,
				'topic'      => $this->webhook->topic,
				'error'      => '** Order #' . $order_id . ' not found **',
			);
		}

		return $order;
	}
}
