<?php
/**
 * Blog read tools plus canonical create/update/publish adapters.
 *
 * @package Niwa_AI
 */

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

trait Niwa_AI_Tools_Blog_Posts {

	public function execute_blog_posts_list( $args ) {
		$status   = isset( $args['status'] ) ? (string) $args['status'] : 'any';
		$search   = isset( $args['search'] ) ? sanitize_text_field( trim( (string) $args['search'] ) ) : '';
		$limit    = isset( $args['limit'] ) ? max( 1, min( 20, intval( $args['limit'] ) ) ) : 10;
		$category = isset( $args['category'] ) ? sanitize_text_field( trim( (string) $args['category'] ) ) : '';
		$tag      = isset( $args['tag'] ) ? sanitize_text_field( trim( (string) $args['tag'] ) ) : '';
		$query    = array(
			'post_type'      => 'post',
			'post_status'    => 'any' === $status ? array( 'publish', 'draft', 'pending', 'private', 'future' ) : $status,
			'posts_per_page' => $limit,
			'orderby'        => 'date',
			'order'          => 'DESC',
		);
		if ( '' !== $search ) {
			$query['s'] = $search;
		}
		if ( '' !== $category ) {
			$term = get_term_by( 'name', $category, 'category' );
			if ( ! $term ) {
				$term = get_term_by( 'slug', sanitize_title( $category ), 'category' );
			}
			if ( $term && ! is_wp_error( $term ) ) {
				$query['cat'] = intval( $term->term_id );
			}
		}
		if ( '' !== $tag ) {
			$term = get_term_by( 'name', $tag, 'post_tag' );
			if ( ! $term ) {
				$term = get_term_by( 'slug', sanitize_title( $tag ), 'post_tag' );
			}
			if ( $term && ! is_wp_error( $term ) ) {
				$query['tag_id'] = intval( $term->term_id );
			}
		}

		$result = array();
		foreach ( get_posts( $query ) as $post ) {
			$result[] = array(
				'post_id'           => intval( $post->ID ),
				'title'             => (string) $post->post_title,
				'slug'              => (string) $post->post_name,
				'status'            => (string) $post->post_status,
				'date'              => (string) $post->post_date,
				'categories'        => (array) wp_get_post_categories( $post->ID, array( 'fields' => 'names' ) ),
				'tags'              => (array) wp_get_post_tags( $post->ID, array( 'fields' => 'names' ) ),
				'excerpt_preview'   => wp_trim_words( $post->post_excerpt ? $post->post_excerpt : $post->post_content, 30, '...' ),
				'featured_image_id' => get_post_thumbnail_id( $post->ID ) ? intval( get_post_thumbnail_id( $post->ID ) ) : null,
				'niwa_managed'      => '1' === (string) get_post_meta( $post->ID, Niwa_AI_Blog_Quality_Gate::META_MANAGED, true ),
			);
		}
		return array( 'ok' => true, 'count' => count( $result ), 'posts' => $result );
	}

	public function execute_blog_post_get( $args ) {
		$post_id = isset( $args['post_id'] ) ? intval( $args['post_id'] ) : 0;
		$slug    = isset( $args['slug'] ) ? sanitize_title( trim( (string) $args['slug'] ) ) : '';
		if ( $post_id <= 0 && '' === $slug ) {
			return array( 'ok' => false, 'error' => 'post_id or slug is required.' );
		}
		if ( $post_id > 0 ) {
			$post = get_post( $post_id );
		} else {
			$found = get_posts( array( 'name' => $slug, 'post_type' => 'post', 'post_status' => array( 'publish', 'draft', 'pending', 'private', 'future' ), 'posts_per_page' => 1 ) );
			$post  = ! empty( $found ) ? $found[0] : null;
		}
		if ( ! $post || 'post' !== $post->post_type ) {
			return array( 'ok' => false, 'error' => 'Blog post was not found.' );
		}
		$thumb_id = get_post_thumbnail_id( $post->ID );
		return array(
			'ok'   => true,
			'post' => array(
				'post_id'            => intval( $post->ID ),
				'title'              => (string) $post->post_title,
				'slug'               => (string) $post->post_name,
				'status'             => (string) $post->post_status,
				'date'               => (string) $post->post_date,
				'modified'           => (string) $post->post_modified,
				'content'            => (string) $post->post_content,
				'excerpt'            => (string) $post->post_excerpt,
				'categories'         => (array) wp_get_post_categories( $post->ID, array( 'fields' => 'names' ) ),
				'tags'               => (array) wp_get_post_tags( $post->ID, array( 'fields' => 'names' ) ),
				'featured_image_id'  => $thumb_id ? intval( $thumb_id ) : null,
				'featured_image_url' => $thumb_id ? wp_get_attachment_url( $thumb_id ) : null,
				'meta_description'   => $this->get_seo_meta_description( $post->ID ),
				'permalink'          => get_permalink( $post->ID ),
				'niwa_managed'       => '1' === (string) get_post_meta( $post->ID, Niwa_AI_Blog_Quality_Gate::META_MANAGED, true ),
				'policy_version'     => (string) get_post_meta( $post->ID, Niwa_AI_Blog_Quality_Gate::META_POLICY_VERSION, true ),
				'quality_report'     => get_post_meta( $post->ID, Niwa_AI_Blog_Quality_Gate::META_QUALITY_REPORT, true ),
			),
		);
	}

