<?php
/**
 * Template REST endpoints.
 *
 * @package EntryFoundry
 */

namespace EntryFoundry\Core\Controllers\Rest;

use EntryFoundry\Core\Models\Template\TemplateRepository;
use EntryFoundry\Core\Models\Template\ValidationException;
use EntryFoundry\Core\Services\Security\RestPermissionService;
use EntryFoundry\Formidable\FormidableFieldPolicy;
use RuntimeException;

/**
 * Handles builder template persistence requests.
 */
final class TemplateRestController {

	/**
	 * REST namespace.
	 */
	private const REST_NAMESPACE = 'entryfoundry/v1';

	/**
	 * Template repository.
	 *
	 * @var TemplateRepository
	 */
	private $templates;

	/**
	 * REST authorization service.
	 *
	 * @var RestPermissionService
	 */
	private $permissions;

	/**
	 * Formidable field entitlement policy.
	 *
	 * @var FormidableFieldPolicy|null
	 */
	private $field_policy;

	/**
	 * Constructor.
	 *
	 * @param TemplateRepository         $templates    Template repository.
	 * @param RestPermissionService      $permissions  Authorization service.
	 * @param FormidableFieldPolicy|null $field_policy Formidable field entitlement policy.
	 */
	public function __construct( TemplateRepository $templates, RestPermissionService $permissions, ?FormidableFieldPolicy $field_policy = null ) {
		$this->templates    = $templates;
		$this->permissions  = $permissions;
		$this->field_policy = $field_policy;
	}

	/**
	 * Register template routes.
	 */
	public function register_routes(): void {
		register_rest_route(
			self::REST_NAMESPACE,
			'/templates',
			array(
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'create' ),
				'permission_callback' => array( $this, 'authorize_create' ),
			)
		);
		register_rest_route(
			self::REST_NAMESPACE,
			'/templates/(?P<template_id>[\d]+)',
			array(
				array(
					'methods'             => \WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get' ),
					'permission_callback' => array( $this, 'authorize_template' ),
					'args'                => array( 'template_id' => $this->get_id_schema() ),
				),
				array(
					'methods'             => \WP_REST_Server::EDITABLE,
					'callback'            => array( $this, 'update' ),
					'permission_callback' => array( $this, 'authorize_template' ),
					'args'                => array( 'template_id' => $this->get_id_schema() ),
				),
			)
		);
	}

	/**
	 * Authorize a collection request.
	 *
	 * @param \WP_REST_Request $request Request.
	 * @return true|\WP_Error
	 */
	public function authorize( \WP_REST_Request $request ) {
		return $this->permissions->authorize( $request );
	}

	/**
	 * Authorize creating and immediately publishing a template.
	 *
	 * @param \WP_REST_Request $request Request.
	 * @return true|\WP_Error
	 */
	public function authorize_create( \WP_REST_Request $request ) {
		$post_type = get_post_type_object( TemplateRepository::POST_TYPE );

		if ( ! $post_type ) {
			return new \WP_Error(
				'entryfoundry_template_create_forbidden',
				__( 'You cannot create EntryFoundry templates.', 'entryfoundry-pdf-builder-for-formidable-forms' ),
				array( 'status' => 403 )
			);
		}

		$authorized = $this->permissions->authorize( $request, $post_type->cap->create_posts );
		if ( true !== $authorized ) {
			return $authorized;
		}

		return current_user_can( $post_type->cap->publish_posts )
			? true
			: new \WP_Error(
				'entryfoundry_template_publish_forbidden',
				__( 'You cannot publish EntryFoundry templates.', 'entryfoundry-pdf-builder-for-formidable-forms' ),
				array( 'status' => 403 )
			);
	}

	/**
	 * Authorize access to a specific template.
	 *
	 * @param \WP_REST_Request $request Request.
	 * @return true|\WP_Error
	 */
	public function authorize_template( \WP_REST_Request $request ) {
		$authorized = $this->authorize( $request );
		if ( true !== $authorized ) {
			return $authorized;
		}

		return current_user_can( 'edit_post', (int) $request['template_id'] )
			? true
			: new \WP_Error( 'entryfoundry_template_forbidden', __( 'You cannot edit this EntryFoundry template.', 'entryfoundry-pdf-builder-for-formidable-forms' ), array( 'status' => 403 ) );
	}

	/**
	 * Get a template.
	 *
	 * @param \WP_REST_Request $request Request.
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function get( \WP_REST_Request $request ) {
		$template = $this->templates->get( (int) $request['template_id'] );
		return null === $template ? $this->not_found() : rest_ensure_response( $template );
	}

	/**
	 * Create a template.
	 *
	 * @param \WP_REST_Request $request Request.
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function create( \WP_REST_Request $request ) {
		return $this->save( $request );
	}

	/**
	 * Update a template.
	 *
	 * @param \WP_REST_Request $request Request.
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function update( \WP_REST_Request $request ) {
		return $this->save( $request, (int) $request['template_id'] );
	}

	/**
	 * Create or update a validated template.
	 *
	 * @param \WP_REST_Request $request     Request.
	 * @param int              $template_id Existing template ID, or zero.
	 * @return \WP_REST_Response|\WP_Error
	 */
	private function save( \WP_REST_Request $request, int $template_id = 0 ) {
		$template = $request->get_json_params();
		if ( ! is_array( $template ) ) {
			return new \WP_Error( 'entryfoundry_invalid_template', __( 'Template data must be a JSON object.', 'entryfoundry-pdf-builder-for-formidable-forms' ), array( 'status' => 400 ) );
		}

		if ( $this->field_policy && $this->field_policy->get_restricted_field_ids( $template ) ) {
			return new \WP_Error(
				'entryfoundry_pro_field_required',
				__( 'This template uses one or more Pro fields. Activate EntryFoundry Pro before saving it.', 'entryfoundry-pdf-builder-for-formidable-forms' ),
				array( 'status' => 403 )
			);
		}

		try {
			$saved = $template_id > 0 ? $this->templates->update( $template_id, $template ) : $this->templates->create( $template );
			return null === $saved ? $this->not_found() : rest_ensure_response( $saved );
		} catch ( ValidationException $exception ) {
			return new \WP_Error( 'entryfoundry_invalid_template', $exception->getMessage(), array( 'status' => 400 ) );
		} catch ( RuntimeException $exception ) {
			return new \WP_Error( 'entryfoundry_template_save_failed', __( 'The EntryFoundry template could not be saved.', 'entryfoundry-pdf-builder-for-formidable-forms' ), array( 'status' => 500 ) );
		}
	}

	/**
	 * Get a positive integer route schema.
	 *
	 * @return array<string, mixed>
	 */
	private function get_id_schema(): array {
		return array(
			'required'          => true,
			'sanitize_callback' => 'absint',
			'validate_callback' => static function ( $value ) {
				return is_numeric( $value ) && (int) $value > 0;
			},
		);
	}

	/**
	 * Build a template-not-found response.
	 *
	 * @return \WP_Error
	 */
	private function not_found(): \WP_Error {
		return new \WP_Error( 'entryfoundry_template_not_found', __( 'The EntryFoundry template was not found.', 'entryfoundry-pdf-builder-for-formidable-forms' ), array( 'status' => 404 ) );
	}
}
