<?php
/**
 * Onboarding state-machine REST controller.
 *
 * @package Carticy\Conversions\Rest
 */

declare(strict_types=1);

namespace Carticy\Conversions\Rest;

use Carticy\Conversions\Diagnostics\VerificationStatus;

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

/**
 * Exposes the server-validated onboarding flow. Transitions are validated on the
 * server; reaching `complete` additionally requires live verification evidence,
 * so the frontend can never assert a healthy/finished setup the backend has not
 * confirmed on the wire.
 */
final class OnboardingController extends AbstractRestController {

	/**
	 * State machine.
	 *
	 * @var OnboardingStateMachine
	 */
	private OnboardingStateMachine $machine;

	/**
	 * Verification evidence evaluator.
	 *
	 * @var VerificationStatus
	 */
	private VerificationStatus $verification;

	/**
	 * Constructor.
	 *
	 * @param OnboardingStateMachine $machine State machine.
	 * @param VerificationStatus     $verification Verification evidence evaluator.
	 */
	public function __construct( OnboardingStateMachine $machine, VerificationStatus $verification ) {
		$this->machine      = $machine;
		$this->verification = $verification;
	}

	/**
	 * Declared routes.
	 *
	 * @return array<int, array<string, mixed>>
	 */
	public function routes(): array {
		return array(
			array(
				'methods'  => 'GET',
				'route'    => '/onboarding',
				'callback' => array( $this, 'get_state' ),
			),
			array(
				'methods'  => 'POST',
				'route'    => '/onboarding/transition',
				'callback' => array( $this, 'transition' ),
			),
		);
	}

	/**
	 * Read the current onboarding position.
	 *
	 * @return \WP_REST_Response
	 */
	public function get_state(): \WP_REST_Response {
		$evidence = $this->verification->evaluate();

		return $this->ok( $this->machine->snapshot( (bool) $evidence['canComplete'], $evidence['blockers'] ) );
	}

	/**
	 * Attempt a server-validated transition.
	 *
	 * @param \WP_REST_Request $request REST request.
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function transition( \WP_REST_Request $request ) {
		$body = $this->body( $request );
		$to   = isset( $body['to'] ) ? (string) $body['to'] : '';
		if ( '' === $to ) {
			return $this->fail( 'carticy_missing_state', __( 'A target onboarding state is required.', 'carticy-conversions-for-woocommerce' ), 400 );
		}

		// Only the `complete` target consults verification evidence; evaluating
		// it on every step would query orders/dispatch rows needlessly.
		$can_complete = 'complete' === sanitize_key( $to ) && $this->verification->can_complete();

		$result = $this->machine->transition( $to, $can_complete );

		if ( empty( $result['ok'] ) ) {
			return $this->map_error( (string) ( $result['error'] ?? 'invalid_transition' ), $result );
		}

		return $this->get_state();
	}

	/**
	 * Map a state-machine error to an HTTP error response.
	 *
	 * @param string               $error Error code.
	 * @param array<string, mixed> $result State-machine result.
	 * @return \WP_Error
	 */
	private function map_error( string $error, array $result ): \WP_Error {
		$context = array();
		foreach ( array( 'from', 'to', 'allowed' ) as $key ) {
			if ( isset( $result[ $key ] ) ) {
				$context[ $key ] = $result[ $key ];
			}
		}

		if ( 'verification_required' === $error ) {
			return $this->fail(
				'carticy_verification_required',
				__( 'Onboarding cannot complete until tracking is verified on the wire.', 'carticy-conversions-for-woocommerce' ),
				409,
				$context
			);
		}

		if ( 'unknown_state' === $error ) {
			return $this->fail(
				'carticy_unknown_state',
				__( 'Unknown onboarding state.', 'carticy-conversions-for-woocommerce' ),
				400,
				$context
			);
		}

		return $this->fail(
			'carticy_invalid_transition',
			__( 'That onboarding transition is not allowed from the current step.', 'carticy-conversions-for-woocommerce' ),
			400,
			$context
		);
	}
}
