<?php
/**
 * Canonical Niwa blog write pipeline.
 *
 * @package Niwa_AI
 */

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

trait Niwa_AI_Tools_Blog_Pipeline {

	private function validate_create_niwa_blog_action_args( $args ) {
		if ( '' === trim( (string) ( isset( $args['title'] ) ? $args['title'] : '' ) ) ) {
			return array( 'success' => false, 'error' => 'Blog title is required.' );
		}
		if ( '' === trim( (string) ( isset( $args['content'] ) ? $args['content'] : '' ) ) ) {
			return array( 'success' => false, 'error' => 'Blog content is required.' );
		}
		if ( isset( $args['status'] ) && 'draft' !== sanitize_key( (string) $args['status'] ) ) {
			return array( 'success' => false, 'error' => 'Blog creation is always draft-first. Use update_blog_post for pending/private and publish_blog_post after approval.' );
		}
		return array( 'success' => true );
	}

	private function preview_create_niwa_blog( $args ) {
		return $this->blog_pipeline_preview_canonical_create( $args, 'markdown', 'create_niwa_blog' );
	}

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

	/**
	 * Shared preview for create_niwa_blog and the create_blog_post compatibility adapter.
	 */
	private function blog_pipeline_preview_canonical_create( $args, $format, $tool_name ) {
		$validation = $this->validate_create_niwa_blog_action_args( $args );
		if ( empty( $validation['success'] ) ) {
			return $validation;
		}

		$proposal = $this->blog_pipeline_proposal_from_args( $args, $format );
		$prepared = $this->blog_pipeline_quality_gate()->prepare( $proposal, $this->blog_pipeline_quality_context() );
		$report   = $prepared['report'];
		if ( empty( $report['pass'] ) ) {
			return array(
				'success'        => false,
				'error'          => 'Blog quality gate blocked draft creation.',
				'quality_report' => $report,
				'corrections'    => $report['critical'],
			);
		}

		$normalized = $prepared['proposal'];
		$normalized['allow_new_terms'] = ! empty( $args['allow_new_terms'] ) || ! empty( $this->blog_pipeline_settings()['allow_new_taxonomy_terms'] );
		$taxonomy  = $this->blog_pipeline_preview_taxonomy( $normalized, $normalized['allow_new_terms'] );
		$report['taxonomy_report'] = $taxonomy['report'];

		return array(
			'success'        => true,
			'quality_report' => $report,
			'action'         => array(
				'type'             => $tool_name,
				'label'            => 'Create governed Niwa blog draft',
				'summary'          => 'Create a canonical quality-gated draft: "' . $normalized['title'] . '".',
				'rollback_plan'    => 'Trash the created draft and remove newly created unused taxonomy terms.',
				'rollback_support' => 'full',
				'targets'          => array(),
				'args'             => array_merge( $normalized, array( 'format' => $format ) ),
			),
		);
	}

	/**
	 * Single creation boundary used by both public create tools.
	 */
	private function blog_pipeline_execute_canonical_create( $args, $format, $tool_name ) {
		$validation = $this->validate_create_niwa_blog_action_args( $args );
		if ( empty( $validation['success'] ) ) {
			return $validation;
		}

		$proposal = $this->blog_pipeline_proposal_from_args( $args, $format );
		$prepared = $this->blog_pipeline_quality_gate()->prepare( $proposal, $this->blog_pipeline_quality_context() );
		$proposal = $prepared['proposal'];
		$report   = $prepared['report'];
		if ( empty( $report['pass'] ) ) {
			return array(
				'success'        => false,
				'error'          => 'Blog quality gate blocked draft creation. No fallback post was created.',
				'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;
		}

		$post_data = array(
			'post_type'    => 'post',
			'post_status'  => 'draft',
			'post_title'   => $proposal['title'],
			'post_content' => $rendered['content'],
			'post_excerpt' => $proposal['excerpt'],
			'post_name'    => $proposal['slug'],
		);
		$author_id = intval( $proposal['author_id'] );
		if ( $author_id > 0 ) {
			$post_data['post_author'] = $author_id;
		}

		$post_id = wp_insert_post( $post_data, true );
		if ( is_wp_error( $post_id ) || intval( $post_id ) <= 0 ) {
			$this->blog_pipeline_cleanup_created_terms( $taxonomy['created'] );
			return array( 'success' => false, 'error' => is_wp_error( $post_id ) ? $post_id->get_error_message() : 'WordPress did not create the blog draft.' );
		}
		$post_id = intval( $post_id );

		$write = $this->blog_pipeline_apply_post_relations( $post_id, $taxonomy, $proposal );
		if ( empty( $write['success'] ) ) {
			$this->blog_pipeline_abort_new_post( $post_id, $taxonomy['created'] );
			return $write;
		}

		$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_pipeline_abort_new_post( $post_id, $taxonomy['created'] );
			return array( 'success' => false, 'error' => ! empty( $managed_write['error'] ) ? $managed_write['error'] : $seo_write['error'] );
		}
		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[] = array( 'type' => 'blog_post_delete', 'post_id' => $post_id );

		return array(
			'success'          => true,
			'summary'          => 'Created a quality-gated WordPress blog draft.',
			'post_id'          => $post_id,
			'title'            => get_the_title( $post_id ),
			'status'           => 'draft',
			'edit_url'         => admin_url( 'post.php?post=' . $post_id . '&action=edit' ),
			'preview_url'      => get_preview_post_link( $post_id ),
			'selected_links'   => $report['link_report']['selected'],
			'taxonomy'         => $report['taxonomy_report'],
			'quality_report'   => $report,
			'executed_actions' => array( array( 'type' => $tool_name, 'post_id' => $post_id, 'status' => 'draft' ) ),
			'rollback_entries' => $rollback,
		);
	}

