<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * The FSE templates of a content type: the single view and the archive.
 *
 * WHAT IT SOLVES. Aura can create a content type, fill it and put it in the menu — and then the
 * visitor clicks and lands on a page drawn by the theme, which knows nothing about that content.
 * Worse: WE added the menu item, so WE are the ones who made REACHABLE a page we never designed.
 * Before, it was a URL nobody visited.
 *
 * WHAT WORDPRESS NEEDS TO RECOGNISE ONE, and it is not obvious: a `wp_template` is a post whose
 * `post_name` is the template slug (`single-pianeta`) and which MUST carry the active theme's
 * `wp_theme` term. Without the term, WordPress associates it with no theme and never uses it;
 * without the right slug, it uses it for the wrong page. `create_section()` does neither — it
 * creates builder content, not theme entities — which is why this class exists instead of reusing
 * that route.
 *
 * THE ORDER IS A SAFETY CHOICE. The template is created ONLY once there is content to put in it.
 * An empty Aura `wp_template` would replace the theme's with nothing: every product page on the
 * site would turn blank, and the worst case of a half-failure has to stay "the template is not
 * there" — which is how it was before — and not "the template is there and it is empty".
 */
class AIPB_Templates {

	/**
	 * The template slugs that make sense for a content type.
	 *
	 * The archive only if the type has one: `archive-pianeta` on a type without an archive is a
	 * template WordPress will never use, and it would sit there suggesting otherwise.
	 *
	 * @return array<string,string> slug => a description of its job, for the brief
	 */
	public static function wanted( $post_type ) {
		$object = get_post_type_object( $post_type );
		if ( ! $object ) {
			return array();
		}

		$singular = isset( $object->labels->singular_name ) ? $object->labels->singular_name : $post_type;
		$plural   = isset( $object->labels->name ) ? $object->labels->name : $post_type;

		$out = array(
			'single-' . $post_type => sprintf(
				/* translators: %s: the content type's singular name. */
				__( 'the page of ONE %s', 'aipb' ),
				mb_strtolower( $singular )
			),
		);

		if ( ! empty( $object->has_archive ) ) {
			$out[ 'archive-' . $post_type ] = sprintf(
				/* translators: %s: the content type's plural name. */
				__( 'the page listing all the %s', 'aipb' ),
				mb_strtolower( $plural )
			);
		}

		return $out;
	}

	/**
	 * Is there already a template with this slug, for the active theme?
	 *
	 * @return int the id, or 0.
	 */
	public static function find( $slug ) {
		$posts = get_posts( array(
			'post_type'   => 'wp_template',
			'name'        => $slug,
			'post_status' => array( 'publish', 'draft', 'auto-draft' ),
			'numberposts' => 1,
			'fields'      => 'ids',
			'tax_query'   => array( array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
				'taxonomy' => 'wp_theme',
				'field'    => 'name',
				'terms'    => get_stylesheet(),
			) ),
			'suppress_filters' => false,
		) );

		return $posts ? (int) $posts[0] : 0;
	}

	/**
	 * Creates the template and puts the already-generated section inside it.
	 *
	 * @param string $slug   `single-pianeta`, `archive-pianeta`.
	 * @param string $title  A readable title, for the templates list.
	 * @param array  $blocks The block stack, already generated: see the class comment.
	 *
	 * @return array{ok:bool,id:int,slug:string,reason:string}
	 */
	public static function create( $slug, $title, $blocks ) {
		$slug = sanitize_title( (string) $slug );
		if ( '' === $slug ) {
			return self::outcome( false, 0, '', 'empty-slug' );
		}

		if ( ! $blocks ) {
			// The case this class exists to prevent.
			return self::outcome( false, 0, $slug, 'no-content' );
		}

		$existing = self::find( $slug );
		if ( $existing ) {
			// It is already there: the theme may have written it, or we did on an earlier run. In
			// neither case is it overwritten — it is a page somebody is using.
			return self::outcome( false, $existing, $slug, 'already-there' );
		}

		$id = wp_insert_post( array(
			'post_type'   => 'wp_template',
			'post_name'   => $slug,
			'post_title'  => $title ? $title : $slug,
			'post_status' => 'publish',
		), true );

		if ( is_wp_error( $id ) || ! $id ) {
			return self::outcome( false, 0, $slug, 'not-created' );
		}

		// The theme's term: without it WordPress associates the template with no theme and never
		// uses it. It is the line that tells a real template from a post that looks like one.
		wp_set_object_terms( (int) $id, get_stylesheet(), 'wp_theme' );

		// Born in the Studio: it belongs to Aura, so the Studio opens it and not the Site Editor.
		if ( class_exists( 'AIPB_Ownership' ) ) {
			AIPB_Ownership::claim( (int) $id, AIPB_Ownership::AURA );
		}

		// wp_slash as in the Studio's own save, and not out of habit: update_post_meta strips the
		// slashes, so without putting them back the JSON arrives with its escaping eaten — and an
		// unreadable block stack is a template that renders nothing.
		update_post_meta( (int) $id, AIPB_META_BLOCKS, wp_slash( wp_json_encode( $blocks ) ) );

		// The snapshot in post_content: it is there for previews and for whoever deactivates the
		// plugin. The real rendering goes through live_block_template(), which runs on every
		// request with the query in front of it — that is how post.title becomes the title of the
		// content being looked at.
		if ( class_exists( 'AIPB_Render' ) ) {
			AIPB_Render::sync_post_content( (int) $id );
		}

		return self::outcome( true, (int) $id, $slug, 'created' );
	}

	/** @return array{ok:bool,id:int,slug:string,reason:string} */
	protected static function outcome( $ok, $id, $slug, $reason ) {
		return array( 'ok' => $ok, 'id' => $id, 'slug' => $slug, 'reason' => $reason );
	}
}
