<?php
/**
 * REST API endpoints for CartCue.
 *
 * @package CartCue
 */

namespace CartCue;

defined( 'ABSPATH' ) || exit;

final class RestController {

	public static function register_routes(): void {
		register_rest_route(
			'cartcue/v1',
			'/plays',
			[
				'methods'             => 'GET',
				'callback'            => [ __CLASS__, 'list_plays' ],
				'permission_callback' => static function (): bool {
					return current_user_can( 'manage_woocommerce' );
				},
			]
		);

		register_rest_route(
			'cartcue/v1',
			'/trigger',
			[
				'methods'             => 'POST',
				'callback'            => [ __CLASS__, 'manual_trigger' ],
				'permission_callback' => static function (): bool {
					return current_user_can( 'manage_woocommerce' );
				},
			]
		);

		if ( ! Plugin::instance()->is_pro() ) {
			return;
		}

		register_rest_route(
			'cartcue/v1',
			'/inbound/(?P<workflow_id>\d+)',
			[
				'methods'             => 'POST',
				'callback'            => [ __CLASS__, 'inbound_trigger' ],
				'permission_callback' => '__return_true',
			]
		);

		// Legacy route — token in URL (deprecated; prefer POST body token).
		register_rest_route(
			'cartcue/v1',
			'/inbound/(?P<workflow_id>\d+)/(?P<token>[A-Za-z0-9_\-]+)',
			[
				'methods'             => 'POST',
				'callback'            => [ __CLASS__, 'inbound_trigger' ],
				'permission_callback' => '__return_true',
			]
		);
	}

	/**
	 * @param \WP_REST_Request $request Request.
	 * @return \WP_REST_Response
	 */
	public static function list_plays( \WP_REST_Request $request ): \WP_REST_Response {
		global $wpdb;
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery
		$rows = $wpdb->get_results(
			$wpdb->prepare(
				'SELECT id, name, trigger_slug, status, run_count, created_at, updated_at FROM %i ORDER BY created_at DESC',
				DB::workflows()
			),
			ARRAY_A
		) ?: [];

		return new \WP_REST_Response( $rows, 200 );
	}

	/**
	 * @param \WP_REST_Request $request Request.
	 * @return \WP_REST_Response|\WP_Error
	 */
	public static function manual_trigger( \WP_REST_Request $request ) {
		$workflow_id = absint( $request->get_param( 'workflow_id' ) );
		$data        = $request->get_param( 'data' );

		if ( ! $workflow_id ) {
			return new \WP_Error( 'cartcue_missing_id', __( 'workflow_id is required.', 'cartcue-sales-automation' ), [ 'status' => 400 ] );
		}

		if ( ! is_array( $data ) ) {
			$data = [];
		}

		if ( ! EnvironmentGuard::should_run_workflows( $data ) ) {
			return new \WP_Error(
				'cartcue_manual_environment',
				__( 'Automations are paused on this environment.', 'cartcue-sales-automation' ),
				[ 'status' => 503 ]
			);
		}

		if ( ! RateLimiter::allow( 'cartcue_manual_rl_' . get_current_user_id(), 30, MINUTE_IN_SECONDS ) ) {
			return new \WP_Error( 'cartcue_manual_rate', __( 'Rate limited.', 'cartcue-sales-automation' ), [ 'status' => 429 ] );
		}

		$runner   = Plugin::instance()->runner;
		$workflow = $runner->get_workflow( $workflow_id );
		if ( ! $workflow || 'active' !== ( $workflow['status'] ?? '' ) ) {
			return new \WP_Error( 'cartcue_play_inactive', __( 'Play not found or not active.', 'cartcue-sales-automation' ), [ 'status' => 404 ] );
		}

		$runner->trigger( $workflow_id, $data );

		return new \WP_REST_Response(
			[
				'ok'          => true,
				'workflow_id' => $workflow_id,
			],
			200
		);
	}