	private function validate_create_blog_post_action_args( $args ) {
		return $this->validate_create_niwa_blog_action_args( $args );
	}

	private function preview_create_blog_post( $args ) {
		return $this->blog_pipeline_preview_canonical_create( $args, 'html', 'create_blog_post' );
	}

	private function execute_create_blog_post_action( $action ) {
		$args = isset( $action['args'] ) && is_array( $action['args'] ) ? $action['args'] : array();
		return $this->blog_pipeline_execute_canonical_create( $args, 'html', 'create_blog_post' );
	}

	private function validate_update_blog_post_action_args( $args ) {
		$post_id = intval( isset( $args['post_id'] ) ? $args['post_id'] : 0 );
		if ( $post_id <= 0 ) {
			return array( 'success' => false, 'error' => 'Post ID is required for update.' );
		}
		if ( isset( $args['status'] ) && 'publish' === sanitize_key( (string) $args['status'] ) ) {
			return array( 'success' => false, 'error' => 'update_blog_post cannot publish. Use publish_blog_post so the current-state gate and approval hash are enforced.' );
		}
		return array( 'success' => true );
	}

	private function preview_update_blog_post( $args ) {
		$validation = $this->validate_update_blog_post_action_args( $args );
		if ( empty( $validation['success'] ) ) {
			return $validation;
		}
		$post = get_post( intval( $args['post_id'] ) );
		if ( ! $post || 'post' !== $post->post_type ) {
			return array( 'success' => false, 'error' => 'Blog post was not found.' );
		}
		$proposal = $this->blog_post_proposal_for_update( $post, $args );
		$prepared = $this->blog_pipeline_quality_gate()->prepare( $proposal, $this->blog_pipeline_quality_context( $post->ID ) );
		if ( empty( $prepared['report']['pass'] ) ) {
			return array( 'success' => false, 'error' => 'Blog quality gate blocked the update.', 'quality_report' => $prepared['report'], 'corrections' => $prepared['report']['critical'] );
		}
		$normalized = $prepared['proposal'];
		$normalized['post_id']        = intval( $post->ID );
		if ( array_key_exists( 'status', $args ) ) {
			$normalized['status'] = sanitize_key( (string) $args['status'] );
		}
		$normalized['allow_new_terms'] = ! empty( $args['allow_new_terms'] );
		return array(
			'success'        => true,
			'quality_report' => $prepared['report'],
			'action'         => array(
				'type'             => 'update_blog_post',
				'label'            => 'Update governed blog post',
				'summary'          => 'Update blog post #' . intval( $post->ID ) . ' through the canonical pipeline.',
				'rollback_plan'    => 'Restore exact post fields, taxonomy, thumbnail, SEO, Niwa assets, quality metadata, and prior key absence.',
				'rollback_support' => 'full',
				'targets'          => array( array( 'post_id' => intval( $post->ID ), 'post_title' => (string) $post->post_title ) ),
				'args'             => $normalized,
			),
		);
	}