	private function blog_pipeline_proposal_from_args( $args, $format ) {
		$settings   = $this->blog_pipeline_settings();
		$categories = isset( $args['categories'] ) ? (array) $args['categories'] : array();
		if ( empty( $categories ) && ! empty( $args['category'] ) ) {
			$categories[] = $args['category'];
		}
		if ( empty( $categories ) && ! empty( $settings['default_category'] ) ) {
			$categories[] = $settings['default_category'];
		}
		$money_page = isset( $args['money_page_url'] ) ? (string) $args['money_page_url'] : '';
		if ( '' === trim( $money_page ) && ! empty( $args['destination_money_page'] ) ) {
			$money_page = (string) $args['destination_money_page'];
		}

		return array(
			'title'             => isset( $args['title'] ) ? $args['title'] : '',
			'content'           => isset( $args['content'] ) ? $args['content'] : '',
			'format'            => $format,
			'excerpt'           => isset( $args['excerpt'] ) ? $args['excerpt'] : '',
			'meta_description'  => isset( $args['meta_description'] ) ? $args['meta_description'] : ( isset( $args['excerpt'] ) ? $args['excerpt'] : '' ),
			'slug'              => isset( $args['slug'] ) ? $args['slug'] : '',
			'categories'        => $categories,
			'tags'              => isset( $args['tags'] ) ? (array) $args['tags'] : array(),
			'featured_image_id' => isset( $args['featured_image_id'] ) ? $args['featured_image_id'] : 0,
			'focus_keyword'     => isset( $args['focus_keyword'] ) ? $args['focus_keyword'] : '',
			'search_intent'     => isset( $args['search_intent'] ) ? $args['search_intent'] : '',
			'audience'          => isset( $args['audience'] ) ? $args['audience'] : '',
			'money_page_url'    => $money_page,
			'language'          => isset( $args['language'] ) ? $args['language'] : ( isset( $settings['default_language'] ) ? $settings['default_language'] : get_bloginfo( 'language' ) ),
			'evidence_urls'     => isset( $args['evidence_urls'] ) ? (array) $args['evidence_urls'] : array(),
			'author_id'         => $this->blog_pipeline_author_id( isset( $args['author_id'] ) ? $args['author_id'] : 0 ),
			'accent_color'      => isset( $args['accent_color'] ) ? $args['accent_color'] : ( isset( $settings['accent_color'] ) ? $settings['accent_color'] : '#3b82f6' ),
		);
	}

	private function blog_pipeline_quality_gate() {
		return new Niwa_AI_Blog_Quality_Gate( new Niwa_AI_Humanizer() );
	}

	private function blog_pipeline_quality_context( $current_post_id = 0, $extra = array() ) {
		$map = method_exists( $this, 'get_site_content_map_for_linking' ) ? $this->get_site_content_map_for_linking() : array();
		return array_merge(
			array(
				'content_map'    => $map,
				'priority_paths' => $this->blog_pipeline_priority_paths(),
				'current_post_id' => intval( $current_post_id ),
			),
			is_array( $extra ) ? $extra : array()
		);
	}