	/**
	 * Public inbound webhook trigger endpoint.
	 *
	 * Security: send the secret token in the JSON body (`token` field), not only in the URL.
	 * Optional HMAC: `X-CartCue-Signature` header = hash_hmac( 'sha256', raw_body, token ).
	 *
	 * @param \WP_REST_Request $request Request.
	 * @return \WP_REST_Response|\WP_Error
	 */
	public static function inbound_trigger( \WP_REST_Request $request ) {
		if ( ! Plugin::instance()->is_pro() ) {
			return new \WP_Error( 'cartcue_inbound_license', __( 'Inbound webhooks require an active CartCue Pro license.', 'cartcue-sales-automation' ), [ 'status' => 403 ] );
		}

		$workflow_id = absint( $request->get_param( 'workflow_id' ) );
		$token       = self::resolve_inbound_token( $request );

		if ( ! $workflow_id || '' === $token ) {
			return new \WP_Error( 'cartcue_inbound_missing', __( 'workflow_id and token are required.', 'cartcue-sales-automation' ), [ 'status' => 400 ] );
		}

		$raw = $request->get_body();
		if ( strlen( $raw ) > 32 * 1024 ) {
			return new \WP_Error( 'cartcue_inbound_too_large', __( 'Payload too large.', 'cartcue-sales-automation' ), [ 'status' => 413 ] );
		}

		if ( ! RateLimiter::allow( 'cartcue_inbound_rl_' . $workflow_id, 60, MINUTE_IN_SECONDS ) ) {
			return new \WP_Error( 'cartcue_inbound_rate', __( 'Rate limited.', 'cartcue-sales-automation' ), [ 'status' => 429 ] );
		}

		$plugin = Plugin::instance();
		$runner = $plugin->runner;
		$play   = $runner->get_workflow( $workflow_id );
		if ( ! $play || 'inbound_webhook' !== (string) ( $play['trigger_slug'] ?? '' ) ) {
			return new \WP_Error( 'cartcue_inbound_not_found', __( 'Inbound webhook play not found.', 'cartcue-sales-automation' ), [ 'status' => 404 ] );
		}

		if ( 'active' !== (string) ( $play['status'] ?? '' ) ) {
			return new \WP_Error( 'cartcue_inbound_paused', __( 'Play is paused.', 'cartcue-sales-automation' ), [ 'status' => 409 ] );
		}

		$saved_token = (string) ( $play['trigger_settings']['token'] ?? '' );
		if ( '' === $saved_token || ! hash_equals( $saved_token, $token ) ) {
			return new \WP_Error( 'cartcue_inbound_forbidden', __( 'Invalid inbound webhook token.', 'cartcue-sales-automation' ), [ 'status' => 403 ] );
		}

		$sig_header = (string) $request->get_header( 'x_cartcue_signature' );
		if ( '' !== $sig_header ) {
			$expected = hash_hmac( 'sha256', $raw, $saved_token );
			if ( ! hash_equals( $expected, $sig_header ) ) {
				return new \WP_Error( 'cartcue_inbound_signature', __( 'Invalid webhook signature.', 'cartcue-sales-automation' ), [ 'status' => 403 ] );
			}
		}

		$body = $request->get_json_params();
		if ( ! is_array( $body ) ) {
			$body = $request->get_body_params();
		}
		if ( ! is_array( $body ) ) {
			$body = [];
		}

		unset( $body['token'] );

		$data = isset( $body['data'] ) && is_array( $body['data'] ) ? $body['data'] : $body;
		$data['trigger']              = 'inbound_webhook';
		$data['_source']              = 'inbound_webhook';
		$data['_inbound_received_at'] = current_time( 'mysql' );

		if ( function_exists( 'as_enqueue_async_action' ) ) {
			as_enqueue_async_action( 'cartcue_inbound_dispatch', [ $workflow_id, $data ], 'cartcue' );
			return new \WP_REST_Response(
				[
					'ok'     => true,
					'queued' => true,
				],
				202
			);
		}

		$runner->trigger( $workflow_id, $data );

		return new \WP_REST_Response(
			[
				'ok'          => true,
				'workflow_id' => $workflow_id,
			],
			200
		);
	}

	private static function resolve_inbound_token( \WP_REST_Request $request ): string {
		$url_token = sanitize_text_field( (string) $request->get_param( 'token' ) );
		if ( '' !== $url_token ) {
			return $url_token;
		}

		$body = $request->get_json_params();
		if ( is_array( $body ) && ! empty( $body['token'] ) ) {
			return sanitize_text_field( (string) $body['token'] );
		}

		$params = $request->get_body_params();
		if ( is_array( $params ) && ! empty( $params['token'] ) ) {
			return sanitize_text_field( (string) $params['token'] );
		}

		return '';
	}
}