	private function execute_update_blog_post_action( $action ) {
		$args       = isset( $action['args'] ) && is_array( $action['args'] ) ? $action['args'] : array();
		$validation = $this->validate_update_blog_post_action_args( $args );
		if ( empty( $validation['success'] ) ) {
			return $validation;
		}
		$post = get_post( intval( $args['post_id'] ) );
		if ( ! $post || 'post' !== $post->post_type ) {
			return array( 'success' => false, 'error' => 'Blog post was not found.' );
		}

		$restore  = $this->build_blog_post_restore_entry( $post );
		$proposal = $this->blog_post_proposal_for_update( $post, $args );
		$prepared = $this->blog_pipeline_quality_gate()->prepare( $proposal, $this->blog_pipeline_quality_context( $post->ID ) );
		$proposal = $prepared['proposal'];
		$report   = $prepared['report'];
		if ( empty( $report['pass'] ) ) {
			return array( 'success' => false, 'error' => 'Blog quality gate blocked the update. No live content was changed.', 'quality_report' => $report, 'corrections' => $report['critical'] );
		}

		$allow_new = ! empty( $args['allow_new_terms'] ) || ! empty( $this->blog_pipeline_settings()['allow_new_taxonomy_terms'] );
		$taxonomy  = $this->blog_pipeline_resolve_taxonomy( $proposal, $allow_new );
		if ( empty( $taxonomy['success'] ) ) {
			$this->blog_pipeline_cleanup_created_terms( isset( $taxonomy['created'] ) ? $taxonomy['created'] : array() );
			return $taxonomy;
		}
		$report['taxonomy_report'] = $taxonomy['report'];
		$rendered = $this->blog_pipeline_render_content( $proposal );
		if ( empty( $rendered['success'] ) ) {
			$this->blog_pipeline_cleanup_created_terms( $taxonomy['created'] );
			return $rendered;
		}

		$status = isset( $args['status'] ) ? sanitize_key( (string) $args['status'] ) : (string) $post->post_status;
		if ( ! in_array( $status, array( 'draft', 'pending', 'private', 'publish' ), true ) ) {
			$status = (string) $post->post_status;
		}
		$updated = wp_update_post(
			array(
				'ID'           => intval( $post->ID ),
				'post_title'   => $proposal['title'],
				'post_content' => $rendered['content'],
				'post_excerpt' => $proposal['excerpt'],
				'post_name'    => $proposal['slug'],
				'post_status'  => $status,
				'post_author'  => intval( $proposal['author_id'] ),
			),
			true
		);
		if ( is_wp_error( $updated ) || intval( $updated ) <= 0 ) {
			$this->blog_pipeline_cleanup_created_terms( $taxonomy['created'] );
			return array( 'success' => false, 'error' => is_wp_error( $updated ) ? $updated->get_error_message() : 'WordPress did not update the blog post.' );
		}

		$relations = $this->blog_pipeline_apply_post_relations( $post->ID, $taxonomy, $proposal );
		if ( ! empty( $proposal['_featured_image_explicit'] ) && 0 === intval( $proposal['featured_image_id'] ) ) {
			delete_post_thumbnail( $post->ID );
		}
		if ( empty( $relations['success'] ) ) {
			$this->blog_post_restore_after_failed_update( $restore );
			$this->blog_pipeline_cleanup_created_terms( $taxonomy['created'] );
			return array( 'success' => false, 'error' => $relations['error'], 'restored_after_failure' => true );
		}

		$actual = array_merge( $proposal, array( 'source_content' => $proposal['content'], 'rendered_content' => $rendered['content'] ) );
		$report['facts']['source_hash'] = Niwa_AI_Blog_Quality_Gate::source_hash( $actual );
		$managed_write = $this->blog_pipeline_persist_managed_state( $post->ID, $actual, $report, $rendered );
		$seo_write     = ! empty( $managed_write['success'] ) ? $this->blog_pipeline_set_seo_meta( $post->ID, $proposal['meta_description'], $proposal['focus_keyword'] ) : $managed_write;
		if ( empty( $managed_write['success'] ) || empty( $seo_write['success'] ) ) {
			$this->blog_post_restore_after_failed_update( $restore );
			$this->blog_pipeline_cleanup_created_terms( $taxonomy['created'] );
			return array( 'success' => false, 'error' => ! empty( $managed_write['error'] ) ? $managed_write['error'] : $seo_write['error'], 'restored_after_failure' => true );
		}
		Niwa_AI_Blog_Quality_Gate::invalidate_content_map( $post->ID );

		$rollback = array();
		foreach ( $taxonomy['created'] as $term ) {
			$rollback[] = array( 'type' => 'blog_term_delete_if_unused', 'term_id' => intval( $term['term_id'] ), 'taxonomy' => (string) $term['taxonomy'] );
		}
		$rollback[] = $restore;
		return array(
			'success'          => true,
			'summary'          => 'Updated the blog through the canonical quality pipeline.',
			'post_id'          => intval( $post->ID ),
			'status'           => $status,
			'edit_url'         => admin_url( 'post.php?post=' . intval( $post->ID ) . '&action=edit' ),
			'quality_report'   => $report,
			'executed_actions' => array( array( 'type' => 'update_blog_post', 'post_id' => intval( $post->ID ) ) ),
			'rollback_entries' => $rollback,
		);
	}