	private function blog_pipeline_settings() {
		$settings = get_option( 'niwa_bloging_settings', array() );
		return is_array( $settings ) ? $settings : array();
	}

	private function blog_pipeline_priority_paths() {
		$settings = $this->blog_pipeline_settings();
		$items    = isset( $settings['money_posts'] ) ? $settings['money_posts'] : array();
		if ( is_string( $items ) ) {
			$decoded = json_decode( $items, true );
			$items   = is_array( $decoded ) ? $decoded : array();
		}
		$paths = array();
		foreach ( is_array( $items ) ? $items : array() as $item ) {
			$path = is_array( $item ) && isset( $item['path'] ) ? (string) $item['path'] : ( is_string( $item ) ? $item : '' );
			if ( '' !== trim( $path ) ) {
				$paths[] = $path;
			}
		}
		return array_values( array_unique( $paths ) );
	}

	private function blog_pipeline_preview_taxonomy( $proposal, $allow_new ) {
		$report = array( 'reused' => array(), 'created' => array(), 'skipped' => array() );
		foreach ( array( 'category' => $proposal['categories'], 'post_tag' => $proposal['tags'] ) as $taxonomy => $names ) {
			foreach ( $names as $name ) {
				$term = get_term_by( 'name', $name, $taxonomy );
				if ( ! $term ) {
					$term = get_term_by( 'slug', sanitize_title( $name ), $taxonomy );
				}
				if ( $term ) {
					$report['reused'][] = array( 'taxonomy' => $taxonomy, 'term_id' => intval( $term->term_id ), 'name' => (string) $term->name );
				} elseif ( $allow_new ) {
					$report['created'][] = array( 'taxonomy' => $taxonomy, 'name' => $name, 'planned' => true );
				} else {
					$report['skipped'][] = array( 'taxonomy' => $taxonomy, 'name' => $name, 'reason' => 'New taxonomy terms require explicit allow_new_terms opt-in.' );
				}
			}
		}
		$has_category = array_filter( array_merge( $report['reused'], $report['created'] ), static function ( $item ) { return isset( $item['taxonomy'] ) && 'category' === $item['taxonomy']; } );
		if ( empty( $has_category ) ) {
			$default_id = intval( get_option( 'default_category', 0 ) );
			$default    = $default_id > 0 ? get_term( $default_id, 'category' ) : null;
			if ( $default && ! is_wp_error( $default ) ) {
				$report['reused'][] = array( 'taxonomy' => 'category', 'term_id' => $default_id, 'name' => (string) $default->name, 'reason' => 'WordPress default category.' );
			}
		}
		return array( 'report' => $report );
	}

	private function blog_pipeline_resolve_taxonomy( $proposal, $allow_new ) {
		$result = array(
			'success'      => true,
			'category_ids' => array(),
			'tag_ids'      => array(),
			'created'      => array(),
			'report'       => array( 'reused' => array(), 'created' => array(), 'skipped' => array() ),
		);
		foreach ( array( 'category' => $proposal['categories'], 'post_tag' => $proposal['tags'] ) as $taxonomy => $names ) {
			foreach ( $names as $name ) {
				$term = get_term_by( 'name', $name, $taxonomy );
				if ( ! $term ) {
					$term = get_term_by( 'slug', sanitize_title( $name ), $taxonomy );
				}
				if ( $term ) {
					$term_id = intval( $term->term_id );
					$result['report']['reused'][] = array( 'taxonomy' => $taxonomy, 'term_id' => $term_id, 'name' => (string) $term->name );
				} elseif ( $allow_new ) {
					$inserted = wp_insert_term( $name, $taxonomy );
					if ( is_wp_error( $inserted ) || empty( $inserted['term_id'] ) ) {
						$result['success'] = false;
						$result['error']   = is_wp_error( $inserted ) ? $inserted->get_error_message() : 'Taxonomy term creation failed.';
						return $result;
					}
					$term_id = intval( $inserted['term_id'] );
					$created = array( 'taxonomy' => $taxonomy, 'term_id' => $term_id, 'name' => $name );
					$result['created'][]           = $created;
					$result['report']['created'][] = $created;
				} else {
					$result['report']['skipped'][] = array( 'taxonomy' => $taxonomy, 'name' => $name, 'reason' => 'New taxonomy terms require explicit allow_new_terms opt-in.' );
					continue;
				}
				if ( 'category' === $taxonomy ) {
					$result['category_ids'][] = $term_id;
				} else {
					$result['tag_ids'][] = $term_id;
				}
			}
		}
		if ( empty( $result['category_ids'] ) ) {
			$default_id = intval( get_option( 'default_category', 0 ) );
			$default    = $default_id > 0 ? get_term( $default_id, 'category' ) : null;
			if ( $default && ! is_wp_error( $default ) ) {
				$result['category_ids'][]       = $default_id;
				$result['report']['reused'][] = array( 'taxonomy' => 'category', 'term_id' => $default_id, 'name' => (string) $default->name, 'reason' => 'WordPress default category.' );
			}
		}
		return $result;
	}

