<?php
/**
 * Shared REST controller behaviour.
 *
 * @package RewindBrevoFormBridge
 */

namespace RewindBrevoFormBridge\Rest;

defined( 'ABSPATH' ) || exit;

/**
 * Base for every rbfb/v1 controller.
 *
 * Subclasses implement register_routes() and delegate to models and the
 * app/ service layer; they hold no business logic of their own. Every route
 * a subclass registers must pass a real permission_callback — the shared
 * can_manage() below — never __return_true.
 *
 * @since 0.4.0
 */
abstract class BaseController {

	/**
	 * REST namespace shared by every controller.
	 *
	 * @var string
	 */
	const NAMESPACE_V1 = 'rbfb/v1';

	/**
	 * HTTP status: OK.
	 *
	 * @var int
	 */
	const HTTP_OK = 200;

	/**
	 * HTTP status: Created.
	 *
	 * @var int
	 */
	const HTTP_CREATED = 201;

	/**
	 * HTTP status: Bad Request.
	 *
	 * @var int
	 */
	const HTTP_BAD_REQUEST = 400;

	/**
	 * HTTP status: Forbidden.
	 *
	 * @var int
	 */
	const HTTP_FORBIDDEN = 403;

	/**
	 * HTTP status: Not Found.
	 *
	 * @var int
	 */
	const HTTP_NOT_FOUND = 404;

	/**
	 * HTTP status: Internal Server Error.
	 *
	 * @var int
	 */
	const HTTP_SERVER_ERROR = 500;

	/**
	 * This controller's route base, e.g. 'feeds'.
	 *
	 * @var string
	 */
	protected string $rest_base;

	/**
	 * Constructor.
	 *
	 * @since 0.4.0
	 * @param string $rest_base Route base for this controller.
	 */
	public function __construct( string $rest_base ) {
		$this->rest_base = $rest_base;
	}

	/**
	 * Register this controller's routes. Called on rest_api_init.
	 *
	 * @since 0.4.0
	 * @return void
	 */
	abstract public function register_routes(): void;

	/**
	 * Shared permission callback: the dashboard is an administrator tool.
	 *
	 * The REST cookie authentication handler verifies the X-WP-Nonce header
	 * before this runs, so a capability check is the whole gate here.
	 *
	 * @since 0.4.0
	 * @return true|\WP_Error
	 */
	public function can_manage() {
		if ( ! current_user_can( 'manage_options' ) ) {
			return new \WP_Error(
				'rbfb_forbidden',
				__( 'You do not have permission to manage this plugin.', 'rewind-form-bridge-for-brevo' ),
				array( 'status' => self::HTTP_FORBIDDEN )
			);
		}

		return true;
	}

	/**
	 * A successful response.
	 *
	 * @since 0.4.0
	 * @param mixed $data   Payload.
	 * @param int   $status HTTP status.
	 * @return \WP_REST_Response
	 */
	protected function respond( $data, int $status = self::HTTP_OK ): \WP_REST_Response {
		return new \WP_REST_Response( $data, $status );
	}

	/**
	 * An error response.
	 *
	 * @since 0.4.0
	 * @param string $code    Machine-readable code.
	 * @param string $message Human sentence for the UI.
	 * @param int    $status  HTTP status.
	 * @return \WP_Error
	 */
	protected function error( string $code, string $message, int $status = self::HTTP_BAD_REQUEST ): \WP_Error {
		return new \WP_Error( $code, $message, array( 'status' => $status ) );
	}

	/**
	 * Convert a WP_Error from the app/ layer into a REST error, preserving
	 * its human message.
	 *
	 * @since 0.4.0
	 * @param \WP_Error $wp_error Error from the service layer.
	 * @param int       $status   HTTP status to report.
	 * @return \WP_Error
	 */
	protected function from_wp_error( \WP_Error $wp_error, int $status = self::HTTP_BAD_REQUEST ): \WP_Error {
		return $this->error(
			$wp_error->get_error_code() ? (string) $wp_error->get_error_code() : 'rbfb_error',
			$wp_error->get_error_message(),
			$status
		);
	}

	/**
	 * Sanitize callback for a free-text search term.
	 *
	 * @since 0.4.0
	 * @param mixed $value Raw value.
	 * @return string
	 */
	public function sanitize_text( $value ): string {
		return sanitize_text_field( (string) $value );
	}

	/**
	 * Validate callback for a Y-m-d date, allowing an empty value.
	 *
	 * @since 0.4.0
	 * @param mixed $value Raw value.
	 * @return bool
	 */
	public function validate_date( $value ): bool {
		$value = (string) $value;

		if ( '' === $value ) {
			return true;
		}

		return (bool) preg_match( '/^\d{4}-\d{2}-\d{2}$/', $value );
	}
}