	private function validate_publish_blog_post_action_args( $args ) {
		$post_id = intval( isset( $args['post_id'] ) ? $args['post_id'] : 0 );
		if ( $post_id <= 0 ) {
			return array( 'success' => false, 'error' => 'Post ID is required for publish.' );
		}
		foreach ( array( 'expected_status', 'approved_source_hash', 'post_modified_gmt', 'quality_policy_version' ) as $key ) {
			if ( ! isset( $args[ $key ] ) || '' === trim( (string) $args[ $key ] ) ) {
				return array( 'success' => false, 'error' => 'Publish approval is incomplete: missing ' . $key . '. Preview the exact current post again.' );
			}
		}
		return array( 'success' => true );
	}

	private function preview_publish_blog_post( $args ) {
		$post_id = intval( isset( $args['post_id'] ) ? $args['post_id'] : 0 );
		$post    = get_post( $post_id );
		if ( ! $post || 'post' !== $post->post_type ) {
			return array( 'success' => false, 'error' => 'Blog post was not found.' );
		}
		if ( ! in_array( $post->post_status, array( 'draft', 'pending', 'private', 'publish' ), true ) ) {
			return array( 'success' => false, 'error' => 'Current post status is not publishable.' );
		}
		$proposal = $this->blog_post_current_rendered_proposal( $post );
		$prepared = $this->blog_pipeline_quality_gate()->prepare(
			$proposal,
			$this->blog_pipeline_quality_context(
				$post_id,
				array(
					'require_managed' => true,
					'managed'         => '1' === (string) get_post_meta( $post_id, Niwa_AI_Blog_Quality_Gate::META_MANAGED, true ),
					'policy_version'  => (string) get_post_meta( $post_id, Niwa_AI_Blog_Quality_Gate::META_POLICY_VERSION, true ),
				)
			)
		);
		if ( empty( $prepared['report']['publishable'] ) ) {
			return array( 'success' => false, 'error' => 'Current post state is not publishable.', 'quality_report' => $prepared['report'], 'corrections' => $prepared['report']['critical'] );
		}
		$hash = Niwa_AI_Blog_Quality_Gate::source_hash( $prepared['proposal'] );
		return array(
			'success'        => true,
			'quality_report' => $prepared['report'],
			'action'         => array(
				'type'             => 'publish_blog_post',
				'label'            => 'Publish approved governed blog revision',
				'summary'          => 'Publish approved revision of blog post #' . $post_id . '.',
				'rollback_plan'    => 'Restore the execution-time previous post status.',
				'rollback_support' => 'full',
				'targets'          => array( array( 'post_id' => $post_id, 'post_title' => (string) $post->post_title ) ),
				'args'             => array(
					'post_id'                => $post_id,
					'expected_status'         => (string) $post->post_status,
					'approved_source_hash'    => $hash,
					'post_modified_gmt'       => $this->blog_post_modified_gmt( $post ),
					'quality_policy_version'  => Niwa_AI_Blog_Quality_Gate::POLICY_VERSION,
				),
			),
		);
	}