	private function blog_pipeline_apply_post_relations( $post_id, $taxonomy, $proposal ) {
		$result = wp_set_post_categories( $post_id, $taxonomy['category_ids'], false );
		if ( is_wp_error( $result ) ) {
			return array( 'success' => false, 'error' => $result->get_error_message() );
		}
		$result = wp_set_post_terms( $post_id, $taxonomy['tag_ids'], 'post_tag', false );
		if ( is_wp_error( $result ) ) {
			return array( 'success' => false, 'error' => $result->get_error_message() );
		}
		if ( $proposal['featured_image_id'] > 0 && intval( get_post_thumbnail_id( $post_id ) ) !== intval( $proposal['featured_image_id'] ) && ! set_post_thumbnail( $post_id, $proposal['featured_image_id'] ) ) {
			return array( 'success' => false, 'error' => 'WordPress could not set the validated featured image.' );
		}
		return array( 'success' => true );
	}

	private function blog_pipeline_abort_new_post( $post_id, $created_terms ) {
		wp_trash_post( intval( $post_id ) );
		$this->blog_pipeline_cleanup_created_terms( $created_terms );
	}

	private function blog_pipeline_cleanup_created_terms( $created_terms ) {
		foreach ( array_reverse( is_array( $created_terms ) ? $created_terms : array() ) as $term ) {
			$term_id  = intval( isset( $term['term_id'] ) ? $term['term_id'] : 0 );
			$taxonomy = isset( $term['taxonomy'] ) ? (string) $term['taxonomy'] : '';
			$current  = $term_id > 0 && $taxonomy ? get_term( $term_id, $taxonomy ) : null;
			if ( $current && ! is_wp_error( $current ) && intval( $current->count ) <= 0 ) {
				wp_delete_term( $term_id, $taxonomy );
			}
		}
	}

	private function blog_pipeline_render_content( $proposal ) {
		if ( 'html' === $proposal['format'] ) {
			$body = $this->blog_pipeline_normalize_html_headings( wp_kses_post( $proposal['content'] ), $headings );
		} else {
			$headings = $this->blog_pipeline_extract_markdown_headings( $proposal['content'] );
			$body     = $this->blog_pipeline_markdown_to_html( $proposal['content'], $headings );
		}
		if ( empty( $headings ) ) {
			return array( 'success' => false, 'error' => 'No valid H2/H3 headings were available for rendering.' );
		}

		$settings = $this->blog_pipeline_settings();
		$parts    = array();
		if ( ! array_key_exists( 'progress_bar', $settings ) || ! empty( $settings['progress_bar'] ) ) {
			$parts[] = '<div class="db-progress" aria-hidden="true"><span class="db-progress-bar"></span></div>';
		}
		if ( ! array_key_exists( 'auto_toc', $settings ) || ! empty( $settings['auto_toc'] ) ) {
			$parts[] = $this->blog_pipeline_generate_toc( $headings );
		}
		$parts[] = $body;
		$content = '<article class="niwa-blog-article" data-niwa-blog-policy="' . esc_attr( Niwa_AI_Blog_Quality_Gate::POLICY_VERSION ) . '">' . implode( '', array_filter( $parts ) ) . '</article>';

		return array(
			'success'  => true,
			'content'  => wp_kses_post( $content ),
			'headings' => $headings,
			'css'      => $this->blog_pipeline_generate_css( $proposal['accent_color'] ),
			'js'       => ( ! array_key_exists( 'progress_bar', $settings ) || ! empty( $settings['progress_bar'] ) ) ? $this->blog_pipeline_generate_progress_bar_js_body() : '',
		);
	}

