<?php
/**
 * Shared Webhook methods.
 *
 * @package WPDiscourse
 */

namespace WPDiscourse\Shared;

use WPDiscourse\Logs\Logger;

/**
 * Trait WebhookUtilities
 */
trait WebhookUtilities {
  	/**
     * Supported Webhook events.
     *
     * @access protected
     * @var mixed|void
     */
    protected $supported_events = null;

    /**
     * Get webhook data for WPDiscourse classes.
     *
     * @param \WP_REST_Request $request The WP_REST_Request data object.
     *
     * @return object|\WP_Error
     */
    protected function get_webhook_data( $request ) {
        if ( ! $this->webhook_enabled() ) {

            return new \WP_Error( 'discourse_webhook_error', __( 'The webhook is not enabled.' ) );
        }

        return $this->get_discourse_webhook_data( $request, $this->supported_events, $this->logger_context );
    }

    /**
     * Get data from discourse webhook request.
     *
     * @param \WP_REST_Request $request The WP_REST_Request data object.
     * @param array            $supported_events An optional array of supported events.
     * @param string           $logger_context An optional logger context.
     *
     * @return object|\WP_Error
     */
    protected function get_discourse_webhook_data( $request, $supported_events = null, $logger_context = 'discourse_webhook' ) {

        $request = $this->verify_discourse_webhook_request( $request );

        if ( is_wp_error( $request ) ) {
            $this->logger->error( "$logger_context.webhook_verification_error", array( 'message', $request->get_error_message() ) );

            return new \WP_Error( 'discourse_webhook_error', __( 'The webhook was not verified.' ) );
        }

        $event_type = $request->get_header( 'x_discourse_event_type' );
        $event      = $request->get_header( 'x_discourse_event' );

        if ( $supported_events && ! in_array( $event, $supported_events, true ) ) {

            return new \WP_Error( 'discourse_webhook_error', __( 'The webhook event is not supported.' ) );
        }

        $json = $request->get_json_params();

        if ( is_wp_error( $json ) ) {
            $this->logger->error( "$logger_context.response_body_error" );

            return new \WP_Error( 'discourse_webhook_error', __( 'The webhook sent an invalid body.' ) );
        }

        return (object) array(
            'json'       => $json,
            'event_type' => $event_type,
            'event'      => $event,
        );
    }

    /**
     * Verify that the request originated from a Discourse webhook and the the secret keys match.
     *
     * @param \WP_REST_Request $data The WP_REST_Request object.
     *
     * @return \WP_Error|\WP_REST_Request
     */
    protected function verify_discourse_webhook_request( $data ) {
        $options = isset( $this->options ) ? $this->options : $this->get_options();
        // The X-Discourse-Event-Signature consists of 'sha256=' . hamc of raw payload.
        // It is generated by computing `hash_hmac( 'sha256', $payload, $secret )`.
        $sig = substr( $data->get_header( 'X-Discourse-Event-Signature' ), 7 );
        if ( $sig ) {
            $payload = $data->get_body();
            // Key used for verifying the request - a matching key needs to be set on the Discourse webhook.
            $secret = ! empty( $options['webhook-secret'] ) ? $options['webhook-secret'] : '';

            if ( ! $secret ) {

                return new \WP_Error( 'discourse_webhook_configuration_error', 'The webhook secret key has not been set.' );
            }

            if ( hash_hmac( 'sha256', $payload, $secret ) === $sig ) {

                return $data;
            } else {

                return new \WP_Error( 'discourse_webhook_authentication_error', 'Discourse Webhook Request Error: signatures did not match.' );
            }
        }

        return new \WP_Error( 'discourse_webhook_authentication_error', 'Discourse Webhook Request Error: the X-Discourse-Event-Signature was not set for the request.' );
    }

    /**
     * Failed response to webhook request.
     *
     * @param string $message Message to add to the response.
     */
    protected function failed_response( $message ) {
        return wp_json_encode(
            array(
                'success' => false,
                'message' => $message,
			)
            );
    }

    /**
     * Success response to webhook request.
     *
     * @param string $message Message to add to the response.
     */
    protected function success_response( $message = '' ) {
        return wp_json_encode(
            array(
                'success' => true,
                'message' => $message,
			)
            );
    }
}