	private function execute_publish_blog_post_action( $action ) {
		$args       = isset( $action['args'] ) && is_array( $action['args'] ) ? $action['args'] : array();
		$validation = $this->validate_publish_blog_post_action_args( $args );
		if ( empty( $validation['success'] ) ) {
			return $validation;
		}
		$post_id = intval( $args['post_id'] );
		$post    = get_post( $post_id );
		if ( ! $post || 'post' !== $post->post_type ) {
			return array( 'success' => false, 'error' => 'Blog post was not found at publish time.' );
		}
		if ( (string) $args['quality_policy_version'] !== Niwa_AI_Blog_Quality_Gate::POLICY_VERSION ) {
			return array( 'success' => false, 'error' => 'The approved quality policy version is stale. Preview and approve the post again.' );
		}
		if ( (string) $args['expected_status'] !== (string) $post->post_status ) {
			return array( 'success' => false, 'error' => 'The post status changed after approval; publish was refused.' );
		}
		if ( (string) $args['post_modified_gmt'] !== $this->blog_post_modified_gmt( $post ) ) {
			return array( 'success' => false, 'error' => 'The approved post revision changed; publish was refused.' );
		}

		$proposal = $this->blog_post_current_rendered_proposal( $post );
		$prepared = $this->blog_pipeline_quality_gate()->prepare(
			$proposal,
			$this->blog_pipeline_quality_context(
				$post_id,
				array(
					'require_managed' => true,
					'managed'         => '1' === (string) get_post_meta( $post_id, Niwa_AI_Blog_Quality_Gate::META_MANAGED, true ),
					'policy_version'  => (string) get_post_meta( $post_id, Niwa_AI_Blog_Quality_Gate::META_POLICY_VERSION, true ),
				)
			)
		);
		$current_hash = Niwa_AI_Blog_Quality_Gate::source_hash( $prepared['proposal'] );
		if ( ! hash_equals( (string) $args['approved_source_hash'], $current_hash ) ) {
			return array( 'success' => false, 'error' => 'The approved content hash no longer matches the current post; publish was refused.', 'quality_report' => $prepared['report'] );
		}
		if ( empty( $prepared['report']['publishable'] ) ) {
			return array( 'success' => false, 'error' => 'The current-state quality gate blocked publish.', 'quality_report' => $prepared['report'], 'corrections' => $prepared['report']['critical'] );
		}
		if ( 'publish' === $post->post_status ) {
			return array(
				'success'          => true,
				'summary'          => 'The approved revision is already published and still passes the current gate.',
				'post_id'          => $post_id,
				'title'            => (string) $post->post_title,
				'status'           => 'publish',
				'url'              => get_permalink( $post_id ),
				'quality_report'   => $prepared['report'],
				'executed_actions' => array( array( 'type' => 'publish_blog_post', 'post_id' => $post_id, 'noop' => true ) ),
				'rollback_entries' => array(),
			);
		}
		if ( ! in_array( $post->post_status, array( 'draft', 'pending', 'private' ), true ) ) {
			return array( 'success' => false, 'error' => 'Current post status is not publishable.' );
		}

		$restore = $this->build_blog_post_restore_entry( $post );
		$updated         = wp_update_post( array( 'ID' => $post_id, 'post_status' => 'publish' ), true );
		if ( is_wp_error( $updated ) || intval( $updated ) <= 0 ) {
			return array( 'success' => false, 'error' => is_wp_error( $updated ) ? $updated->get_error_message() : 'WordPress did not publish the post.' );
		}
		$refresh = $this->blog_post_refresh_publish_meta( $post_id, $prepared['proposal'], $prepared['report'], $current_hash );
		if ( empty( $refresh['success'] ) ) {
			$this->blog_post_restore_after_failed_update( $restore );
			return array( 'success' => false, 'error' => $refresh['error'], 'restored_after_failure' => true );
		}
		Niwa_AI_Blog_Quality_Gate::invalidate_content_map( $post_id );
		return array(
			'success'          => true,
			'summary'          => 'Published the approved current blog revision.',
			'post_id'          => $post_id,
			'title'            => get_the_title( $post_id ),
			'status'           => 'publish',
			'url'              => get_permalink( $post_id ),
			'quality_report'   => $prepared['report'],
			'executed_actions' => array( array( 'type' => 'publish_blog_post', 'post_id' => $post_id ) ),
			'rollback_entries' => array( $restore ),
		);
	}