	private function blog_pipeline_extract_markdown_headings( $markdown ) {
		$headings = array();
		$seen     = array();
		if ( preg_match_all( '/^\s*(#{2,3})\s+(.+?)\s*#*\s*$/mu', $markdown, $matches, PREG_SET_ORDER ) ) {
			foreach ( $matches as $match ) {
				$text       = trim( wp_strip_all_tags( preg_replace( '/[*_`]/u', '', $match[2] ) ) );
				$headings[] = array( 'level' => strlen( $match[1] ), 'text' => $text, 'id' => $this->blog_pipeline_unique_heading_id( $text, $seen ) );
			}
		}
		return $headings;
	}

	private function blog_pipeline_normalize_html_headings( $html, &$headings ) {
		$headings = array();
		$seen     = array();
		$html     = preg_replace( '/<h1\b[^>]*>(.*?)<\/h1>/is', '<p>$1</p>', $html );
		return preg_replace_callback(
			'/<h([2-3])\b[^>]*>(.*?)<\/h\1>/is',
			function ( $match ) use ( &$headings, &$seen ) {
				$level      = intval( $match[1] );
				$text       = trim( wp_strip_all_tags( $match[2] ) );
				$id         = $this->blog_pipeline_unique_heading_id( $text, $seen );
				$headings[] = array( 'level' => $level, 'text' => $text, 'id' => $id );
				return '<h' . $level . ' id="' . esc_attr( $id ) . '" class="db-h' . $level . '">' . wp_kses_post( $match[2] ) . '</h' . $level . '>';
			},
			$html
		);
	}

	private function blog_pipeline_markdown_to_html( $markdown, $headings ) {
		$lines        = preg_split( '/\R/u', $markdown );
		$html         = array();
		$heading_index = 0;
		$list_type    = '';
		foreach ( is_array( $lines ) ? $lines : array() as $line ) {
			$trim = trim( $line );
			if ( '' === $trim ) {
				if ( $list_type ) {
					$html[]    = '</' . $list_type . '>';
					$list_type = '';
				}
				continue;
			}
			if ( preg_match( '/^(#{2,3})\s+(.+?)\s*#*$/u', $trim, $match ) ) {
				if ( $list_type ) {
					$html[]    = '</' . $list_type . '>';
					$list_type = '';
				}
				$level   = strlen( $match[1] );
				$record  = isset( $headings[ $heading_index ] ) ? $headings[ $heading_index ] : array( 'id' => sanitize_title( $match[2] ) );
				$html[]  = '<h' . $level . ' id="' . esc_attr( $record['id'] ) . '" class="db-h' . $level . '">' . $this->blog_pipeline_inline_markdown( $match[2] ) . '</h' . $level . '>';
				$heading_index++;
				continue;
			}
			if ( preg_match( '/^[-*]\s+(.+)$/u', $trim, $match ) || preg_match( '/^\d+[.)]\s+(.+)$/u', $trim, $match ) ) {
				$type = preg_match( '/^\d+/u', $trim ) ? 'ol' : 'ul';
				if ( $list_type && $list_type !== $type ) {
					$html[]    = '</' . $list_type . '>';
					$list_type = '';
				}
				if ( ! $list_type ) {
					$list_type = $type;
					$html[]    = '<' . $type . ' class="db-list">';
				}
				$html[] = '<li>' . $this->blog_pipeline_inline_markdown( $match[1] ) . '</li>';
				continue;
			}
			if ( $list_type ) {
				$html[]    = '</' . $list_type . '>';
				$list_type = '';
			}
			if ( preg_match( '/^>\s*(.+)$/u', $trim, $match ) ) {
				$html[] = '<blockquote class="db-quote"><p>' . $this->blog_pipeline_inline_markdown( $match[1] ) . '</p></blockquote>';
				continue;
			}
			if ( preg_match( '/^!\[([^\]]+)\]\(([^)\s]+)(?:\s+["\'][^"\']*["\'])?\)$/u', $trim, $match ) ) {
				$html[] = '<figure class="db-figure"><img src="' . esc_url( $match[2] ) . '" alt="' . esc_attr( $match[1] ) . '" loading="lazy"></figure>';
				continue;
			}
			$html[] = '<p>' . $this->blog_pipeline_inline_markdown( $trim ) . '</p>';
		}
		if ( $list_type ) {
			$html[] = '</' . $list_type . '>';
		}
		return implode( '', $html );
	}

