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

/**
 * Which editor owns a piece of content.
 *
 * Aura and Gutenberg live on the same site: every page belongs to ONE of them, never to both. The
 * owner is recorded in the '_aipb_editor' meta:
 *
 *  - 'aura'      -> Gutenberg is off on that post: the layout is edited in the Studio, and
 *                   post.php stays the FIELDS screen (ACF, SEO, taxonomies) with no content area.
 *                   Entities with no fields (sections, patterns, templates) skip post.php entirely
 *                   and go straight to the Studio.
 *  - 'gutenberg' -> the Studio does not touch that post; the builder does not appear in its actions.
 *  - absent      -> still FREE: the first save decides (or the user, with "Build with Aura").
 *
 * For content created before this flag existed, the owner is INFERRED — see {@see owner()}.
 */
class AIPB_Ownership {

	const META      = '_aipb_editor';
	const AURA      = 'aura';
	const GUTENBERG = 'gutenberg';

	public static function init() {
		// A save carrying block content on a still-free post assigns it to Gutenberg.
		add_action( 'save_post', array( __CLASS__, 'claim_on_editor_save' ), 10, 2 );
	}

	/**
	 * The content's owner: 'aura', 'gutenberg', or '' when it is still free.
	 */
	public static function owner( $post_id ) {
		$post_id = (int) $post_id;
		if ( ! $post_id ) {
			return '';
		}

		$post = get_post( $post_id );
		if ( ! $post ) {
			return '';
		}

		// The sections CPT is ours by definition: it does not even support 'editor'.
		if ( AIPB_CPT::POST_TYPE === $post->post_type ) {
			return self::AURA;
		}

		$flag = (string) get_post_meta( $post_id, self::META, true );
		if ( self::AURA === $flag || self::GUTENBERG === $flag ) {
			return $flag;
		}

		// Content older than the flag: infer it, and THE ORDER MATTERS. Aura first, because Aura
		// mirrors its own output into post_content as a wp:html block (see
		// AIPB_Render::sync_post_content): has_blocks() on its own would answer "Gutenberg".
		if ( self::has_aura_content( $post_id ) ) {
			return self::AURA;
		}

		return '' !== trim( (string) $post->post_content ) ? self::GUTENBERG : '';
	}

	/**
	 * Does the content really hold material built with Aura (blocks or a legacy template)?
	 *
	 * A TRANSLATION COUNTS AS OURS EVEN WHEN IT IS EMPTY. WPML can create the other language's post
	 * without our meta — duplicate mode copies nothing we asked it to leave alone — and that post is
	 * an Aura page all the same: it draws from the original (see AIPB_WPML::content_id). Answering
	 * "no" here would send it to the inference below, which reads the copied post_content, finds
	 * blocks in it and hands the page to Gutenberg for good.
	 */
	public static function has_aura_content( $post_id ) {
		if ( self::has_own_content( $post_id ) ) {
			return true;
		}

		$source = AIPB_WPML::content_id( $post_id );

		return ( $source !== (int) $post_id ) && self::has_own_content( $source );
	}

	/** The same question, asked of this post ALONE: no translation, no origin, just its own meta. */
	public static function has_own_content( $post_id ) {
		$blocks = get_post_meta( (int) $post_id, AIPB_META_BLOCKS, true );
		if ( $blocks && '[]' !== trim( (string) $blocks ) ) {
			return true;
		}

		return '' !== (string) get_post_meta( (int) $post_id, AIPB_META_TEMPLATE, true );
	}

	public static function is_aura( $post_id ) {
		return self::AURA === self::owner( $post_id );
	}

	public static function is_gutenberg( $post_id ) {
		return self::GUTENBERG === self::owner( $post_id );
	}

	public static function claim( $post_id, $owner ) {
		if ( self::AURA !== $owner && self::GUTENBERG !== $owner ) {
			return false;
		}

		update_post_meta( (int) $post_id, self::META, $owner );

		return true;
	}

	/**
	 * Assigns to Gutenberg a free piece of content that was just saved with real content.
	 *
	 * An empty post stays free: that way a freshly created draft can still go to Aura.
	 */
	public static function claim_on_editor_save( $post_id, $post ) {
		if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) {
			return;
		}
		if ( ! $post || in_array( $post->post_status, array( 'auto-draft', 'trash' ), true ) ) {
			return;
		}
		if ( ! AIPB_Config::is_builder_post_type( $post->post_type ) ) {
			return;
		}
		if ( '' !== (string) get_post_meta( $post_id, self::META, true ) ) {
			return; // already assigned
		}
		if ( self::has_aura_content( $post_id ) ) {
			return; // it is ours: the Studio claims it on save
		}
		if ( '' === trim( (string) $post->post_content ) ) {
			return; // empty: it stays free
		}

		self::claim( $post_id, self::GUTENBERG );
	}

	/**
	 * The keys of the Site Editor entities (FSE patterns and templates) that Aura owns.
	 *
	 * The Site Editor's JS guard needs them: it is a SPA, and it has to know BEFORE navigating
	 * which entities to divert to the Studio and which to leave to Gutenberg. For templates the
	 * key is both the numeric ID and the "theme//slug" form the Site Editor uses in its URLs.
	 */
	public static function studio_entity_keys() {
		$ids = get_posts( array(
			'post_type'      => AIPB_Config::studio_native_types(),
			'post_status'    => 'any',
			'posts_per_page' => (int) apply_filters( 'aipb_owned_entities_limit', 300 ),
			'fields'         => 'ids',
			'no_found_rows'  => true,
			'meta_query'     => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
				'relation' => 'OR',
				array( 'key' => self::META, 'value' => self::AURA ),
				array( 'key' => AIPB_META_TEMPLATE, 'compare' => 'EXISTS' ),
				array( 'key' => AIPB_META_BLOCKS, 'compare' => 'EXISTS' ),
			),
		) );

		$keys = array();
		foreach ( $ids as $id ) {
			if ( ! self::is_aura( $id ) ) {
				continue;
			}

			$keys[] = (string) $id;

			if ( AIPB_Config::is_template_type( get_post_type( $id ) ) ) {
				$keys[] = self::theme_of( $id ) . '//' . get_post_field( 'post_name', $id );
			}
		}

		return array_values( array_unique( $keys ) );
	}

	/** The theme a template belongs to: its wp_theme term, or the active stylesheet. */
	protected static function theme_of( $post_id ) {
		$terms = get_the_terms( $post_id, 'wp_theme' );

		return ( $terms && ! is_wp_error( $terms ) ) ? $terms[0]->name : get_stylesheet();
	}
}