	private function blog_post_proposal_for_update( $post, $args ) {
		$managed = '1' === (string) get_post_meta( $post->ID, Niwa_AI_Blog_Quality_Gate::META_MANAGED, true );
		$source  = $managed ? (string) get_post_meta( $post->ID, Niwa_AI_Blog_Quality_Gate::META_SOURCE_CONTENT, true ) : (string) $post->post_content;
		$format  = $managed ? (string) get_post_meta( $post->ID, Niwa_AI_Blog_Quality_Gate::META_SOURCE_FORMAT, true ) : 'html';
		if ( array_key_exists( 'content', $args ) ) {
			$source = (string) $args['content'];
			$requested_format = isset( $args['format'] ) ? $args['format'] : ( isset( $args['content_format'] ) ? $args['content_format'] : 'html' );
			$format = 'markdown' === sanitize_key( $requested_format ) ? 'markdown' : 'html';
		}
		$proposal = array(
			'title'             => array_key_exists( 'title', $args ) ? $args['title'] : $post->post_title,
			'content'           => $source,
			'format'            => $format,
			'excerpt'           => array_key_exists( 'excerpt', $args ) ? $args['excerpt'] : $post->post_excerpt,
			'meta_description'  => array_key_exists( 'meta_description', $args ) ? $args['meta_description'] : $this->get_seo_meta_description( $post->ID ),
			'slug'              => array_key_exists( 'slug', $args ) ? $args['slug'] : $post->post_name,
			'categories'        => array_key_exists( 'categories', $args ) ? (array) $args['categories'] : (array) wp_get_post_categories( $post->ID, array( 'fields' => 'names' ) ),
			'tags'              => array_key_exists( 'tags', $args ) ? (array) $args['tags'] : (array) wp_get_post_tags( $post->ID, array( 'fields' => 'names' ) ),
			'featured_image_id' => array_key_exists( 'featured_image_id', $args ) ? $args['featured_image_id'] : get_post_thumbnail_id( $post->ID ),
			'focus_keyword'     => array_key_exists( 'focus_keyword', $args ) ? $args['focus_keyword'] : get_post_meta( $post->ID, Niwa_AI_Blog_Quality_Gate::META_FOCUS_KEYWORD, true ),
			'search_intent'     => array_key_exists( 'search_intent', $args ) ? $args['search_intent'] : get_post_meta( $post->ID, Niwa_AI_Blog_Quality_Gate::META_SEARCH_INTENT, true ),
			'audience'          => array_key_exists( 'audience', $args ) ? $args['audience'] : get_post_meta( $post->ID, Niwa_AI_Blog_Quality_Gate::META_AUDIENCE, true ),
			'money_page_url'    => array_key_exists( 'money_page_url', $args ) ? $args['money_page_url'] : get_post_meta( $post->ID, Niwa_AI_Blog_Quality_Gate::META_MONEY_PAGE, true ),
			'language'          => array_key_exists( 'language', $args ) ? $args['language'] : ( get_post_meta( $post->ID, Niwa_AI_Blog_Quality_Gate::META_LANGUAGE, true ) ?: get_bloginfo( 'language' ) ),
			'evidence_urls'     => array_key_exists( 'evidence_urls', $args ) ? (array) $args['evidence_urls'] : (array) get_post_meta( $post->ID, Niwa_AI_Blog_Quality_Gate::META_EVIDENCE_URLS, true ),
			'author_id'         => array_key_exists( 'author_id', $args ) ? intval( $args['author_id'] ) : intval( $post->post_author ),
			'accent_color'      => isset( $args['accent_color'] ) ? $args['accent_color'] : ( isset( $this->blog_pipeline_settings()['accent_color'] ) ? $this->blog_pipeline_settings()['accent_color'] : '#3b82f6' ),
		);
		if ( array_key_exists( 'featured_image_id', $args ) ) {
			$proposal['_featured_image_explicit'] = true;
		}
		return $proposal;
	}