	private function blog_pipeline_inline_markdown( $text ) {
		$text = esc_html( trim( (string) $text ) );
		$text = preg_replace_callback(
			'/\[([^\]]+)\]\(([^)\s]+)\)/u',
			static function ( $match ) {
				$url = html_entity_decode( $match[2], ENT_QUOTES, 'UTF-8' );
				return '<a href="' . esc_url( $url ) . '">' . $match[1] . '</a>';
			},
			$text
		);
		$text = preg_replace( '/\*\*([^*]+)\*\*/u', '<strong>$1</strong>', $text );
		$text = preg_replace( '/(?<!\*)\*([^*]+)\*(?!\*)/u', '<em>$1</em>', $text );
		$text = preg_replace( '/`([^`]+)`/u', '<code>$1</code>', $text );
		return $text;
	}

	private function blog_pipeline_unique_heading_id( $text, &$seen ) {
		$base = sanitize_title( $text );
		if ( '' === $base ) {
			$base = 'section';
		}
		$count         = isset( $seen[ $base ] ) ? $seen[ $base ] + 1 : 1;
		$seen[ $base ] = $count;
		return 1 === $count ? $base : $base . '-' . $count;
	}

	private function blog_pipeline_generate_toc( $headings ) {
		$items = array();
		foreach ( $headings as $heading ) {
			$class   = 3 === intval( $heading['level'] ) ? ' class="db-toc-sub"' : '';
			$items[] = '<li' . $class . '><a href="#' . esc_attr( $heading['id'] ) . '">' . esc_html( $heading['text'] ) . '</a></li>';
		}
		return empty( $items ) ? '' : '<nav class="db-toc" aria-label="' . esc_attr( __( 'Table of contents', 'niwa-ai' ) ) . '"><p class="db-toc-title">' . esc_html__( 'Contents', 'niwa-ai' ) . '</p><ol>' . implode( '', $items ) . '</ol></nav>';
	}