	private function blog_post_current_rendered_proposal( $post ) {
		return array(
			'title'             => (string) $post->post_title,
			'content'           => (string) $post->post_content,
			'source_content'    => (string) get_post_meta( $post->ID, Niwa_AI_Blog_Quality_Gate::META_SOURCE_CONTENT, true ),
			'rendered_content'  => (string) $post->post_content,
			'format'            => 'html',
			'excerpt'           => (string) $post->post_excerpt,
			'meta_description'  => $this->get_seo_meta_description( $post->ID ),
			'slug'              => (string) $post->post_name,
			'categories'        => (array) wp_get_post_categories( $post->ID, array( 'fields' => 'names' ) ),
			'tags'              => (array) wp_get_post_tags( $post->ID, array( 'fields' => 'names' ) ),
			'featured_image_id' => get_post_thumbnail_id( $post->ID ),
			'focus_keyword'     => get_post_meta( $post->ID, Niwa_AI_Blog_Quality_Gate::META_FOCUS_KEYWORD, true ),
			'search_intent'     => get_post_meta( $post->ID, Niwa_AI_Blog_Quality_Gate::META_SEARCH_INTENT, true ),
			'audience'          => get_post_meta( $post->ID, Niwa_AI_Blog_Quality_Gate::META_AUDIENCE, true ),
			'money_page_url'    => get_post_meta( $post->ID, Niwa_AI_Blog_Quality_Gate::META_MONEY_PAGE, true ),
			'language'          => get_post_meta( $post->ID, Niwa_AI_Blog_Quality_Gate::META_LANGUAGE, true ) ?: get_bloginfo( 'language' ),
			'evidence_urls'     => (array) get_post_meta( $post->ID, Niwa_AI_Blog_Quality_Gate::META_EVIDENCE_URLS, true ),
			'author_id'         => intval( $post->post_author ),
			'accent_color'      => isset( $this->blog_pipeline_settings()['accent_color'] ) ? $this->blog_pipeline_settings()['accent_color'] : '#3b82f6',
		);
	}

	private function blog_post_modified_gmt( $post ) {
		$value = isset( $post->post_modified_gmt ) ? (string) $post->post_modified_gmt : '';
		if ( '' === $value || '0000-00-00 00:00:00' === $value ) {
			$value = isset( $post->post_modified ) ? (string) $post->post_modified : '';
		}
		return $value;
	}

	private function blog_post_refresh_publish_meta( $post_id, $proposal, $report, $source_hash ) {
		$settings = $this->blog_pipeline_settings();
		$schemas  = ( ! array_key_exists( 'json_ld', $settings ) || ! empty( $settings['json_ld'] ) ) ? $this->blog_pipeline_generate_schemas_for_post( $post_id, $proposal ) : array();
		if ( ! empty( $schemas ) ) {
			if ( ! $this->blog_pipeline_store_meta_verified( $post_id, Niwa_AI_Blog_Asset_Renderer::META_SCHEMAS, $schemas ) ) {
				return array( 'success' => false, 'error' => 'Failed to refresh publish-time blog schema.' );
			}
		} elseif ( ! $this->blog_pipeline_delete_meta_verified( $post_id, Niwa_AI_Blog_Asset_Renderer::META_SCHEMAS ) ) {
			return array( 'success' => false, 'error' => 'Failed to clear disabled or competing publish-time schema.' );
		}
		$report['facts']['source_hash'] = $source_hash;
		foreach ( array(
			Niwa_AI_Blog_Quality_Gate::META_SOURCE_HASH    => $source_hash,
			Niwa_AI_Blog_Quality_Gate::META_QUALITY_REPORT => $report,
			Niwa_AI_Blog_Quality_Gate::META_SELECTED_LINKS => $report['link_report']['selected'],
		) as $key => $value ) {
			if ( ! $this->blog_pipeline_store_meta_verified( $post_id, $key, $value ) ) {
				return array( 'success' => false, 'error' => 'Failed to persist publish-time blog metadata: ' . $key . '.' );
			}
		}
		return array( 'success' => true );
	}

	private function get_seo_meta_description( $post_id ) {
		foreach ( array( '_yoast_wpseo_metadesc', 'rank_math_description', '_niwa_meta_description' ) as $key ) {
			$value = get_post_meta( $post_id, $key, true );
			if ( '' !== trim( (string) $value ) ) {
				return (string) $value;
			}
		}
		return '';
	}

	private function build_blog_post_restore_entry( $post ) {
		$meta = array();
		foreach ( $this->blog_post_rollback_meta_keys() as $key ) {
			$exists = function_exists( 'metadata_exists' ) ? metadata_exists( 'post', $post->ID, $key ) : array() !== get_post_meta( $post->ID, $key, false );
			$meta[ $key ] = array( 'exists' => $exists, 'values' => $exists ? get_post_meta( $post->ID, $key, false ) : array() );
		}
		return array(
			'type'                  => 'blog_post_restore',
			'post_id'               => intval( $post->ID ),
			'previous_title'        => (string) $post->post_title,
			'previous_content'      => (string) $post->post_content,
			'previous_excerpt'      => (string) $post->post_excerpt,
			'previous_status'       => (string) $post->post_status,
			'previous_slug'         => (string) $post->post_name,
			'previous_author'       => intval( isset( $post->post_author ) ? $post->post_author : 0 ),
			'previous_date'         => isset( $post->post_date ) ? (string) $post->post_date : '',
			'previous_date_gmt'     => isset( $post->post_date_gmt ) ? (string) $post->post_date_gmt : '',
			'previous_modified'     => isset( $post->post_modified ) ? (string) $post->post_modified : '',
			'previous_modified_gmt' => isset( $post->post_modified_gmt ) ? (string) $post->post_modified_gmt : '',
			'previous_category_ids' => (array) wp_get_post_categories( $post->ID ),
			'previous_tag_ids'      => (array) wp_get_post_tags( $post->ID, array( 'fields' => 'ids' ) ),
			'previous_thumbnail_id' => intval( get_post_thumbnail_id( $post->ID ) ),
			'previous_meta'         => $meta,
		);
	}

	private function blog_post_rollback_meta_keys() {
		return array(
			Niwa_AI_Blog_Asset_Renderer::META_CSS,
			Niwa_AI_Blog_Asset_Renderer::META_JS,
			Niwa_AI_Blog_Asset_Renderer::META_SCHEMAS,
			Niwa_AI_Blog_Quality_Gate::META_MANAGED,
			Niwa_AI_Blog_Quality_Gate::META_POLICY_VERSION,
			Niwa_AI_Blog_Quality_Gate::META_SOURCE_HASH,
			Niwa_AI_Blog_Quality_Gate::META_SOURCE_CONTENT,
			Niwa_AI_Blog_Quality_Gate::META_SOURCE_FORMAT,
			Niwa_AI_Blog_Quality_Gate::META_QUALITY_REPORT,
			Niwa_AI_Blog_Quality_Gate::META_SELECTED_LINKS,
			Niwa_AI_Blog_Quality_Gate::META_FOCUS_KEYWORD,
			Niwa_AI_Blog_Quality_Gate::META_SEARCH_INTENT,
			Niwa_AI_Blog_Quality_Gate::META_AUDIENCE,
			Niwa_AI_Blog_Quality_Gate::META_MONEY_PAGE,
			Niwa_AI_Blog_Quality_Gate::META_LANGUAGE,
			Niwa_AI_Blog_Quality_Gate::META_EVIDENCE_URLS,
			'_yoast_wpseo_metadesc',
			'_yoast_wpseo_focuskw',
			'_yoast_wpseo_opengraph-title',
			'_yoast_wpseo_opengraph-description',
			'_yoast_wpseo_opengraph-image-id',
			'_yoast_wpseo_twitter-title',
			'_yoast_wpseo_twitter-description',
			'rank_math_description',
			'rank_math_focus_keyword',
			'rank_math_facebook_title',
			'rank_math_facebook_description',
			'rank_math_facebook_image_id',
			'rank_math_twitter_title',
			'rank_math_twitter_description',
			'_niwa_meta_description',
		);
	}

	private function blog_post_restore_after_failed_update( $restore ) {
		if ( method_exists( $this, 'restore_entry' ) ) {
			return $this->restore_entry( $restore );
		}
		return array( 'success' => false, 'error' => 'Rollback service was unavailable.' );
	}
}