	private function blog_pipeline_persist_managed_state( $post_id, $proposal, $report, $rendered ) {
		$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();
		$values = array(
			Niwa_AI_Blog_Asset_Renderer::META_CSS             => $rendered['css'],
			Niwa_AI_Blog_Quality_Gate::META_MANAGED          => '1',
			Niwa_AI_Blog_Quality_Gate::META_POLICY_VERSION   => Niwa_AI_Blog_Quality_Gate::POLICY_VERSION,
			Niwa_AI_Blog_Quality_Gate::META_SOURCE_CONTENT   => isset( $proposal['source_content'] ) ? $proposal['source_content'] : $proposal['content'],
			Niwa_AI_Blog_Quality_Gate::META_SOURCE_FORMAT    => $proposal['format'],
			Niwa_AI_Blog_Quality_Gate::META_SOURCE_HASH      => Niwa_AI_Blog_Quality_Gate::source_hash( $proposal ),
			Niwa_AI_Blog_Quality_Gate::META_QUALITY_REPORT   => $report,
			Niwa_AI_Blog_Quality_Gate::META_SELECTED_LINKS   => $report['link_report']['selected'],
			Niwa_AI_Blog_Quality_Gate::META_FOCUS_KEYWORD    => $proposal['focus_keyword'],
			Niwa_AI_Blog_Quality_Gate::META_SEARCH_INTENT    => $proposal['search_intent'],
			Niwa_AI_Blog_Quality_Gate::META_AUDIENCE         => $proposal['audience'],
			Niwa_AI_Blog_Quality_Gate::META_MONEY_PAGE       => $proposal['money_page_url'],
			Niwa_AI_Blog_Quality_Gate::META_LANGUAGE         => $proposal['language'],
			Niwa_AI_Blog_Quality_Gate::META_EVIDENCE_URLS    => $proposal['evidence_urls'],
		);
		if ( '' !== $rendered['js'] ) {
			$values[ Niwa_AI_Blog_Asset_Renderer::META_JS ] = $rendered['js'];
		} elseif ( ! $this->blog_pipeline_delete_meta_verified( $post_id, Niwa_AI_Blog_Asset_Renderer::META_JS ) ) {
			return array( 'success' => false, 'error' => 'Failed to clear disabled blog progress JavaScript.' );
		}
		if ( ! empty( $schemas ) ) {
			$values[ Niwa_AI_Blog_Asset_Renderer::META_SCHEMAS ] = $schemas;
		} 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 blog schema.' );
		}
		foreach ( $values as $key => $value ) {
			if ( ! $this->blog_pipeline_store_meta_verified( $post_id, $key, $value ) ) {
				return array( 'success' => false, 'error' => 'Failed to persist canonical blog metadata: ' . $key . '.' );
			}
		}
		return array( 'success' => true );
	}

	private function blog_pipeline_store_meta_verified( $post_id, $key, $value ) {
		update_post_meta( $post_id, $key, $value );
		return serialize( get_post_meta( $post_id, $key, true ) ) === serialize( $value );
	}

	private function blog_pipeline_delete_meta_verified( $post_id, $key ) {
		delete_post_meta( $post_id, $key );
		return function_exists( 'metadata_exists' ) ? ! metadata_exists( 'post', $post_id, $key ) : array() === get_post_meta( $post_id, $key, false );
	}

	private function blog_pipeline_generate_schemas_for_post( $post_id, $proposal ) {
		if ( $this->blog_pipeline_schema_owned_by_seo_plugin() ) {
			return array();
		}
		$post = get_post( $post_id );
		if ( ! $post ) {
			return array();
		}
		$author = get_userdata( intval( $post->post_author ) );
		if ( ! $author ) {
			return array();
		}
		$permalink = get_permalink( $post_id );
		$schema    = array(
			'@context'      => 'https://schema.org',
			'@type'         => 'BlogPosting',
			'headline'      => (string) $post->post_title,
			'description'   => (string) $proposal['meta_description'],
			'url'           => $permalink,
			'mainEntityOfPage' => array( '@type' => 'WebPage', '@id' => $permalink ),
			'datePublished' => get_post_time( DATE_W3C, $post->post_date_gmt && '0000-00-00 00:00:00' !== $post->post_date_gmt, $post, false ),
			'dateModified'  => get_post_modified_time( DATE_W3C, $post->post_modified_gmt && '0000-00-00 00:00:00' !== $post->post_modified_gmt, $post, false ),
			'author'        => array( '@type' => 'Person', 'name' => $author->display_name, 'url' => get_author_posts_url( $author->ID ) ),
			'publisher'     => array( '@type' => 'Organization', 'name' => get_bloginfo( 'name' ), 'url' => home_url( '/' ) ),
			'inLanguage'    => (string) $proposal['language'],
		);
		if ( $proposal['featured_image_id'] > 0 ) {
			$image = wp_get_attachment_image_url( $proposal['featured_image_id'], 'full' );
			if ( $image ) {
				$schema['image'] = $image;
			}
		}
		$breadcrumb = array(
			'@context'        => 'https://schema.org',
			'@type'           => 'BreadcrumbList',
			'itemListElement' => array(
				array( '@type' => 'ListItem', 'position' => 1, 'name' => get_bloginfo( 'name' ), 'item' => home_url( '/' ) ),
				array( '@type' => 'ListItem', 'position' => 2, 'name' => (string) $post->post_title, 'item' => $permalink ),
			),
		);
		return array_values( array_filter( array( wp_json_encode( $schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ), wp_json_encode( $breadcrumb, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) ) ) );
	}

	private function blog_pipeline_schema_owned_by_seo_plugin() {
		return defined( 'WPSEO_VERSION' ) || defined( 'RANK_MATH_VERSION' ) || function_exists( 'rank_math' );
	}

	private function blog_pipeline_set_seo_meta( $post_id, $description, $focus_keyword = '' ) {
		$description = trim( (string) $description );
		$focus_keyword = trim( (string) $focus_keyword );
		if ( defined( 'RANK_MATH_VERSION' ) ) {
			return $this->blog_pipeline_store_meta_verified( $post_id, 'rank_math_description', $description ) && $this->blog_pipeline_store_meta_verified( $post_id, 'rank_math_focus_keyword', $focus_keyword )
				? array( 'success' => true ) : array( 'success' => false, 'error' => 'Failed to persist Rank Math blog metadata.' );
		}
		if ( defined( 'WPSEO_VERSION' ) ) {
			return $this->blog_pipeline_store_meta_verified( $post_id, '_yoast_wpseo_metadesc', $description ) && $this->blog_pipeline_store_meta_verified( $post_id, '_yoast_wpseo_focuskw', $focus_keyword )
				? array( 'success' => true ) : array( 'success' => false, 'error' => 'Failed to persist Yoast blog metadata.' );
		}
		return $this->blog_pipeline_store_meta_verified( $post_id, '_niwa_meta_description', $description ) && $this->blog_pipeline_store_meta_verified( $post_id, Niwa_AI_Blog_Quality_Gate::META_FOCUS_KEYWORD, $focus_keyword )
			? array( 'success' => true ) : array( 'success' => false, 'error' => 'Failed to persist Niwa fallback blog metadata.' );
	}

	private function blog_pipeline_author_id( $requested = 0 ) {
		$requested = intval( $requested );
		if ( $requested > 0 && get_userdata( $requested ) ) {
			return $requested;
		}
		$settings = $this->blog_pipeline_settings();
		$default  = intval( isset( $settings['default_author'] ) ? $settings['default_author'] : 0 );
		if ( $default > 0 && get_userdata( $default ) ) {
			return $default;
		}
		$current = intval( get_current_user_id() );
		if ( $current > 0 && get_userdata( $current ) ) {
			return $current;
		}
		if ( function_exists( 'get_users' ) ) {
			$users = get_users( array( 'role__in' => array( 'administrator', 'editor' ), 'number' => 1, 'fields' => 'ids' ) );
			if ( ! empty( $users[0] ) && get_userdata( intval( $users[0] ) ) ) {
				return intval( $users[0] );
			}
		}
		return 0;
	}

	/** Compatibility entry used by the explicit legacy migrator. */
	private function blog_pipeline_generate_css( $accent ) {
		$accent = function_exists( 'sanitize_hex_color' ) ? sanitize_hex_color( $accent ) : ( preg_match( '/^#[0-9a-f]{6}$/i', $accent ) ? $accent : '' );
		$accent = $accent ? $accent : '#3b82f6';
		return '.niwa-blog-article{--niwa-blog-accent:' . $accent . ';max-width:820px;margin-inline:auto;overflow-wrap:anywhere}.niwa-blog-article *{box-sizing:border-box}.niwa-blog-article .db-progress{position:fixed;inset:0 0 auto;z-index:9999;height:3px;pointer-events:none}.niwa-blog-article .db-progress-bar{display:block;width:0;height:100%;background:var(--niwa-blog-accent)}.niwa-blog-article .db-toc{margin:1.75rem 0;padding:1.25rem;border:1px solid color-mix(in srgb,var(--niwa-blog-accent) 28%,#d1d5db);border-radius:12px}.niwa-blog-article .db-toc-title{margin:0 0 .75rem;font-weight:700}.niwa-blog-article .db-toc ol{margin:0;padding-left:1.25rem}.niwa-blog-article .db-toc-sub{margin-left:1rem}.niwa-blog-article .db-h2{margin:2.5rem 0 1rem;font-size:clamp(1.55rem,3vw,2rem);line-height:1.25}.niwa-blog-article .db-h3{margin:1.75rem 0 .75rem;font-size:clamp(1.2rem,2.4vw,1.5rem);line-height:1.3}.niwa-blog-article p,.niwa-blog-article li{line-height:1.75}.niwa-blog-article a{color:var(--niwa-blog-accent);text-underline-offset:.18em}.niwa-blog-article img{display:block;max-width:100%;height:auto}.niwa-blog-article .db-figure{margin:2rem 0}.niwa-blog-article .db-list{padding-left:1.4rem}.niwa-blog-article .db-quote{margin:1.75rem 0;padding:1rem 1.25rem;border-left:4px solid var(--niwa-blog-accent);background:color-mix(in srgb,var(--niwa-blog-accent) 7%,transparent)}@media(max-width:640px){.niwa-blog-article{width:100%;max-width:100%}.niwa-blog-article .db-toc{padding:1rem}.niwa-blog-article .db-toc-sub{margin-left:.35rem}}';
	}

	/** Compatibility entry used by the explicit legacy migrator. */
	private function blog_pipeline_generate_progress_bar_js_body() {
		return '(function(){var b=document.querySelector(".niwa-blog-article .db-progress-bar");if(!b)return;function u(){var d=document.documentElement,h=Math.max(d.scrollHeight-window.innerHeight,1);b.style.width=Math.max(0,Math.min(100,(window.scrollY/h)*100))+"%"}u();window.addEventListener("scroll",u,{passive:true});window.addEventListener("resize",u,{passive:true})})();';
	}

}
