<?php
/**
 * WP Blog Agent Admin Settings Page
 *
 * @package WP_Blog_Agent
 * @since 1.0.0
 */

// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * WBAG_Admin class for managing plugin settings and admin interface
 *
 * @since 1.0.0
 */
class WBAG_Admin {

	/**
	 * Plugin version
	 *
	 * @var string
	 */
	private $version = WBAG_VERSION;

	/**
	 * Plugin slug
	 *
	 * @var string
	 */
	private $plugin_slug = 'wbag';

	/**
	 * Text domain
	 *
	 * @var string
	 */
	private $text_domain = 'wp-blog-agent';

	/**
	 * Constructor
	 *
	 * @since 1.0.0
	 */
	public function __construct() {
		// Add admin menu
		add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );

		// Register settings
		add_action( 'admin_init', array( $this, 'register_settings' ) );

		// Enqueue admin scripts and styles
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_assets' ) );

		// Register AJAX handlers
		add_action( 'wp_ajax_wbag_test_api', array( $this, 'ajax_test_api' ) );
		add_action( 'wp_ajax_wbag_test_gemini_api', array( $this, 'ajax_test_gemini_api' ) );
		add_action( 'wp_ajax_wbag_run_topic_scout', array( $this, 'ajax_run_topic_scout' ) );
		add_action( 'wp_ajax_wbag_run_blog_writer', array( $this, 'ajax_run_blog_writer' ) );
		add_action( 'wp_ajax_wbag_approve_topic', array( $this, 'ajax_approve_topic' ) );
		add_action( 'wp_ajax_wbag_remove_approved_topic', array( $this, 'ajax_remove_approved_topic' ) );
		add_action( 'wp_ajax_wbag_delete_topic', array( $this, 'ajax_delete_topic' ) );
		add_action( 'wp_ajax_wbag_reset_dismissed_topics', array( $this, 'ajax_reset_dismissed_topics' ) );
		add_action( 'wp_ajax_wbag_save_topic_notes', array( $this, 'ajax_save_topic_notes' ) );
		add_action( 'wp_ajax_wbag_save_topic_images', array( $this, 'ajax_save_topic_images' ) );
		add_action( 'wp_ajax_wbag_remove_topic_image', array( $this, 'ajax_remove_topic_image' ) );
		add_action( 'wp_ajax_wbag_analyze_reference', array( $this, 'ajax_analyze_reference' ) );
		add_action( 'wp_ajax_wbag_delete_custom_template', array( $this, 'ajax_delete_custom_template' ) );
		add_action( 'wp_ajax_wbag_write_custom_topic', array( $this, 'ajax_write_custom_topic' ) );
		add_action( 'wp_ajax_wbag_preview_post', array( $this, 'ajax_preview_post' ) );
		add_action( 'wp_ajax_wbag_revise_post', array( $this, 'ajax_revise_post' ) );
		add_action( 'wp_ajax_wbag_run_autopilot_now', array( $this, 'ajax_run_autopilot_now' ) );
		add_action( 'wp_ajax_wbag_autopilot_progress', array( $this, 'ajax_autopilot_progress' ) );
		add_action( 'wp_ajax_wbag_save_rss_settings', array( $this, 'ajax_save_rss_settings' ) );
		add_action( 'wp_ajax_wbag_preview_rss_matches', array( $this, 'ajax_preview_rss_matches' ) );

		// KI-Überarbeitung in the post editor (Gutenberg + Classic).
		add_action( 'add_meta_boxes', array( $this, 'add_revise_meta_box' ) );
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_editor_revise_assets' ) );

		// Preserve active tab after settings save redirect.
		add_filter( 'wp_redirect', array( $this, 'preserve_active_tab_on_redirect' ) );
	}

	/**
	 * Append the active tab hash to the redirect URL after settings save.
	 *
	 * WordPress strips the URL hash when redirecting after options.php save.
	 * This filter re-appends it so the user stays on the same tab.
	 *
	 * @param string $location Redirect URL.
	 * @return string Modified redirect URL with tab hash.
	 * @since 1.3.65
	 */
	public function preserve_active_tab_on_redirect( $location ) {
		// Only act on the settings-updated redirect for our settings page.
		if ( strpos( $location, 'page=wbag-settings' ) === false || strpos( $location, 'settings-updated=true' ) === false ) {
			return $location;
		}

		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Read-only; nonce verified by options.php before redirect.
		$active_tab = isset( $_POST['wbag_active_tab'] ) ? sanitize_key( $_POST['wbag_active_tab'] ) : '';
		if ( ! empty( $active_tab ) ) {
			$location = remove_query_arg( 'wbag_tab', $location );
			$location .= '#' . $active_tab;
		}

		return $location;
	}

	/**
	 * Add admin menu
	 *
	 * @since 1.0.0
	 */
	public function add_admin_menu() {
		add_menu_page(
			__( 'Blog Agent', 'wp-blog-agent' ),
			__( 'Blog Agent', 'wp-blog-agent' ),
			'manage_options',
			'wbag-settings',
			array( $this, 'render_settings_page' ),
			'dashicons-edit-large',
			100
		);
	}

	/**
	 * Register settings
	 *
	 * @since 1.0.0
	 */
	public function register_settings() {
		register_setting(
			'wbag_settings_group',
			'wbag_settings',
			array(
				'type'              => 'array',
				'sanitize_callback' => array( $this, 'sanitize_settings' ),
				'show_in_rest'      => false,
			)
		);
	}

	/**
	 * Sanitize settings
	 *
	 * @param array $input The input settings array.
	 * @return array Sanitized settings
	 *
	 * @since 1.0.0
	 */
	public function sanitize_settings( $input ) {
		// Start from the existing option so that fields not present in $input
		// (e.g. because they live on another tab, because a request hit a size
		// limit, or because the form was rendered by an older version) keep
		// their stored values instead of silently being wiped. Important for
		// long free-text fields like `reference_style` which users may
		// painstakingly have filled in or auto-generated via the analyzer.
		$existing = get_option( 'wbag_settings', array() );
		if ( ! is_array( $existing ) ) {
			$existing = array();
		}

		if ( ! is_array( $input ) ) {
			// Nothing submitted — do NOT overwrite existing settings.
			return $existing;
		}

		$sanitized = $existing;

		// Checkbox fields need special handling: unchecked boxes are NOT sent
		// in the POST body, so after merging from existing we must treat them
		// as 0 if missing (otherwise we could never uncheck them).
		$checkbox_fields = array( 'include_toc', 'schedule_enabled' );
		foreach ( $checkbox_fields as $cb ) {
			$sanitized[ $cb ] = ! empty( $input[ $cb ] ) ? 1 : 0;
		}
		// schedule_days is a multi-checkbox — treat a missing key as "all
		// unchecked" rather than falling back to old value.
		if ( ! isset( $input['schedule_days'] ) ) {
			$sanitized['schedule_days'] = array();
		}

		// Identity tab
		if ( isset( $input['post_author_id'] ) ) {
			$sanitized['post_author_id'] = absint( $input['post_author_id'] );
		}
		// Legacy: keep author_name in sync for prompt usage.
		if ( ! empty( $sanitized['post_author_id'] ) ) {
			$user = get_userdata( $sanitized['post_author_id'] );
			$sanitized['author_name'] = $user ? $user->display_name : '';
		}
		if ( isset( $input['language'] ) ) {
			$allowed_languages = array( 'de', 'en', 'fr', 'es', 'it' );
			$sanitized['language'] = in_array( $input['language'], $allowed_languages, true ) ? $input['language'] : 'en';
		}
		if ( isset( $input['formal_address'] ) ) {
			$allowed_address = array( 'sie', 'du' );
			$sanitized['formal_address'] = in_array( $input['formal_address'], $allowed_address, true ) ? $input['formal_address'] : 'sie';
		}

		// Niche tab
		if ( isset( $input['niche'] ) ) {
			$sanitized['niche'] = sanitize_text_field( $input['niche'] );
		}
		if ( isset( $input['subtopics'] ) ) {
			$sanitized['subtopics'] = sanitize_textarea_field( $input['subtopics'] );
		}
		if ( isset( $input['target_audience'] ) ) {
			$sanitized['target_audience'] = sanitize_textarea_field( $input['target_audience'] );
		}
		if ( isset( $input['competitor_urls'] ) ) {
			$sanitized['competitor_urls'] = sanitize_textarea_field( $input['competitor_urls'] );
		}
		if ( isset( $input['topic_count'] ) ) {
			$sanitized['topic_count'] = max( 1, min( 20, intval( $input['topic_count'] ) ) );
		}

		// Structure tab
		if ( isset( $input['word_count_min'] ) ) {
			$sanitized['word_count_min'] = intval( $input['word_count_min'] );
		}
		if ( isset( $input['word_count_max'] ) ) {
			$sanitized['word_count_max'] = intval( $input['word_count_max'] );
		}
		if ( isset( $input['faq_count'] ) ) {
			$sanitized['faq_count'] = intval( $input['faq_count'] );
		}
		if ( isset( $input['cta_text'] ) ) {
			$sanitized['cta_text'] = sanitize_text_field( $input['cta_text'] );
		}
		if ( isset( $input['cta_url'] ) ) {
			$sanitized['cta_url'] = esc_url_raw( $input['cta_url'] );
		}
		if ( isset( $input['cta_color'] ) ) {
			// Validate hex color
			$color = sanitize_hex_color( $input['cta_color'] );
			$sanitized['cta_color'] = ! empty( $color ) ? $color : '#0073aa';
		}
		if ( isset( $input['related_posts'] ) ) {
			$sanitized['related_posts'] = intval( $input['related_posts'] );
		}
		if ( isset( $input['content_max_width'] ) ) {
			$val = intval( $input['content_max_width'] );
			$sanitized['content_max_width'] = ( $val >= 400 && $val <= 1600 ) ? $val : '';
		}
		if ( isset( $input['include_toc'] ) ) {
			$sanitized['include_toc'] = ! empty( $input['include_toc'] ) ? 1 : 0;
		}
		if ( isset( $input['post_category'] ) ) {
			$sanitized['post_category'] = sanitize_title( $input['post_category'] );
		}
		if ( isset( $input['reference_url'] ) ) {
			$sanitized['reference_url'] = esc_url_raw( $input['reference_url'] );
		}
		if ( isset( $input['reference_style'] ) ) {
			$sanitized['reference_style'] = sanitize_textarea_field( $input['reference_style'] );
		}

		// Custom style slots (3 additional manually-maintained variants).
		for ( $i = 1; $i <= 3; $i++ ) {
			$label_key = 'custom_style_' . $i . '_label';
			$text_key  = 'custom_style_' . $i;
			if ( isset( $input[ $label_key ] ) ) {
				$sanitized[ $label_key ] = sanitize_text_field( $input[ $label_key ] );
			}
			if ( isset( $input[ $text_key ] ) ) {
				$sanitized[ $text_key ] = sanitize_textarea_field( $input[ $text_key ] );
			}
		}

		// Active style selector (which of the four slots is actually used).
		if ( isset( $input['active_style'] ) ) {
			$allowed_active = array( 'detected', 'custom_1', 'custom_2', 'custom_3' );
			$sanitized['active_style'] = in_array( $input['active_style'], $allowed_active, true )
				? $input['active_style']
				: 'detected';
		}

		// SEO tab
		if ( isset( $input['seo_plugin'] ) ) {
			$allowed_seo = array( 'rankmath', 'yoast', 'seopress', 'none' );
			$sanitized['seo_plugin'] = in_array( $input['seo_plugin'], $allowed_seo, true ) ? $input['seo_plugin'] : 'none';
		}
		if ( isset( $input['focus_kw_strategy'] ) ) {
			$allowed_strategy = array( 'single', 'multiple' );
			$sanitized['focus_kw_strategy'] = in_array( $input['focus_kw_strategy'], $allowed_strategy, true ) ? $input['focus_kw_strategy'] : 'single';
		}
		if ( isset( $input['internal_links'] ) ) {
			$sanitized['internal_links'] = intval( $input['internal_links'] );
		}
		if ( isset( $input['external_links'] ) ) {
			$sanitized['external_links'] = intval( $input['external_links'] );
		}

		// API tab
		if ( isset( $input['api_key'] ) ) {
			$sanitized['api_key'] = sanitize_text_field( $input['api_key'] );
		}
		if ( isset( $input['api_model'] ) ) {
			$allowed_models = array(
				'claude-sonnet-4-6',
				'claude-opus-4-6',
				'claude-haiku-4-5-20251001',
			);
			$sanitized['api_model'] = in_array( $input['api_model'], $allowed_models, true ) ? $input['api_model'] : 'claude-sonnet-4-6';
		}
		if ( isset( $input['gemini_api_key'] ) ) {
			$sanitized['gemini_api_key'] = sanitize_text_field( $input['gemini_api_key'] );
		}

		// Schedule tab (Pro feature - server-side gate)
		if ( isset( $input['schedule_enabled'] ) ) {
			// Hard gate: only Pro users can enable the scheduler, regardless of UI state.
			$sanitized['schedule_enabled'] = ( wbag_is_pro() && ! empty( $input['schedule_enabled'] ) ) ? 1 : 0;
		} else {
			$sanitized['schedule_enabled'] = 0;
		}

		// If user lost Pro, proactively clear any pending cron events.
		if ( ! wbag_is_pro() && class_exists( 'WBAG_Scheduler' ) ) {
			WBAG_Scheduler::unschedule();
		}
		if ( isset( $input['schedule_days'] ) ) {
			$allowed_days = array( 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday' );
			$days = is_array( $input['schedule_days'] ) ? $input['schedule_days'] : array();
			$sanitized['schedule_days'] = array_intersect( $days, $allowed_days );
		}
		if ( isset( $input['schedule_time'] ) ) {
			$time = sanitize_text_field( $input['schedule_time'] );
			// Validate time format HH:MM
			if ( preg_match( '/^\d{2}:\d{2}$/', $time ) ) {
				$sanitized['schedule_time'] = $time;
			}
		}
		if ( isset( $input['batch_size'] ) ) {
			$sanitized['batch_size'] = intval( $input['batch_size'] );
		}

		// RSS Feeds (Autopilot tab, Pro only)
		if ( wbag_is_pro() ) {
			$allowed_predefined = array( 'google_news_de', 'tagesschau', 'heise', 't3n', 'golem', 'spiegel', 'welt', 'google_news_en', 'bbc', 'reuters', 'techcrunch', 'the_verge', 'ars_technica', 'wired', 'hacker_news' );
			if ( isset( $input['rss_predefined'] ) && is_array( $input['rss_predefined'] ) ) {
				$sanitized['rss_predefined'] = array_values( array_intersect( $input['rss_predefined'], $allowed_predefined ) );
			} else {
				$sanitized['rss_predefined'] = array();
			}
			if ( isset( $input['rss_custom'] ) ) {
				$sanitized['rss_custom'] = sanitize_textarea_field( $input['rss_custom'] );
			}
			if ( isset( $input['rss_keywords'] ) ) {
				$sanitized['rss_keywords'] = sanitize_textarea_field( $input['rss_keywords'] );
			}
			$sanitized['rss_notify_email'] = ! empty( $input['rss_notify_email'] ) ? 1 : 0;
		}

		// Images tab
		if ( isset( $input['image_style'] ) ) {
			$sanitized['image_style'] = sanitize_textarea_field( $input['image_style'] );
		}
		if ( isset( $input['image_size'] ) ) {
			$allowed_sizes = array( '1024x572', '1024x1024', '1200x628' );
			$sanitized['image_size'] = in_array( $input['image_size'], $allowed_sizes, true ) ? $input['image_size'] : '1024x572';
		}
		if ( isset( $input['image_count'] ) ) {
			$sanitized['image_count'] = intval( $input['image_count'] );
		}
		if ( isset( $input['image_prompt_lang'] ) ) {
			$allowed_img_langs = array( 'en', 'de' );
			$sanitized['image_prompt_lang'] = in_array( $input['image_prompt_lang'], $allowed_img_langs, true ) ? $input['image_prompt_lang'] : 'en';
		}

		// Affiliate Links tab (Pro only)
		if ( wbag_is_pro() && isset( $input['global_affiliate_links'] ) && is_array( $input['global_affiliate_links'] ) ) {
			$clean_links = array();
			foreach ( $input['global_affiliate_links'] as $alink ) {
				if ( ! is_array( $alink ) ) {
					continue;
				}
				$label       = sanitize_text_field( $alink['label'] ?? '' );
				$url         = esc_url_raw( $alink['url'] ?? '' );
				$description = sanitize_text_field( $alink['description'] ?? '' );
				if ( ! empty( $label ) && ! empty( $url ) ) {
					$clean_links[] = array(
						'label'       => $label,
						'url'         => $url,
						'description' => $description,
					);
				}
			}
			$sanitized['global_affiliate_links'] = $clean_links;
		}

		// (Re)schedule cron after saving settings.
		if ( class_exists( 'WBAG_Scheduler' ) ) {
			if ( wbag_is_pro() && ! empty( $sanitized['schedule_enabled'] ) ) {
				WBAG_Scheduler::schedule( $sanitized );
			} else {
				WBAG_Scheduler::unschedule();
			}
		}

		return $sanitized;
	}

	/**
	 * Enqueue admin assets
	 *
	 * @param string $hook_suffix The current admin page hook.
	 *
	 * @since 1.0.0
	 */
	public function enqueue_admin_assets( $hook_suffix ) {
		// Only enqueue on our plugin settings page
		if ( 'toplevel_page_wbag-settings' !== $hook_suffix ) {
			return;
		}

		// Enqueue styles
		wp_enqueue_style(
			'wbag-admin',
			plugin_dir_url( dirname( __FILE__ ) ) . 'admin/css/wbag-admin.css',
			array(),
			$this->version
		);

		// Enqueue color picker
		wp_enqueue_style( 'wp-color-picker' );

		// Enqueue WordPress media uploader for topic image uploads.
		wp_enqueue_media();

		// Enqueue scripts
		wp_enqueue_script(
			'wbag-admin',
			plugin_dir_url( dirname( __FILE__ ) ) . 'admin/js/wbag-admin.js',
			array( 'jquery', 'wp-color-picker' ),
			$this->version,
			true
		);

		// Localize script with AJAX data
		$current_settings = get_option( 'wbag_settings', array() );
		wp_localize_script(
			'wbag-admin',
			'wbag_admin',
			array(
				'ajax_url'        => admin_url( 'admin-ajax.php' ),
				'nonce'           => wp_create_nonce( 'wbag_admin_nonce' ),
				'has_gemini_key'  => ! empty( $current_settings['gemini_api_key'] ),
				'i18n_processing' => __( 'Wird verarbeitet …', 'wp-blog-agent' ),
			)
		);
	}

	/**
	 * Register KI-Überarbeitung meta box for the post editor.
	 * Only shown for posts generated by this plugin.
	 *
	 * @since 1.3.29
	 */
	public function add_revise_meta_box() {
		$screen = get_current_screen();
		if ( ! $screen || 'post' !== $screen->post_type ) {
			return;
		}

		// Only show for WBAG-generated posts.
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only check on admin edit screen.
		$post_id = isset( $_GET['post'] ) ? absint( $_GET['post'] ) : 0;
		if ( ! $post_id || '1' !== get_post_meta( $post_id, '_wbag_generated', true ) ) {
			return;
		}

		add_meta_box(
			'wbag-revise-metabox',
			'🤖 ' . __( 'KI-Überarbeitung', 'wp-blog-agent' ),
			array( $this, 'render_revise_meta_box' ),
			'post',
			'side',
			'high'
		);
	}

	/**
	 * Render KI-Überarbeitung meta box content.
	 *
	 * @param WP_Post $post The current post.
	 * @since 1.3.29
	 */
	public function render_revise_meta_box( $post ) {
		$is_pro       = wbag_is_pro();
		$rev_count    = (int) get_post_meta( $post->ID, '_wbag_revision_count', true );
		$last_rev     = get_post_meta( $post->ID, '_wbag_last_revision_at', true );
		?>
		<div class="wbag-editor-revise">
			<?php if ( ! $is_pro ) : ?>
				<p class="wbag-editor-revise-pro-notice" style="background:#fff3cd;padding:8px 10px;border-left:3px solid #ffc107;margin:0 0 10px;">
					<?php esc_html_e( 'Die KI-Überarbeitung ist ein Pro-Feature.', 'wp-blog-agent' ); ?>
					<?php if ( function_exists( 'wbag_fs' ) ) : ?>
						<a href="<?php echo esc_url( wbag_fs()->get_upgrade_url() ); ?>"><?php esc_html_e( 'Jetzt upgraden', 'wp-blog-agent' ); ?></a>
					<?php endif; ?>
				</p>
			<?php endif; ?>

			<p style="margin:0 0 8px;font-size:12px;color:#666;">
				<?php
				printf(
					/* translators: %d = number of revisions */
					esc_html__( 'Bisherige Überarbeitungen: %d', 'wp-blog-agent' ),
					intval( $rev_count )
				);
				if ( $last_rev ) {
					echo ' &middot; ' . esc_html( $last_rev );
				}
				?>
			</p>

			<textarea
				id="wbag-editor-revise-input"
				rows="4"
				style="width:100%;margin-bottom:8px;"
				placeholder="<?php esc_attr_e( 'Beschreibe, was geändert werden soll, z.B.: „Der Code-Block ist nicht lesbar, bitte formatiere ihn besser" oder „Füge eine Zusammenfassung am Anfang ein"', 'wp-blog-agent' ); ?>"
				<?php disabled( ! $is_pro ); ?>
			></textarea>

			<div style="display:flex;align-items:center;gap:8px;">
				<button
					type="button"
					id="wbag-editor-revise-btn"
					class="button button-primary"
					<?php disabled( ! $is_pro ); ?>
				>
					<?php esc_html_e( 'Überarbeiten lassen', 'wp-blog-agent' ); ?>
				</button>
				<span id="wbag-editor-revise-status" style="font-size:12px;color:#666;"></span>
			</div>
		</div>
		<?php
	}

	/**
	 * Enqueue JS/CSS for the KI-Überarbeitung meta box on post edit screens.
	 *
	 * @param string $hook_suffix The current admin page.
	 * @since 1.3.29
	 */
	public function enqueue_editor_revise_assets( $hook_suffix ) {
		if ( ! in_array( $hook_suffix, array( 'post.php', 'post-new.php' ), true ) ) {
			return;
		}

		$screen = get_current_screen();
		if ( ! $screen || 'post' !== $screen->post_type ) {
			return;
		}

		// Only for WBAG-generated posts.
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only check on admin edit screen.
		$post_id = isset( $_GET['post'] ) ? absint( $_GET['post'] ) : 0;
		if ( ! $post_id || '1' !== get_post_meta( $post_id, '_wbag_generated', true ) ) {
			return;
		}

		wp_enqueue_script(
			'wbag-editor-revise',
			plugin_dir_url( dirname( __FILE__ ) ) . 'admin/js/wbag-editor-revise.js',
			array( 'jquery' ),
			$this->version,
			true
		);

		wp_localize_script(
			'wbag-editor-revise',
			'wbag_editor_revise',
			array(
				'ajax_url' => admin_url( 'admin-ajax.php' ),
				'nonce'    => wp_create_nonce( 'wbag_admin_nonce' ),
				'post_id'  => $post_id,
				'i18n'     => array(
					'working'     => __( 'KI arbeitet …', 'wp-blog-agent' ),
					'success'     => __( 'Überarbeitung angewendet! Seite wird neu geladen …', 'wp-blog-agent' ),
					'error'       => __( 'Fehler: ', 'wp-blog-agent' ),
					'empty'       => __( 'Bitte beschreibe, was geändert werden soll.', 'wp-blog-agent' ),
				),
			)
		);
	}

	/**
	 * Render settings page
	 *
	 * @since 1.0.0
	 */
	public function render_settings_page() {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_die( esc_html__( 'You do not have permission to access this page.', 'wp-blog-agent' ) );
		}

		$settings = get_option( 'wbag_settings', array() );
		$topic_queue = get_option( 'wbag_topic_queue', array() );
		?>
		<div class="wbag-admin-container">
			<?php $this->render_header(); ?>

			<div class="wbag-settings-wrapper">
				<form method="post" action="options.php" class="wbag-settings-form">
					<?php settings_fields( 'wbag_settings_group' ); ?>
					<input type="hidden" id="wbag_active_tab" name="wbag_active_tab" value="" />

					<!-- Tab Navigation -->
					<div class="wbag-tabs-nav">
						<button type="button" class="wbag-tab-button active" data-tab="content">
							<span class="dashicons dashicons-welcome-write-blog"></span>
							<?php esc_html_e( 'Inhalte erstellen', 'wp-blog-agent' ); ?>
						</button>
						<button type="button" class="wbag-tab-button" data-tab="identity">
							<span class="dashicons dashicons-id-alt"></span>
							<?php esc_html_e( 'Identität', 'wp-blog-agent' ); ?>
						</button>
						<button type="button" class="wbag-tab-button" data-tab="niche">
							<span class="dashicons dashicons-tag"></span>
							<?php esc_html_e( 'Nische & Themen', 'wp-blog-agent' ); ?>
						</button>
						<button type="button" class="wbag-tab-button" data-tab="structure">
							<span class="dashicons dashicons-editor-table"></span>
							<?php esc_html_e( 'Beitragsstruktur', 'wp-blog-agent' ); ?>
						</button>
						<button type="button" class="wbag-tab-button" data-tab="seo">
							<span class="dashicons dashicons-chart-line"></span>
							<?php esc_html_e( 'SEO', 'wp-blog-agent' ); ?>
						</button>
						<button type="button" class="wbag-tab-button" data-tab="api">
							<span class="dashicons dashicons-rest-api"></span>
							<?php esc_html_e( 'API', 'wp-blog-agent' ); ?>
						</button>
						<button type="button" class="wbag-tab-button" data-tab="schedule">
							<span class="dashicons dashicons-controls-repeat"></span>
							<?php esc_html_e( 'Autopilot', 'wp-blog-agent' ); ?>
							<span class="wbag-pro-badge">PRO</span>
						</button>
						<button type="button" class="wbag-tab-button" data-tab="affiliate">
							<span class="dashicons dashicons-money-alt"></span>
							<?php esc_html_e( 'Affiliate-Links', 'wp-blog-agent' ); ?>
							<span class="wbag-pro-badge">PRO</span>
						</button>
					</div>

					<!-- Content Creation Tab -->
					<div class="wbag-tab-content active" id="tab-content">
						<!-- Action Buttons -->
						<div class="wbag-actions-section">
							<h2 class="wbag-section-title">
								<span class="dashicons dashicons-controls-play"></span>
								<?php esc_html_e( 'Aktionen', 'wp-blog-agent' ); ?>
							</h2>
							<div class="wbag-actions-grid">
								<div class="wbag-action-card wbag-action-card--scout">
									<div class="wbag-action-card-icon">
										<span class="dashicons dashicons-search"></span>
									</div>
									<div class="wbag-action-card-body">
										<h3>
											<?php esc_html_e( 'Themen finden', 'wp-blog-agent' ); ?>
											<span class="wbag-pro-badge">PRO</span>
											<span class="wbag-info-icon" aria-label="<?php esc_attr_e( 'Info', 'wp-blog-agent' ); ?>">?<span class="wbag-tooltip"><?php esc_html_e( 'Die KI analysiert Ihre Nische, Zielgruppe und Unterthemen und schlägt passende Blog-Themen mit Fokus-Keywords vor. Die Ergebnisse landen in der Warteschlange.', 'wp-blog-agent' ); ?></span></span>
										</h3>
										<p><?php esc_html_e( 'KI recherchiert neue Blog-Themen passend zu Ihrer Nische und Zielgruppe.', 'wp-blog-agent' ); ?></p>
										<?php if ( ! wbag_is_pro() ) : ?>
										<p class="wbag-upgrade-inline">
											<a href="<?php echo esc_url( function_exists( 'wbag_fs' ) ? wbag_fs()->get_upgrade_url() : 'https://www.2fox4.de/automatischer-ki-wordpress-blog-agent/' ); ?>">
												<?php esc_html_e( 'Pro-Feature &ndash; jetzt upgraden &rarr;', 'wp-blog-agent' ); ?>
											</a>
										</p>
										<?php endif; ?>
										<div class="wbag-scout-options">
											<label for="wbag_topic_count_inline"><?php esc_html_e( 'Anzahl:', 'wp-blog-agent' ); ?></label>
											<input
												type="number"
												id="wbag_topic_count_inline"
												name="wbag_settings[topic_count]"
												value="<?php echo esc_attr( isset( $settings['topic_count'] ) ? $settings['topic_count'] : '5' ); ?>"
												min="1"
												max="20"
												step="1"
											/>
										</div>
									</div>
									<button
										type="button"
										class="button wbag-action-btn wbag-btn-scout"
										data-action="topic-scout"
										<?php disabled( ! wbag_is_pro() ); ?>
									>
										<span class="dashicons <?php echo wbag_is_pro() ? 'dashicons-search' : 'dashicons-lock'; ?>"></span>
										<?php esc_html_e( 'Themen finden', 'wp-blog-agent' ); ?>
									</button>
									<div class="wbag-action-spinner" style="display:none;">
										<span class="spinner"></span>
									</div>
									<div class="wbag-action-result"></div>
								</div>
							</div>
						</div>

						<!-- Custom Topic -->
						<div class="wbag-custom-topic-section">
							<h2 class="wbag-section-title">
								<span class="dashicons dashicons-edit-page"></span>
								<?php esc_html_e( 'Eigenes Thema', 'wp-blog-agent' ); ?>
									<span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Erstellen Sie manuell einen Blogbeitrag zu einem Thema Ihrer Wahl. Geben Sie Titel, Beschreibung und optional ein Fokus-Keyword ein.', 'wp-blog-agent' ); ?></span></span>
							</h2>
							<div class="wbag-card wbag-custom-topic-card">
								<p class="wbag-custom-topic-desc">
									<?php esc_html_e( 'Legen Sie selbst ein Thema fest, zu dem ein Blogbeitrag geschrieben werden soll.', 'wp-blog-agent' ); ?>
								</p>
								<div class="wbag-form-group">
									<label for="wbag_custom_topic_title">
										<?php esc_html_e( 'Beitragsthema', 'wp-blog-agent' ); ?>
									</label>
									<input
										type="text"
										id="wbag_custom_topic_title"
										class="regular-text widefat"
										placeholder="<?php esc_attr_e( 'z. B. Die besten WordPress-Themes für 2026', 'wp-blog-agent' ); ?>"
									/>
								</div>
								<div class="wbag-form-group">
									<label for="wbag_custom_topic_description">
										<?php esc_html_e( 'Beschreibung / Schwerpunkte (optional)', 'wp-blog-agent' ); ?>
									</label>
									<textarea
										id="wbag_custom_topic_description"
										class="regular-text widefat"
										rows="2"
										placeholder="<?php esc_attr_e( 'Worum soll es im Detail gehen? Welche Schwerpunkte?', 'wp-blog-agent' ); ?>"
									></textarea>
								</div>
								<div class="wbag-form-group">
									<label for="wbag_custom_topic_keyword">
										<?php esc_html_e( 'Fokus-Keyword (optional)', 'wp-blog-agent' ); ?>
									</label>
									<input
										type="text"
										id="wbag_custom_topic_keyword"
										class="regular-text"
										placeholder="<?php esc_attr_e( 'z. B. WordPress Themes 2026', 'wp-blog-agent' ); ?>"
									/>
								</div>


								<hr style="margin: 20px 0; border: none; border-top: 1px solid #e2e8f0;" />

								<button
									type="button"
									id="wbag_custom_topic_write"
									class="button button-primary button-large wbag-action-btn"
								>
									<span class="dashicons dashicons-welcome-write-blog"></span>
									<?php esc_html_e( 'Beitrag mit eigenem Thema schreiben', 'wp-blog-agent' ); ?>
								</button>
								<div id="wbag_custom_topic_spinner" class="wbag-action-spinner" style="display:none;">
									<span class="spinner"></span>
								</div>
								<div id="wbag_custom_topic_result" class="wbag-action-result"></div>

									<?php
									$custom_history = get_option( 'wbag_custom_topic_history', array() );
									if ( ! empty( $custom_history ) && is_array( $custom_history ) ) :
									?>
									<div class="wbag-template-history" style="margin-top: 24px;">
										<details>
											<summary style="cursor: pointer; font-size: 14px; font-weight: 600; color: #475569; padding: 10px 0; user-select: none;">
												<span class="dashicons dashicons-archive" style="font-size: 16px; width: 16px; height: 16px; vertical-align: -2px; margin-right: 4px;"></span>
												<?php printf( esc_html__( 'Letzte Themen (%d gespeichert)', 'wp-blog-agent' ), count( $custom_history ) ); ?>
											</summary>
											<div style="margin-top: 10px; border: 1px solid #e2e8f0; border-radius: 10px; overflow: hidden;">
												<?php foreach ( $custom_history as $hi => $tpl ) :
													$tpl_title = esc_attr( $tpl['title'] ?? '' );
													$tpl_desc  = esc_attr( $tpl['description'] ?? '' );
													$tpl_kw    = esc_attr( $tpl['keyword'] ?? '' );
													$tpl_date  = ! empty( $tpl['created_at'] ) ? date_i18n( get_option( 'date_format' ) . ', ' . get_option( 'time_format' ), strtotime( $tpl['created_at'] ) ) : '';
													$tpl_post  = ! empty( $tpl['post_id'] ) ? absint( $tpl['post_id'] ) : 0;
													$tpl_edit  = $tpl_post ? get_edit_post_link( $tpl_post, 'raw' ) : '';
												?>
													<div style="display: flex; align-items: center; gap: 12px; padding: 10px 14px; border-bottom: 1px solid #f1f5f9; font-size: 13px;">
														<div style="flex: 1; min-width: 0;">
															<strong style="color: #0f172a; display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;"><?php echo esc_html( $tpl['title'] ?? '' ); ?></strong>
															<?php if ( ! empty( $tpl['keyword'] ) ) : ?>
																<span style="color: #6366f1; font-size: 11px;"><?php echo esc_html( $tpl['keyword'] ); ?></span>
															<?php endif; ?>
															<?php if ( $tpl_date ) : ?>
																<span style="color: #94a3b8; font-size: 11px; margin-left: 6px;"><?php echo esc_html( $tpl_date ); ?></span>
															<?php endif; ?>
														</div>
														<div style="display: flex; gap: 6px; flex-shrink: 0;">
															<button type="button" class="button button-small wbag-load-template" data-title="<?php echo $tpl_title; ?>" data-description="<?php echo $tpl_desc; ?>" data-keyword="<?php echo $tpl_kw; ?>" title="<?php esc_attr_e( 'In Formular laden', 'wp-blog-agent' ); ?>">
																<span class="dashicons dashicons-update" style="font-size: 14px; width: 14px; height: 14px; vertical-align: -2px;"></span>
																<?php esc_html_e( 'Laden', 'wp-blog-agent' ); ?>
															</button>
															<?php if ( $tpl_edit ) : ?>
																<a href="<?php echo esc_url( $tpl_edit ); ?>" target="_blank" class="button button-small" title="<?php esc_attr_e( 'Beitrag bearbeiten', 'wp-blog-agent' ); ?>">
																	<span class="dashicons dashicons-edit" style="font-size: 14px; width: 14px; height: 14px; vertical-align: -2px;"></span>
																</a>
															<?php endif; ?>
																<button type="button" class="button button-small wbag-delete-template" data-index="<?php echo esc_attr( $hi ); ?>" title="<?php esc_attr_e( 'Vorlage entfernen', 'wp-blog-agent' ); ?>" style="color: #dc2626;">
																	<span class="dashicons dashicons-trash" style="font-size: 14px; width: 14px; height: 14px; vertical-align: -2px;"></span>
																</button>
														</div>
													</div>
												<?php endforeach; ?>
											</div>
										</details>
									</div>
									<?php endif; ?>
							</div>
						</div>

						<!-- Topic Queue -->
						<div class="wbag-card wbag-topic-queue">
							<h2 class="wbag-section-title">
								<span class="dashicons dashicons-list-view"></span>
								<?php esc_html_e( 'Themen-Warteschlange', 'wp-blog-agent' ); ?>
								<span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Hier werden alle von der KI gefundenen Themen aufgelistet. Sie können einzelne Themen direkt als Blogbeitrag schreiben lassen oder aus der Liste entfernen.', 'wp-blog-agent' ); ?></span></span>
							</h2>
							<?php if ( ! empty( $topic_queue ) && is_array( $topic_queue ) ) : ?>
								<div class="wbag-queue-list">
									<?php foreach ( $topic_queue as $index => $topic ) :
										$has_notes = ! empty( $topic['user_notes'] );
									?>
										<div class="wbag-queue-item" data-index="<?php echo esc_attr( $index ); ?>">
											<div class="wbag-queue-info">
												<h4>
													<?php echo esc_html( $topic['title'] ?? 'Untitled' ); ?>
													<?php if ( ! empty( $topic['competitor_inspired'] ) ) : ?>
														<?php /* translators: %s: competitor domain name */ ?>
														<span class="wbag-competitor-badge" title="<?php echo esc_attr( sprintf( __( 'Inspiriert von %s', 'wp-blog-agent' ), $topic['competitor_inspired'] ) ); ?>">
															<span class="dashicons dashicons-visibility"></span>
															<?php echo esc_html( $topic['competitor_inspired'] ); ?>
														</span>
													<?php endif; ?>
													<?php if ( ! empty( $topic['content_type'] ) ) : ?>
														<span class="wbag-content-type-badge" title="<?php esc_attr_e( 'Artikelformat', 'wp-blog-agent' ); ?>">
															<span class="dashicons dashicons-edit"></span>
															<?php echo esc_html( $topic['content_type'] ); ?>
														</span>
													<?php endif; ?>
												</h4>
												<?php if ( ! empty( $topic['description'] ) ) : ?>
													<p><?php echo esc_html( $topic['description'] ); ?></p>
												<?php endif; ?>
												<div class="wbag-queue-meta" style="margin-top: 6px; font-size: 11px; color: #94a3b8;">
													<?php if ( ! empty( $topic['created_at'] ) ) : ?>
														<span class="dashicons dashicons-clock" style="font-size: 11px; width: 11px; height: 11px; vertical-align: -1px;"></span>
														<?php echo esc_html( date_i18n( get_option( 'date_format' ) . ', ' . get_option( 'time_format' ), strtotime( $topic['created_at'] ) ) ); ?>
													<?php endif; ?>
													<?php if ( ! empty( $topic['source'] ) ) : ?>
														&middot;
														<?php if ( 'autopilot' === $topic['source'] ) : ?>
															<?php esc_html_e( 'Autopilot', 'wp-blog-agent' ); ?>
														<?php else : ?>
															<?php esc_html_e( 'Themen finden', 'wp-blog-agent' ); ?>
														<?php endif; ?>
													<?php endif; ?>
												</div>
											</div>
											<div class="wbag-queue-actions">
												<button
													type="button"
													class="button button-small wbag-toggle-notes<?php echo $has_notes ? ' wbag-has-notes' : ''; ?>"
													data-index="<?php echo esc_attr( $index ); ?>"
													title="<?php esc_attr_e( 'Hinweise für diesen Beitrag', 'wp-blog-agent' ); ?>"
												>
													<span class="dashicons dashicons-edit"></span>
													<?php esc_html_e( 'Hinweise', 'wp-blog-agent' ); ?>
												</button>
												<button
													type="button"
													class="button button-primary button-small wbag-write-topic"
													data-index="<?php echo esc_attr( $index ); ?>"
												>
													<span class="dashicons dashicons-welcome-write-blog"></span>
													<?php esc_html_e( 'Beitrag schreiben', 'wp-blog-agent' ); ?>
												</button>
												<button
													type="button"
													class="button button-small wbag-delete-topic"
													data-index="<?php echo esc_attr( $index ); ?>"
												>
													<?php esc_html_e( 'Löschen', 'wp-blog-agent' ); ?>
												</button>
										</div>
											<div class="wbag-queue-notes" data-index="<?php echo esc_attr( $index ); ?>" style="display: none;">
												<textarea
													class="wbag-notes-textarea"
													rows="3"
													placeholder="<?php esc_attr_e( 'z. B. Affiliate-Links, eigene Plugins erwähnen, bestimmte Quellen, Schwerpunkte …', 'wp-blog-agent' ); ?>"
												><?php echo esc_textarea( $topic['user_notes'] ?? '' ); ?></textarea>
												<div class="wbag-notes-actions">
													<button type="button" class="button button-small button-primary wbag-save-notes" data-index="<?php echo esc_attr( $index ); ?>">
														<span class="dashicons dashicons-saved"></span>
														<?php esc_html_e( 'Speichern', 'wp-blog-agent' ); ?>
													</button>
													<span class="wbag-notes-status"></span>
												</div>
											<?php if ( wbag_is_pro() ) : ?>
									<div class="wbag-topic-images" data-index="<?php echo esc_attr( $index ); ?>">
												<label class="wbag-images-label">
													<span class="dashicons dashicons-format-image"></span>
													<?php esc_html_e( 'Eigene Bilder', 'wp-blog-agent' ); ?>
													<span class="wbag-pro-badge">PRO</span>
													<span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Laden Sie eigene Bilder hoch, die der Blog Agent passend in den Beitrag einbaut. Die Bilder werden in der WordPress-Mediathek gespeichert.', 'wp-blog-agent' ); ?></span></span>
												</label>
												<div class="wbag-image-thumbs">
													<?php
													$topic_images = isset( $topic['user_images'] ) && is_array( $topic['user_images'] ) ? $topic['user_images'] : array();
													foreach ( $topic_images as $img_id ) :
														$thumb_url = wp_get_attachment_image_url( (int) $img_id, 'thumbnail' );
														if ( ! $thumb_url ) continue;
													?>
														<div class="wbag-image-thumb" data-id="<?php echo esc_attr( $img_id ); ?>">
															<img src="<?php echo esc_url( $thumb_url ); ?>" alt="" />
															<button type="button" class="wbag-remove-image" data-id="<?php echo esc_attr( $img_id ); ?>" data-index="<?php echo esc_attr( $index ); ?>" title="<?php esc_attr_e( 'Entfernen', 'wp-blog-agent' ); ?>">&times;</button>
														</div>
													<?php endforeach; ?>
												</div>
												<button type="button" class="button button-small wbag-add-topic-image" data-index="<?php echo esc_attr( $index ); ?>">
													<span class="dashicons dashicons-plus-alt2"></span>
													<?php esc_html_e( 'Bild hinzufügen', 'wp-blog-agent' ); ?>
												</button>
											</div>
											<?php endif; ?>
										</div>
										</div>
									<?php endforeach; ?>
								</div>
							<?php else : ?>
								<p><?php esc_html_e( 'Die Warteschlange ist leer. Verwenden Sie "Themen finden", um neue Themen hinzuzufügen.', 'wp-blog-agent' ); ?></p>
							<?php endif; ?>

							<?php
							$dismissed_topics = get_option( 'wbag_dismissed_topics', array() );
							$dismissed_count  = is_array( $dismissed_topics ) ? count( $dismissed_topics ) : 0;
							?>
							<?php if ( $dismissed_count > 0 ) : ?>
								<div class="wbag-dismissed-info" style="margin-top: 16px; padding: 12px 16px; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 8px;">
									<span style="color: #64748b; font-size: 13px;">
										<span class="dashicons dashicons-dismiss" style="font-size: 16px; width: 16px; height: 16px; vertical-align: text-bottom; color: #94a3b8;"></span>
										<?php
										printf(
											/* translators: %d = number of dismissed topics */
											esc_html( _n(
												'%d Thema wird bei der nächsten Suche ausgeschlossen.',
												'%d Themen werden bei der nächsten Suche ausgeschlossen.',
												$dismissed_count,
												'wp-blog-agent'
											) ),
											intval( $dismissed_count )
										);
										?>
									</span>
									<button type="button" id="wbag_reset_dismissed" class="button button-small">
										<span class="dashicons dashicons-image-rotate" style="font-size: 14px; width: 14px; height: 14px; vertical-align: text-bottom;"></span>
										<?php esc_html_e( 'Blacklist zurücksetzen', 'wp-blog-agent' ); ?>
									</button>
								</div>
							<?php endif; ?>
						</div>
					</div>

					<!-- Identity Tab -->
					<div class="wbag-tab-content" id="tab-identity">
						<div class="wbag-card">
							<h2><?php esc_html_e( 'Identität', 'wp-blog-agent' ); ?> <span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Definieren Sie die Identität Ihres Blogs: Autorenname, Sprache und Anrede. Diese Informationen fließen in jeden generierten Beitrag ein.', 'wp-blog-agent' ); ?></span></span></h2>

							<div class="wbag-form-group">
								<label for="wbag_post_author_id">
									<?php esc_html_e( 'Autor', 'wp-blog-agent' ); ?>
									<span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Der ausgewählte Benutzer wird als Autor aller generierten Beiträge eingetragen. Sein Anzeigename fließt auch in den KI-Prompt ein.', 'wp-blog-agent' ); ?></span></span>
								</label>
								<?php
								$selected_author = isset( $settings['post_author_id'] ) ? absint( $settings['post_author_id'] ) : 0;
								wp_dropdown_users( array(
									'name'             => 'wbag_settings[post_author_id]',
									'id'               => 'wbag_post_author_id',
									'class'            => 'regular-text',
									'selected'         => $selected_author,
									'capability'       => array( 'edit_posts' ),
									'show_option_none' => esc_html__( '— Autor wählen —', 'wp-blog-agent' ),
									'option_none_value' => 0,
									'show'             => 'display_name_with_login',
								) );
								?>
								<p class="description">
									<?php esc_html_e( 'WordPress-Benutzer, der als Autor für generierte Beiträge eingetragen wird.', 'wp-blog-agent' ); ?>
								</p>
							</div>

							<div class="wbag-form-group">
								<label for="wbag_language">
									<?php esc_html_e( 'Sprache', 'wp-blog-agent' ); ?>
								</label>
								<select
									id="wbag_language"
									name="wbag_settings[language]"
									class="regular-text"
								>
									<option value="de" <?php selected( isset( $settings['language'] ) ? $settings['language'] : 'de', 'de' ); ?>>
										<?php esc_html_e( 'Deutsch', 'wp-blog-agent' ); ?>
									</option>
									<option value="en" <?php selected( isset( $settings['language'] ) ? $settings['language'] : 'de', 'en' ); ?>>
										<?php esc_html_e( 'English', 'wp-blog-agent' ); ?>
									</option>
									<option value="fr" <?php selected( isset( $settings['language'] ) ? $settings['language'] : 'de', 'fr' ); ?>>
										<?php esc_html_e( 'Français', 'wp-blog-agent' ); ?>
									</option>
									<option value="es" <?php selected( isset( $settings['language'] ) ? $settings['language'] : 'de', 'es' ); ?>>
										<?php esc_html_e( 'Español', 'wp-blog-agent' ); ?>
									</option>
									<option value="it" <?php selected( isset( $settings['language'] ) ? $settings['language'] : 'de', 'it' ); ?>>
										<?php esc_html_e( 'Italiano', 'wp-blog-agent' ); ?>
									</option>
								</select>
								<p class="description">
									<?php esc_html_e( 'Wählen Sie die Sprache für die Inhaltsgenerierung', 'wp-blog-agent' ); ?>
								</p>
							</div>

							<div class="wbag-form-group">
								<label for="wbag_formal_address">
									<?php esc_html_e( 'Ansprechweise', 'wp-blog-agent' ); ?>
								</label>
								<select
									id="wbag_formal_address"
									name="wbag_settings[formal_address]"
									class="regular-text"
								>
									<option value="sie" <?php selected( isset( $settings['formal_address'] ) ? $settings['formal_address'] : 'sie', 'sie' ); ?>>
										<?php esc_html_e( 'Sie (Formell)', 'wp-blog-agent' ); ?>
									</option>
									<option value="du" <?php selected( isset( $settings['formal_address'] ) ? $settings['formal_address'] : 'sie', 'du' ); ?>>
										<?php esc_html_e( 'Du (Informell)', 'wp-blog-agent' ); ?>
									</option>
								</select>
								<p class="description">
									<?php esc_html_e( 'Wählen Sie die Ansprechweise für den generierten Inhalt', 'wp-blog-agent' ); ?>
								</p>
							</div>
						</div>
					</div>

					<!-- Niche Tab -->
					<div class="wbag-tab-content" id="tab-niche">
						<div class="wbag-card">
							<h2><?php esc_html_e( 'Nische & Themen', 'wp-blog-agent' ); ?> <span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Definieren Sie das thematische Fundament Ihres Blogs. Die KI nutzt Nische, Unterthemen und Zielgruppe, um passende Inhalte zu generieren.', 'wp-blog-agent' ); ?></span></span></h2>

							<div class="wbag-form-group">
								<label for="wbag_niche">
									<?php esc_html_e( 'Hauptnische', 'wp-blog-agent' ); ?>
									<span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Das übergeordnete Thema Ihres Blogs, z. B. "WordPress", "Fitness" oder "Finanzen". Bestimmt die grundlegende Ausrichtung aller Inhalte.', 'wp-blog-agent' ); ?></span></span>
								</label>
								<input
									type="text"
									id="wbag_niche"
									name="wbag_settings[niche]"
									value="<?php echo esc_attr( isset( $settings['niche'] ) ? $settings['niche'] : '' ); ?>"
									class="regular-text"
								/>
								<p class="description">
									<?php esc_html_e( 'Das Hauptthema oder die Branche Ihres Blogs', 'wp-blog-agent' ); ?>
								</p>
							</div>

							<div class="wbag-form-group">
								<label for="wbag_subtopics">
									<?php esc_html_e( 'Unterthemen', 'wp-blog-agent' ); ?>
									<span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Spezifische Bereiche innerhalb Ihrer Nische. Die KI nutzt diese, um abwechslungsreiche und relevante Themen zu finden.', 'wp-blog-agent' ); ?></span></span>
								</label>
								<textarea
									id="wbag_subtopics"
									name="wbag_settings[subtopics]"
									rows="4"
									class="large-text"
									placeholder="<?php esc_attr_e( 'Kommagetrennte Liste von Unterthemen', 'wp-blog-agent' ); ?>"
								><?php echo esc_textarea( isset( $settings['subtopics'] ) ? $settings['subtopics'] : '' ); ?></textarea>
								<p class="description">
									<?php esc_html_e( 'Geben Sie Unterthemen kommagetrennt ein', 'wp-blog-agent' ); ?>
								</p>
							</div>

							<div class="wbag-form-group">
								<label for="wbag_target_audience">
									<?php esc_html_e( 'Zielgruppe', 'wp-blog-agent' ); ?>
									<span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Beschreibung Ihrer Leserschaft: Alter, Beruf, Interessen, Kenntnisstand. Beeinflusst Tonalität und Komplexität der Inhalte.', 'wp-blog-agent' ); ?></span></span>
								</label>
								<textarea
									id="wbag_target_audience"
									name="wbag_settings[target_audience]"
									rows="4"
									class="large-text"
									placeholder="<?php esc_attr_e( 'Beschreiben Sie Ihre Zielgruppe', 'wp-blog-agent' ); ?>"
								><?php echo esc_textarea( isset( $settings['target_audience'] ) ? $settings['target_audience'] : '' ); ?></textarea>
								<p class="description">
									<?php esc_html_e( 'Beschreiben Sie die Demografie und Interessen Ihrer Zielgruppe', 'wp-blog-agent' ); ?>
								</p>
							</div>

							<div class="wbag-form-group">
								<label for="wbag_competitor_urls">
									<?php esc_html_e( 'Konkurrenten-URLs', 'wp-blog-agent' ); ?>
									<span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'URLs von Blogs in Ihrer Nische. Hilft der KI, Themen zu finden, die Ihre Konkurrenz abdeckt — und Lücken zu identifizieren.', 'wp-blog-agent' ); ?></span></span>
								</label>
								<textarea
									id="wbag_competitor_urls"
									name="wbag_settings[competitor_urls]"
									rows="4"
									class="large-text"
									placeholder="<?php esc_attr_e( 'Eine URL pro Zeile', 'wp-blog-agent' ); ?>"
								><?php echo esc_textarea( isset( $settings['competitor_urls'] ) ? $settings['competitor_urls'] : '' ); ?></textarea>
								<p class="description">
									<?php esc_html_e( 'URLs von Konkurrenten zum Benchmarking, eine pro Zeile', 'wp-blog-agent' ); ?>
								</p>
							</div>
						</div>
					</div>

					<!-- Structure Tab -->
					<div class="wbag-tab-content" id="tab-structure">
						<div class="wbag-card">
							<h2><?php esc_html_e( 'Beitragsstruktur', 'wp-blog-agent' ); ?> <span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Steuern Sie Aufbau und Format Ihrer Blogbeiträge: Wortanzahl, FAQ-Fragen, CTA-Buttons, verwandte Beiträge und Inhaltsverzeichnis.', 'wp-blog-agent' ); ?></span></span></h2>

							<div class="wbag-form-group">
								<label for="wbag_post_category">
									<?php esc_html_e( 'Beitragskategorie', 'wp-blog-agent' ); ?>
									<span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Alle automatisch erstellten Beiträge werden dieser WordPress-Kategorie zugewiesen.', 'wp-blog-agent' ); ?></span></span>
								</label>
								<?php
								$categories = get_categories( array( 'hide_empty' => false ) );
								?>
								<select
									id="wbag_post_category"
									name="wbag_settings[post_category]"
									class="regular-text"
								>
									<option value=""><?php esc_html_e( '— Keine Kategorie —', 'wp-blog-agent' ); ?></option>
									<?php foreach ( $categories as $cat ) : ?>
										<option value="<?php echo esc_attr( $cat->slug ); ?>" <?php selected( isset( $settings['post_category'] ) ? $settings['post_category'] : '', $cat->slug ); ?>>
											<?php echo esc_html( $cat->name ); ?>
										</option>
									<?php endforeach; ?>
								</select>
								<p class="description">
									<?php esc_html_e( 'Neue Beiträge werden automatisch dieser Kategorie zugewiesen', 'wp-blog-agent' ); ?>
								</p>
							</div>

							<div class="wbag-form-group wbag-form-group-inline">
								<label for="wbag_word_count_min">
									<?php esc_html_e( 'Minimale Wortanzahl', 'wp-blog-agent' ); ?>
								</label>
								<input
									type="number"
									id="wbag_word_count_min"
									name="wbag_settings[word_count_min]"
									value="<?php echo esc_attr( isset( $settings['word_count_min'] ) ? $settings['word_count_min'] : '1000' ); ?>"
									min="100"
									step="100"
									class="small-text"
								/>
							</div>

							<div class="wbag-form-group wbag-form-group-inline">
								<label for="wbag_word_count_max">
									<?php esc_html_e( 'Maximale Wortanzahl', 'wp-blog-agent' ); ?>
								</label>
								<input
									type="number"
									id="wbag_word_count_max"
									name="wbag_settings[word_count_max]"
									value="<?php echo esc_attr( isset( $settings['word_count_max'] ) ? $settings['word_count_max'] : '3000' ); ?>"
									min="100"
									step="100"
									class="small-text"
								/>
							</div>

							<div class="wbag-form-group wbag-form-group-inline">
								<label for="wbag_faq_count">
									<?php esc_html_e( 'Anzahl der FAQ-Fragen', 'wp-blog-agent' ); ?>
									<span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Wie viele FAQ-Fragen am Ende des Beitrags eingefügt werden sollen. FAQs verbessern die SEO und beantworten häufige Leserfragen.', 'wp-blog-agent' ); ?></span></span>
								</label>
								<input
									type="number"
									id="wbag_faq_count"
									name="wbag_settings[faq_count]"
									value="<?php echo esc_attr( isset( $settings['faq_count'] ) ? $settings['faq_count'] : '5' ); ?>"
									min="0"
									step="1"
									class="small-text"
								/>
							</div>

							<div class="wbag-form-group wbag-form-group-inline">
								<label for="wbag_content_max_width">
									<?php esc_html_e( 'Beitragsbreite (max-width)', 'wp-blog-agent' ); ?>
									<span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Begrenzt die maximale Breite des Beitragsinhalts. Optimal für Lesbarkeit: 700–900 px. Leer = Theme-Standard.', 'wp-blog-agent' ); ?></span></span>
								</label>
								<input
									type="number"
									id="wbag_content_max_width"
									name="wbag_settings[content_max_width]"
									value="<?php echo esc_attr( isset( $settings['content_max_width'] ) ? $settings['content_max_width'] : '' ); ?>"
									min="400"
									max="1600"
									step="10"
									class="small-text"
									placeholder="z.B. 800"
								/>
								<span class="wbag-unit">px</span>
								<p class="description">
									<?php esc_html_e( 'Maximale Breite des Beitragsinhalts in Pixeln. Leer lassen = keine Begrenzung (Theme-Standard). Empfehlung: 700–900 px für optimale Lesbarkeit.', 'wp-blog-agent' ); ?>
								</p>
							</div>

							<div class="wbag-form-group">
								<label for="wbag_cta_text">
									<?php esc_html_e( 'CTA-Text', 'wp-blog-agent' ); ?>
									<span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Text für den Call-to-Action-Button, der am Ende jedes Beitrags eingefügt wird, z. B. "Jetzt anmelden" oder "Mehr erfahren".', 'wp-blog-agent' ); ?></span></span>
								</label>
								<input
									type="text"
									id="wbag_cta_text"
									name="wbag_settings[cta_text]"
									value="<?php echo esc_attr( isset( $settings['cta_text'] ) ? $settings['cta_text'] : '' ); ?>"
									class="regular-text"
									placeholder="<?php esc_attr_e( 'z.B. Jetzt anmelden', 'wp-blog-agent' ); ?>"
								/>
								<p class="description">
									<?php esc_html_e( 'Text für den Call-to-Action Button', 'wp-blog-agent' ); ?>
								</p>
							</div>

							<div class="wbag-form-group">
								<label for="wbag_cta_url">
									<?php esc_html_e( 'CTA-URL', 'wp-blog-agent' ); ?>
								</label>
								<input
									type="url"
									id="wbag_cta_url"
									name="wbag_settings[cta_url]"
									value="<?php echo esc_attr( isset( $settings['cta_url'] ) ? $settings['cta_url'] : '' ); ?>"
									class="regular-text"
									placeholder="https://example.com"
								/>
								<p class="description">
									<?php esc_html_e( 'Die Ziel-URL für den CTA Button', 'wp-blog-agent' ); ?>
								</p>
							</div>

							<div class="wbag-form-group">
								<label for="wbag_cta_color">
									<?php esc_html_e( 'CTA-Farbe', 'wp-blog-agent' ); ?>
								</label>
								<input
									type="color"
									id="wbag_cta_color"
									name="wbag_settings[cta_color]"
									value="<?php echo esc_attr( isset( $settings['cta_color'] ) ? $settings['cta_color'] : '#0073aa' ); ?>"
								/>
								<p class="description">
									<?php esc_html_e( 'Wählen Sie eine Farbe für den CTA Button', 'wp-blog-agent' ); ?>
								</p>
							</div>

							<div class="wbag-form-group wbag-form-group-inline">
								<label for="wbag_related_posts">
									<?php esc_html_e( 'Anzahl der verwandten Beiträge', 'wp-blog-agent' ); ?>
									<span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Anzahl ähnlicher Beiträge, die am Ende des Artikels verlinkt werden. Fördert die interne Verlinkung und Verweildauer.', 'wp-blog-agent' ); ?></span></span>
								</label>
								<input
									type="number"
									id="wbag_related_posts"
									name="wbag_settings[related_posts]"
									value="<?php echo esc_attr( isset( $settings['related_posts'] ) ? $settings['related_posts'] : '3' ); ?>"
									min="0"
									step="1"
									class="small-text"
								/>
							</div>

							<div class="wbag-form-group">
								<label for="wbag_include_toc">
									<input
										type="checkbox"
										id="wbag_include_toc"
										name="wbag_settings[include_toc]"
										value="1"
										<?php checked( isset( $settings['include_toc'] ) && $settings['include_toc'], 1 ); ?>
									/>
									<?php esc_html_e( 'Inhaltsverzeichnis einschließen', 'wp-blog-agent' ); ?>
								</label>
								<p class="description">
									<?php esc_html_e( 'Aktivieren Sie dies, um automatisch ein Inhaltsverzeichnis zu erstellen', 'wp-blog-agent' ); ?>
								</p>
							</div>

							<hr style="margin: 20px 0; border-color: rgba(255,255,255,0.1);" />
							<h3><?php esc_html_e( 'Referenz-Beitrag (Stilvorlage)', 'wp-blog-agent' ); ?> <span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Geben Sie die URL eines bestehenden Beitrags an. Die KI analysiert Stil, Farben und Formatierung und übernimmt diesen Stil für neue Beiträge.', 'wp-blog-agent' ); ?></span></span></h3>
							<p class="description" style="margin-bottom: 12px;">
								<?php esc_html_e( 'Geben Sie die URL eines bestehenden Blogbeitrags an. Der Blog Writer analysiert Stil, Formatierung, Farben und Aufbau und orientiert sich daran bei neuen Beiträgen.', 'wp-blog-agent' ); ?>
							</p>

							<div class="wbag-form-group">
								<label for="wbag_reference_url">
									<?php esc_html_e( 'Referenz-URL', 'wp-blog-agent' ); ?>
								</label>
								<div style="display: flex; gap: 8px; align-items: flex-start;">
									<input
										type="url"
										id="wbag_reference_url"
										name="wbag_settings[reference_url]"
										value="<?php echo esc_attr( isset( $settings['reference_url'] ) ? $settings['reference_url'] : '' ); ?>"
										class="regular-text"
										placeholder="https://www.2fox4.de/beispiel-beitrag/"
									/>
									<button type="button" class="button" id="wbag_analyze_reference">
										<?php esc_html_e( 'Analysieren', 'wp-blog-agent' ); ?>
									</button>
									<span class="spinner" id="wbag_analyze_spinner" style="float: none;"></span>
								</div>
							</div>

							<div id="wbag_reference_analysis_result"></div>

							<?php
							$active_style = isset( $settings['active_style'] ) ? $settings['active_style'] : 'detected';
							$style_slots  = array(
								'detected' => array(
									'key'         => 'reference_style',
									'label_fixed' => __( 'Erkannte Stilvorlage (automatisch)', 'wp-blog-agent' ),
									'placeholder' => __( 'Wird nach der Analyse automatisch ausgefüllt. Du kannst den Text auch manuell bearbeiten.', 'wp-blog-agent' ),
								),
								'custom_1' => array(
									'key'         => 'custom_style_1',
									'label_key'   => 'custom_style_1_label',
									'default'     => __( 'Eigene Stilvorlage 1', 'wp-blog-agent' ),
									'placeholder' => __( 'Beschreibe hier deinen eigenen Wunschstil. Struktur, Design-Elemente, Farben, Typografie, CTA-Stil, Tonalität …', 'wp-blog-agent' ),
								),
								'custom_2' => array(
									'key'         => 'custom_style_2',
									'label_key'   => 'custom_style_2_label',
									'default'     => __( 'Eigene Stilvorlage 2', 'wp-blog-agent' ),
									'placeholder' => __( 'Zweite Variante — z. B. sachlicher oder verspielter als Variante 1.', 'wp-blog-agent' ),
								),
								'custom_3' => array(
									'key'         => 'custom_style_3',
									'label_key'   => 'custom_style_3_label',
									'default'     => __( 'Eigene Stilvorlage 3', 'wp-blog-agent' ),
									'placeholder' => __( 'Dritte Variante — ideal für A/B/C/D-Tests.', 'wp-blog-agent' ),
								),
							);
							?>

							<div class="wbag-form-group">
								<label style="display:block; margin-bottom:8px; font-weight:600;">
									<?php esc_html_e( 'Aktive Stilvorlage für den Blog Writer', 'wp-blog-agent' ); ?>
								</label>
								<p class="description" style="margin-bottom:12px;">
									<?php esc_html_e( 'Du kannst bis zu vier verschiedene Stilvorlagen hinterlegen und hier auswählen, welche beim Schreiben neuer Beiträge verwendet wird. So kannst du verschiedene Stile gegeneinander testen.', 'wp-blog-agent' ); ?>
								</p>
								<div class="wbag-style-selector" style="display:flex; gap:8px; flex-wrap:wrap; margin-bottom:16px;">
									<?php foreach ( $style_slots as $slot_id => $slot ) :
										$is_active = ( $active_style === $slot_id );
										if ( isset( $slot['label_fixed'] ) ) {
											$display_label = $slot['label_fixed'];
										} else {
											$display_label = ! empty( $settings[ $slot['label_key'] ] ) ? $settings[ $slot['label_key'] ] : $slot['default'];
										}
									?>
										<label class="wbag-style-chip<?php echo $is_active ? ' is-active' : ''; ?>" style="flex:1; min-width:180px; padding:10px 14px; border:1px solid rgba(255,255,255,0.15); border-radius:8px; cursor:pointer; display:flex; align-items:center; gap:8px; background:<?php echo $is_active ? 'rgba(255, 107, 53, 0.15)' : 'rgba(255,255,255,0.03)'; ?>; border-color:<?php echo $is_active ? '#FF6B35' : 'rgba(255,255,255,0.15)'; ?>;">
											<input
												type="radio"
												name="wbag_settings[active_style]"
												value="<?php echo esc_attr( $slot_id ); ?>"
												<?php checked( $active_style, $slot_id ); ?>
												class="wbag-style-radio"
												data-slot="<?php echo esc_attr( $slot['key'] ); ?>"
											/>
											<span class="wbag-style-chip-label" data-slot="<?php echo esc_attr( $slot['key'] ); ?>">
												<?php echo esc_html( $display_label ); ?>
											</span>
										</label>
									<?php endforeach; ?>
								</div>
							</div>

							<?php foreach ( $style_slots as $slot_id => $slot ) :
								$textarea_id  = 'wbag_' . $slot['key'];
								$textarea_val = isset( $settings[ $slot['key'] ] ) ? $settings[ $slot['key'] ] : '';
								$is_detected  = isset( $slot['label_fixed'] );
								$slot_hidden  = ( $active_style !== $slot_id );
							?>
								<div class="wbag-form-group wbag-style-slot" data-slot-id="<?php echo esc_attr( $slot_id ); ?>"<?php echo $slot_hidden ? ' style="display:none;"' : ''; ?>>
									<?php if ( $is_detected ) : ?>
										<label for="<?php echo esc_attr( $textarea_id ); ?>">
											<?php echo esc_html( $slot['label_fixed'] ); ?>
										</label>
									<?php else : ?>
										<label for="wbag_<?php echo esc_attr( $slot['label_key'] ); ?>">
											<?php esc_html_e( 'Name dieser Stilvorlage', 'wp-blog-agent' ); ?>
										</label>
										<input
											type="text"
											id="wbag_<?php echo esc_attr( $slot['label_key'] ); ?>"
											name="wbag_settings[<?php echo esc_attr( $slot['label_key'] ); ?>]"
											value="<?php echo esc_attr( isset( $settings[ $slot['label_key'] ] ) ? $settings[ $slot['label_key'] ] : '' ); ?>"
											class="regular-text wbag-style-label-input"
											data-slot="<?php echo esc_attr( $slot['key'] ); ?>"
											placeholder="<?php echo esc_attr( $slot['default'] ); ?>"
											style="margin-bottom:8px; width:100%; max-width:400px;"
										/>
										<label for="<?php echo esc_attr( $textarea_id ); ?>" style="display:block; margin-top:8px;">
											<?php esc_html_e( 'Stilbeschreibung', 'wp-blog-agent' ); ?>
										</label>
									<?php endif; ?>
									<textarea
										id="<?php echo esc_attr( $textarea_id ); ?>"
										name="wbag_settings[<?php echo esc_attr( $slot['key'] ); ?>]"
										class="large-text"
										rows="8"
										placeholder="<?php echo esc_attr( $slot['placeholder'] ); ?>"
									><?php echo esc_textarea( $textarea_val ); ?></textarea>
								</div>
							<?php endforeach; ?>

							<p class="description" style="margin-top:8px;">
								<?php esc_html_e( 'Die oben ausgewählte Stilvorlage wird dem Blog Writer als Formatierungsvorlage mitgegeben. Alle vier Slots bleiben dauerhaft gespeichert — zum Wechseln einfach das Radio-Feld oben umstellen und speichern.', 'wp-blog-agent' ); ?>
							</p>

								<hr style="margin: 30px 0; border: none; border-top: 2px solid #e2e8f0;" />

							<h2><?php esc_html_e( 'Bild-Einstellungen', 'wp-blog-agent' ); ?> <span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Steuern Sie die automatische Bildgenerierung: Stil, Größe, Anzahl und Sprache der Bild-Prompts. Benötigt einen Gemini API-Schlüssel.', 'wp-blog-agent' ); ?></span></span></h2>

							<div class="wbag-form-group">
								<label for="wbag_image_style">
									<?php esc_html_e( 'Bildstil-Prompt', 'wp-blog-agent' ); ?>
									<span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Beschreiben Sie den gewünschten visuellen Stil für KI-generierte Bilder, z. B. "moderner Flat-Design-Stil mit Pastellfarben".', 'wp-blog-agent' ); ?></span></span>
								</label>
								<textarea
									id="wbag_image_style"
									name="wbag_settings[image_style]"
									rows="4"
									class="large-text"
									placeholder="<?php esc_attr_e( 'z.B. modernes Design, realistisch, Stockfoto-Stil', 'wp-blog-agent' ); ?>"
								><?php echo esc_textarea( isset( $settings['image_style'] ) ? $settings['image_style'] : '' ); ?></textarea>
								<p class="description">
									<?php esc_html_e( 'Beschreiben Sie den gewünschten Stil für generierte Bilder', 'wp-blog-agent' ); ?>
								</p>
							</div>

							<div class="wbag-form-group">
								<label for="wbag_image_size">
									<?php esc_html_e( 'Bildgröße', 'wp-blog-agent' ); ?>
								</label>
								<select
									id="wbag_image_size"
									name="wbag_settings[image_size]"
									class="regular-text"
								>
									<option value="1024x572" <?php selected( isset( $settings['image_size'] ) ? $settings['image_size'] : '1024x572', '1024x572' ); ?>>
										<?php esc_html_e( '1024 x 572 (Breitbild)', 'wp-blog-agent' ); ?>
									</option>
									<option value="1024x1024" <?php selected( isset( $settings['image_size'] ) ? $settings['image_size'] : '1024x572', '1024x1024' ); ?>>
										<?php esc_html_e( '1024 x 1024 (Quadratisch)', 'wp-blog-agent' ); ?>
									</option>
									<option value="1200x628" <?php selected( isset( $settings['image_size'] ) ? $settings['image_size'] : '1024x572', '1200x628' ); ?>>
										<?php esc_html_e( '1200 x 628 (Social Media)', 'wp-blog-agent' ); ?>
									</option>
								</select>
								<p class="description">
									<?php esc_html_e( 'Wählen Sie die Größe für generierte Bilder', 'wp-blog-agent' ); ?>
								</p>
							</div>

							<div class="wbag-form-group wbag-form-group-inline">
								<label for="wbag_image_count">
									<?php esc_html_e( 'Anzahl der Bilder pro Beitrag', 'wp-blog-agent' ); ?>
									<span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( '0 = Keine Bilder. 1 = Nur Featured Image (Beitragsbild). 2 = Featured Image + 1 Content-Bild im Text. 3 = Featured Image + 2 Content-Bilder im Text. Jedes Bild verbraucht Gemini-API-Kontingent.', 'wp-blog-agent' ); ?></span></span>
								</label>
								<input
									type="number"
									id="wbag_image_count"
									name="wbag_settings[image_count]"
									value="<?php echo esc_attr( isset( $settings['image_count'] ) ? $settings['image_count'] : '1' ); ?>"
									min="0"
									step="1"
									class="small-text"
								/>
							</div>

							<div class="wbag-form-group">
								<label for="wbag_image_prompt_lang">
									<?php esc_html_e( 'Sprache für Bild-Prompts', 'wp-blog-agent' ); ?>
									<span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'In welcher Sprache die Bild-Beschreibungen an die KI gesendet werden. Englisch liefert oft bessere Ergebnisse.', 'wp-blog-agent' ); ?></span></span>
								</label>
								<select
									id="wbag_image_prompt_lang"
									name="wbag_settings[image_prompt_lang]"
									class="regular-text"
								>
									<option value="en" <?php selected( isset( $settings['image_prompt_lang'] ) ? $settings['image_prompt_lang'] : 'en', 'en' ); ?>>
										<?php esc_html_e( 'English', 'wp-blog-agent' ); ?>
									</option>
									<option value="de" <?php selected( isset( $settings['image_prompt_lang'] ) ? $settings['image_prompt_lang'] : 'en', 'de' ); ?>>
										<?php esc_html_e( 'Deutsch', 'wp-blog-agent' ); ?>
									</option>
								</select>
								<p class="description">
									<?php esc_html_e( 'Sprache für die Bild-Generierungsbefehle', 'wp-blog-agent' ); ?>
								</p>
							</div>
						</div>
					</div>

					<!-- SEO Tab -->
					<div class="wbag-tab-content" id="tab-seo">
						<div class="wbag-card">
							<h2><?php esc_html_e( 'SEO-Einstellungen', 'wp-blog-agent' ); ?> <span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Konfigurieren Sie die SEO-Integration: Welches SEO-Plugin wird verwendet, wie werden Keywords eingesetzt und wie viele interne/externe Links sollen eingefügt werden.', 'wp-blog-agent' ); ?></span></span></h2>

							<div class="wbag-form-group">
								<label for="wbag_seo_plugin">
									<?php esc_html_e( 'SEO-Plugin', 'wp-blog-agent' ); ?>
								</label>
								<select
									id="wbag_seo_plugin"
									name="wbag_settings[seo_plugin]"
									class="regular-text"
								>
									<option value="none" <?php selected( isset( $settings['seo_plugin'] ) ? $settings['seo_plugin'] : 'none', 'none' ); ?>>
										<?php esc_html_e( 'Kein SEO-Plugin', 'wp-blog-agent' ); ?>
									</option>
									<option value="rankmath" <?php selected( isset( $settings['seo_plugin'] ) ? $settings['seo_plugin'] : 'none', 'rankmath' ); ?>>
										<?php esc_html_e( 'Rank Math', 'wp-blog-agent' ); ?>
									</option>
									<option value="yoast" <?php selected( isset( $settings['seo_plugin'] ) ? $settings['seo_plugin'] : 'none', 'yoast' ); ?>>
										<?php esc_html_e( 'Yoast SEO', 'wp-blog-agent' ); ?>
									</option>
									<option value="seopress" <?php selected( isset( $settings['seo_plugin'] ) ? $settings['seo_plugin'] : 'none', 'seopress' ); ?>>
										<?php esc_html_e( 'SEOPress', 'wp-blog-agent' ); ?>
									</option>
								</select>
								<p class="description">
									<?php esc_html_e( 'Wählen Sie das SEO-Plugin, das Sie verwenden', 'wp-blog-agent' ); ?>
								</p>
								<div class="wbag-seo-recommendation" style="margin-top: 12px; padding: 14px 18px; background: linear-gradient(135deg, rgba(107, 78, 255, 0.06), rgba(107, 78, 255, 0.02)); border: 1px solid rgba(107, 78, 255, 0.18); border-radius: 10px;">
									<p style="margin: 0 0 6px; font-weight: 600; color: #6b4eff;">
										<span class="dashicons dashicons-star-filled" style="font-size: 16px; width: 16px; height: 16px; vertical-align: text-bottom; color: #6b4eff;"></span>
										<?php esc_html_e( 'Unsere Empfehlung: Rank Math SEO', 'wp-blog-agent' ); ?>
									</p>
									<p style="margin: 0 0 8px; font-size: 13px; color: #64748b; line-height: 1.6;">
										<?php esc_html_e( 'Rank Math bietet bereits in der kostenlosen Version einen Funktionsumfang, der bei anderen SEO-Plugins erst mit einem kostenpflichtigen Upgrade verfügbar ist — darunter detaillierte Keyword-Analyse, Power-Word-Erkennung und eine umfassende SEO-Bewertung direkt im Editor. In Kombination mit dem Blog Agent erzielen Sie damit die besten Ergebnisse.', 'wp-blog-agent' ); ?>
									</p>
									<a href="https://rankmath.com/?ref=oliverdeppe" target="_blank" rel="noopener nofollow sponsored" style="display: inline-block; margin-top: 4px; padding: 7px 16px; background: #6b4eff; color: #fff; border-radius: 6px; font-size: 13px; font-weight: 600; text-decoration: none; transition: background 0.2s;">
										<?php esc_html_e( 'Rank Math kostenlos herunterladen &rarr;', 'wp-blog-agent' ); ?>
									</a>
									<p style="margin: 10px 0 0; font-size: 11px; color: #94a3b8; line-height: 1.5;">
										<span class="dashicons dashicons-info-outline" style="font-size: 12px; width: 12px; height: 12px; vertical-align: -1px; margin-right: 2px;"></span>
										<?php esc_html_e( 'Transparenzhinweis: Dieser Link ist ein Affiliate-Link. Wenn Sie Rank Math darüber installieren, erhalten wir eine kleine Provision — für Sie entstehen keine Mehrkosten. Wir empfehlen nur Produkte, von denen wir selbst überzeugt sind.', 'wp-blog-agent' ); ?>
									</p>
								</div>

							</div>

							<div class="wbag-form-group">
								<label for="wbag_focus_kw_strategy">
									<?php esc_html_e( 'Fokus-Keyword-Strategie', 'wp-blog-agent' ); ?>
									<span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( '"Einzelnes Keyword" optimiert auf ein Haupt-Keyword. "Mehrere Keywords" streut verwandte Begriffe für breitere Sichtbarkeit.', 'wp-blog-agent' ); ?></span></span>
								</label>
								<select
									id="wbag_focus_kw_strategy"
									name="wbag_settings[focus_kw_strategy]"
									class="regular-text"
								>
									<option value="single" <?php selected( isset( $settings['focus_kw_strategy'] ) ? $settings['focus_kw_strategy'] : 'single', 'single' ); ?>>
										<?php esc_html_e( 'Einzelnes Keyword', 'wp-blog-agent' ); ?>
									</option>
									<option value="multiple" <?php selected( isset( $settings['focus_kw_strategy'] ) ? $settings['focus_kw_strategy'] : 'single', 'multiple' ); ?>>
										<?php esc_html_e( 'Mehrere Keywords', 'wp-blog-agent' ); ?>
									</option>
								</select>
								<p class="description">
									<?php esc_html_e( 'Strategie zur Verwendung von Keywords im Inhalt', 'wp-blog-agent' ); ?>
								</p>
							</div>

							<div class="wbag-form-group wbag-form-group-inline">
								<label for="wbag_internal_links">
									<?php esc_html_e( 'Interne Links pro Beitrag', 'wp-blog-agent' ); ?>
									<span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Anzahl der Links auf andere Beiträge Ihres Blogs. Stärkt die interne Verlinkung und hilft Suchmaschinen bei der Indexierung.', 'wp-blog-agent' ); ?></span></span>
								</label>
								<input
									type="number"
									id="wbag_internal_links"
									name="wbag_settings[internal_links]"
									value="<?php echo esc_attr( isset( $settings['internal_links'] ) ? $settings['internal_links'] : '3' ); ?>"
									min="0"
									step="1"
									class="small-text"
								/>
							</div>

							<div class="wbag-form-group wbag-form-group-inline">
								<label for="wbag_external_links">
									<?php esc_html_e( 'Externe Links pro Beitrag', 'wp-blog-agent' ); ?>
									<span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Anzahl der Links auf externe Quellen. Externe Links zu hochwertigen Quellen können das Suchmaschinen-Ranking verbessern.', 'wp-blog-agent' ); ?></span></span>
								</label>
								<input
									type="number"
									id="wbag_external_links"
									name="wbag_settings[external_links]"
									value="<?php echo esc_attr( isset( $settings['external_links'] ) ? $settings['external_links'] : '2' ); ?>"
									min="0"
									step="1"
									class="small-text"
								/>
							</div>
						</div>
					</div>

					<!-- API Tab -->
					<div class="wbag-tab-content" id="tab-api">
						<div class="wbag-card">
							<h2><?php esc_html_e( 'API-Konfiguration', 'wp-blog-agent' ); ?> <span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Hier hinterlegen Sie Ihre API-Schlüssel für Claude (Textgenerierung) und Gemini (Bildgenerierung). Ohne gültigen API-Schlüssel kann das Plugin keine Inhalte erstellen.', 'wp-blog-agent' ); ?></span></span></h2>

							<div class="wbag-form-group">
								<label for="wbag_api_key">
									<?php esc_html_e( 'Claude API-Schlüssel', 'wp-blog-agent' ); ?>
								</label>
								<input
									type="password"
									id="wbag_api_key"
									name="wbag_settings[api_key]"
									value="<?php echo esc_attr( isset( $settings['api_key'] ) ? $settings['api_key'] : '' ); ?>"
									class="regular-text"
									placeholder="sk-ant-..."
								/>
								<p class="description">
									<?php esc_html_e( 'Geben Sie Ihren Claude API-Schlüssel ein', 'wp-blog-agent' ); ?>
									<br />
									<span class="wbag-api-links">
										<a href="https://console.anthropic.com" target="_blank" rel="noopener noreferrer">
											<span class="dashicons dashicons-admin-network"></span>
											<?php esc_html_e( 'API-Schlüssel erhalten', 'wp-blog-agent' ); ?>
										</a>
										<span class="wbag-api-link-sep">|</span>
										<a href="https://console.anthropic.com/settings/billing" target="_blank" rel="noopener noreferrer" class="wbag-billing-link">
											<span class="dashicons dashicons-chart-area"></span>
											<?php esc_html_e( 'Guthaben & Verbrauch prüfen', 'wp-blog-agent' ); ?>
										</a>
									</span>
								</p>
							</div>

							<div class="wbag-form-group">
								<label for="wbag_api_model">
									<?php esc_html_e( 'Claude-Modell', 'wp-blog-agent' ); ?>
									<span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Sonnet 4.6: Beste Balance aus Qualität und Kosten (empfohlen). Opus 4.6: Höchste Qualität, teurer. Haiku 4.5: Am günstigsten, einfachere Texte.', 'wp-blog-agent' ); ?></span></span>
								</label>
								<select
									id="wbag_api_model"
									name="wbag_settings[api_model]"
									class="regular-text"
								>
									<option value="claude-sonnet-4-6" <?php selected( isset( $settings['api_model'] ) ? $settings['api_model'] : 'claude-sonnet-4-6', 'claude-sonnet-4-6' ); ?>>
										<?php esc_html_e( 'Claude Sonnet 4.6 (Empfohlen)', 'wp-blog-agent' ); ?>
									</option>
									<option value="claude-opus-4-6" <?php selected( isset( $settings['api_model'] ) ? $settings['api_model'] : '', 'claude-opus-4-6' ); ?>>
										<?php esc_html_e( 'Claude Opus 4.6 (Premium)', 'wp-blog-agent' ); ?>
									</option>
									<option value="claude-haiku-4-5-20251001" <?php selected( isset( $settings['api_model'] ) ? $settings['api_model'] : '', 'claude-haiku-4-5-20251001' ); ?>>
										<?php esc_html_e( 'Claude Haiku 4.5 (Günstig)', 'wp-blog-agent' ); ?>
									</option>
								</select>
								<p class="description">
									<?php esc_html_e( 'Wählen Sie das Claude-Modell für die Inhaltsgenerierung', 'wp-blog-agent' ); ?>
								</p>
							</div>

							<div class="wbag-form-group">
								<button
									type="button"
									id="wbag_test_api_button"
									class="button button-primary"
								>
									<?php esc_html_e( 'Verbindung testen', 'wp-blog-agent' ); ?>
								</button>
								<span id="wbag_test_api_spinner" class="spinner" style="display:none;"></span>
								<div id="wbag_test_api_result" style="margin-top:10px;"></div>
							</div>

							<hr style="margin: 20px 0;" />

							<h3><?php esc_html_e( 'Bildgenerierung (Nano Banana 2 / Gemini)', 'wp-blog-agent' ); ?></h3>

							<div class="wbag-form-group">
								<label for="wbag_gemini_api_key">
									<?php esc_html_e( 'Gemini API-Schlüssel', 'wp-blog-agent' ); ?>
								</label>
								<input
									type="password"
									id="wbag_gemini_api_key"
									name="wbag_settings[gemini_api_key]"
									value="<?php echo esc_attr( isset( $settings['gemini_api_key'] ) ? $settings['gemini_api_key'] : '' ); ?>"
									class="regular-text"
									placeholder="AIza..."
								/>
								<p class="description">
									<?php esc_html_e( 'Für die automatische Bildgenerierung mit Nano Banana 2', 'wp-blog-agent' ); ?>
									<br />
									<span class="wbag-api-links">
										<a href="https://aistudio.google.com/apikey" target="_blank" rel="noopener noreferrer">
											<span class="dashicons dashicons-admin-network"></span>
											<?php esc_html_e( 'API-Schlüssel erhalten (kostenlos)', 'wp-blog-agent' ); ?>
										</a>
										<span class="wbag-api-link-sep">|</span>
										<a href="https://aistudio.google.com/billing" target="_blank" rel="noopener noreferrer" class="wbag-billing-link">
											<span class="dashicons dashicons-chart-area"></span>
											<?php esc_html_e( 'Guthaben & Verbrauch prüfen', 'wp-blog-agent' ); ?>
										</a>
									</span>
								</p>
							</div>

								<div class="wbag-form-group">
									<button
										type="button"
										id="wbag_test_gemini_api_button"
										class="button button-primary"
									>
										<?php esc_html_e( 'Verbindung testen', 'wp-blog-agent' ); ?>
									</button>
									<span id="wbag_test_gemini_api_spinner" class="spinner" style="display:none;"></span>
									<div id="wbag_test_gemini_api_result" style="margin-top:10px;"></div>
								</div>
						</div>
					</div>

					<!-- Schedule Tab -->
					<div class="wbag-tab-content" id="tab-schedule">
						<div class="wbag-card">
							<h2>
								<?php esc_html_e( 'Autopilot', 'wp-blog-agent' ); ?>
								<span class="wbag-pro-badge">PRO</span>
								<span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Der Autopilot erstellt automatisch tagesaktuelle Blogbeiträge auf Basis von RSS-Nachrichtenfeeds. Wählen Sie Ihre Quellen, legen Sie den Zeitplan fest — den Rest erledigt die KI.', 'wp-blog-agent' ); ?></span></span>
							</h2>

							<div class="wbag-autopilot-niche-hint" style="margin-bottom: 20px; padding: 12px 16px; background: linear-gradient(135deg, rgba(34, 113, 177, 0.06), rgba(34, 113, 177, 0.02)); border: 1px solid rgba(34, 113, 177, 0.18); border-radius: 10px;">
								<p style="margin: 0; font-size: 13px; color: #1e3a5f; line-height: 1.6;">
									<span class="dashicons dashicons-info-outline" style="font-size: 16px; width: 16px; height: 16px; vertical-align: -2px; margin-right: 4px; color: #2271b1;"></span>
									<?php esc_html_e( 'So funktioniert der Autopilot: Er liest Ihre RSS-Nachrichtenfeeds aus, findet relevante Meldungen zu Ihrer Nische und erstellt daraus automatisch Blogbeiträge. Ihre Einstellungen aus „Nische & Themen" (Nische, Unterthemen, Zielgruppe) bestimmen dabei die inhaltliche Ausrichtung.', 'wp-blog-agent' ); ?>
								</p>
							</div>

							<?php if ( ! wbag_is_pro() ) : ?>
							<div class="wbag-upgrade-notice">
								<span>&#9888;</span>
								<span>
									<?php esc_html_e( 'Der Autopilot ist ein Pro-Feature.', 'wp-blog-agent' ); ?>
									<a href="<?php echo esc_url( function_exists( 'wbag_fs' ) ? wbag_fs()->get_upgrade_url() : 'https://www.2fox4.de/automatischer-ki-wordpress-blog-agent/' ); ?>">
										<?php esc_html_e( 'Jetzt auf Pro upgraden &rarr;', 'wp-blog-agent' ); ?>
									</a>
								</span>
							</div>
							<?php endif; ?>

							<div <?php echo ! wbag_is_pro() ? 'style="opacity:.45;pointer-events:none;"' : ''; ?>>

								<!-- Section 1: RSS Feeds -->
								<h3 style="margin: 0 0 6px; font-size: 14px; color: #1d2327;">
									<span class="dashicons dashicons-rss" style="font-size: 18px; width: 18px; height: 18px; vertical-align: -3px; margin-right: 4px; color: #2271b1;"></span>
									<?php esc_html_e( 'Nachrichtenquellen', 'wp-blog-agent' ); ?>
								</h3>
								<p class="description" style="margin: 0 0 14px; color: #646970;">
									<?php esc_html_e( 'Wählen Sie die RSS-Feeds, aus denen der Autopilot aktuelle Meldungen bezieht. Ohne aktive Feeds kann der Autopilot keine Beiträge erstellen.', 'wp-blog-agent' ); ?>
								</p>

								<div class="wbag-form-group">
									<label><strong><?php esc_html_e( 'Vorgefertigte Feeds', 'wp-blog-agent' ); ?></strong></label>
									<p class="description" style="margin-bottom: 10px;">
										<?php esc_html_e( 'Wählen Sie passende Nachrichtenquellen per Klick aus. Die ausgewählten Feeds werden vom Autopilot vor dem Schreiben abgerufen.', 'wp-blog-agent' ); ?>
									</p>
									<?php
									$feed_groups = array(
										array(
											'label' => __( 'Deutschland', 'wp-blog-agent' ),
											'flag'  => '🇩🇪',
											'feeds' => array(
												array(
													'id'    => 'google_news_de',
													'label' => 'Google News',
													'url'   => 'https://news.google.com/rss?hl=de&gl=DE&ceid=DE:de',
													'icon'  => 'dashicons-google',
												),
												array(
													'id'    => 'tagesschau',
													'label' => 'tagesschau.de',
													'url'   => 'https://www.tagesschau.de/index~rss2.xml',
													'icon'  => 'dashicons-megaphone',
												),
												array(
													'id'    => 'heise',
													'label' => 'heise online',
													'url'   => 'https://www.heise.de/rss/heise-atom.xml',
													'icon'  => 'dashicons-laptop',
												),
												array(
													'id'    => 't3n',
													'label' => 't3n Magazin',
													'url'   => 'https://t3n.de/rss.xml',
													'icon'  => 'dashicons-admin-site-alt3',
												),
												array(
													'id'    => 'golem',
													'label' => 'Golem.de',
													'url'   => 'https://rss.golem.de/rss.php?feed=ATOM1.0',
													'icon'  => 'dashicons-desktop',
												),
												array(
													'id'    => 'spiegel',
													'label' => 'Spiegel Online',
													'url'   => 'https://www.spiegel.de/schlagzeilen/index.rss',
													'icon'  => 'dashicons-media-text',
												),
												array(
													'id'    => 'welt',
													'label' => 'WELT',
													'url'   => 'https://www.welt.de/feeds/latest.rss',
													'icon'  => 'dashicons-admin-site',
												),
											),
										),
										array(
											'label' => __( 'International (EN)', 'wp-blog-agent' ),
											'flag'  => '🇬🇧🇺🇸',
											'feeds' => array(
												array(
													'id'    => 'google_news_en',
													'label' => 'Google News',
													'url'   => 'https://news.google.com/rss?hl=en&gl=US&ceid=US:en',
													'icon'  => 'dashicons-google',
												),
												array(
													'id'    => 'bbc',
													'label' => 'BBC News',
													'url'   => 'https://feeds.bbci.co.uk/news/rss.xml',
													'icon'  => 'dashicons-megaphone',
												),
												array(
													'id'    => 'reuters',
													'label' => 'Reuters',
													'url'   => 'https://www.reutersagency.com/feed/',
													'icon'  => 'dashicons-admin-site',
												),
												array(
													'id'    => 'techcrunch',
													'label' => 'TechCrunch',
													'url'   => 'https://techcrunch.com/feed/',
													'icon'  => 'dashicons-lightbulb',
												),
												array(
													'id'    => 'the_verge',
													'label' => 'The Verge',
													'url'   => 'https://www.theverge.com/rss/index.xml',
													'icon'  => 'dashicons-laptop',
												),
												array(
													'id'    => 'ars_technica',
													'label' => 'Ars Technica',
													'url'   => 'https://feeds.arstechnica.com/arstechnica/index',
													'icon'  => 'dashicons-desktop',
												),
												array(
													'id'    => 'wired',
													'label' => 'WIRED',
													'url'   => 'https://www.wired.com/feed/rss',
													'icon'  => 'dashicons-admin-site-alt3',
												),
												array(
													'id'    => 'hacker_news',
													'label' => 'Hacker News',
													'url'   => 'https://hnrss.org/frontpage',
													'icon'  => 'dashicons-editor-code',
												),
											),
										),
									);
									$selected_feeds = isset( $settings['rss_predefined'] ) && is_array( $settings['rss_predefined'] ) ? $settings['rss_predefined'] : array();
									?>
									<?php foreach ( $feed_groups as $group ) : ?>
										<div class="wbag-rss-group" style="margin-bottom: 16px;">
											<p style="margin: 0 0 8px; font-size: 13px; font-weight: 600; color: #1d2327;">
												<?php echo esc_html( $group['flag'] ); ?> <?php echo esc_html( $group['label'] ); ?>
											</p>
											<div class="wbag-rss-predefined-grid">
												<?php foreach ( $group['feeds'] as $feed ) : ?>
													<label class="wbag-rss-feed-chip <?php echo in_array( $feed['id'], $selected_feeds, true ) ? 'wbag-rss-active' : ''; ?>">
														<input
															type="checkbox"
															name="wbag_settings[rss_predefined][]"
															value="<?php echo esc_attr( $feed['id'] ); ?>"
															<?php checked( in_array( $feed['id'], $selected_feeds, true ) ); ?>
															style="display:none;"
														/>
														<span class="dashicons <?php echo esc_attr( $feed['icon'] ); ?>"></span>
														<?php echo esc_html( $feed['label'] ); ?>
													</label>
												<?php endforeach; ?>
											</div>
										</div>
									<?php endforeach; ?>
								</div>

								<div class="wbag-form-group" style="margin-top: 20px;">
									<label for="wbag_rss_custom"><strong><?php esc_html_e( 'Eigene RSS-Feeds', 'wp-blog-agent' ); ?></strong></label>
									<textarea
										id="wbag_rss_custom"
										name="wbag_settings[rss_custom]"
										rows="4"
										class="large-text"
										placeholder="<?php esc_attr_e( "https://beispiel.de/feed/\nhttps://branchenportal.de/rss.xml", 'wp-blog-agent' ); ?>"
									><?php echo esc_textarea( isset( $settings['rss_custom'] ) ? $settings['rss_custom'] : '' ); ?></textarea>
									<p class="description">
										<?php esc_html_e( 'Geben Sie eigene RSS-Feed-URLs ein, eine pro Zeile. Ideal für Branchenportale, Fachmedien oder Konkurrenz-Blogs.', 'wp-blog-agent' ); ?>
									</p>
								</div>

								<div class="wbag-form-group" style="margin-top: 20px;">
									<label for="wbag_rss_keywords"><strong><?php esc_html_e( 'Schlagwort-Filter', 'wp-blog-agent' ); ?></strong></label>
									<textarea
										id="wbag_rss_keywords"
										name="wbag_settings[rss_keywords]"
										rows="3"
										class="large-text"
										placeholder="<?php esc_attr_e( "WordPress 7\nKI Webdesign\nClaude API", 'wp-blog-agent' ); ?>"
									><?php echo esc_textarea( isset( $settings['rss_keywords'] ) ? $settings['rss_keywords'] : '' ); ?></textarea>
									<p class="description">
										<?php esc_html_e( 'Optional: Ein Suchbegriff pro Zeile oder kommagetrennt. Nur Meldungen, die mindestens einen Begriff enthalten, werden berücksichtigt.', 'wp-blog-agent' ); ?>
										<br>
										<code>WordPress 7</code> — <?php esc_html_e( 'beide Wörter müssen vorkommen', 'wp-blog-agent' ); ?>&nbsp;&nbsp;
										<code>"WordPress 7"</code> — <?php esc_html_e( 'exakte Phrase', 'wp-blog-agent' ); ?>&nbsp;&nbsp;
										<code>WordPress</code> — <?php esc_html_e( 'einfache Teilwort-Suche', 'wp-blog-agent' ); ?>
									</p>
									<span id="wbag-rss-autosave-indicator" style="display:none;font-size:12px;color:#00a32a;margin-top:4px;display:inline-block;"></span>
								</div>

								<div class="wbag-form-group" style="margin-top: 20px;">
									<label for="wbag_rss_notify_email">
										<input
											type="checkbox"
											id="wbag_rss_notify_email"
											name="wbag_settings[rss_notify_email]"
											value="1"
											<?php checked( ! empty( $settings['rss_notify_email'] ) ); ?>
										/>
										<strong><?php esc_html_e( 'E-Mail-Benachrichtigung bei neuem RSS-Beitrag', 'wp-blog-agent' ); ?></strong>
									</label>
									<p class="description" style="margin-left: 24px;">
										<?php
										$admin_email = get_option( 'admin_email' );
										/* translators: %s: admin email address */
										printf(
											esc_html__( 'Sie erhalten eine E-Mail an %s, sobald der Autopilot einen neuen Beitrag auf Basis eines RSS-Feeds als Entwurf erstellt hat. So können Sie den Beitrag zeitnah prüfen und freigeben.', 'wp-blog-agent' ),
											'<code>' . esc_html( $admin_email ) . '</code>'
										);
										?>
									</p>
								</div>

								<!-- Section 2: Zeitplan -->
								<hr style="margin: 28px 0 20px; border: none; border-top: 1px solid #e2e8f0;" />

								<h3 style="margin: 0 0 6px; font-size: 14px; color: #1d2327;">
									<span class="dashicons dashicons-clock" style="font-size: 18px; width: 18px; height: 18px; vertical-align: -3px; margin-right: 4px; color: #2271b1;"></span>
									<?php esc_html_e( 'Zeitplan', 'wp-blog-agent' ); ?>
								</h3>
								<p class="description" style="margin: 0 0 14px; color: #646970;">
									<?php esc_html_e( 'Legen Sie fest, an welchen Tagen und zu welcher Uhrzeit der Autopilot die RSS-Feeds prüft und bei relevanten Meldungen Entwürfe erstellt.', 'wp-blog-agent' ); ?>
								</p>

								<div class="wbag-form-group">
									<label for="wbag_schedule_enabled">
										<input
											type="checkbox"
											id="wbag_schedule_enabled"
											name="wbag_settings[schedule_enabled]"
											value="1"
											<?php checked( isset( $settings['schedule_enabled'] ) && $settings['schedule_enabled'], 1 ); ?>
											<?php disabled( ! wbag_is_pro() ); ?>
										/>
										<?php esc_html_e( 'Beiträge automatisch veröffentlichen', 'wp-blog-agent' ); ?>
									</label>
									<div class="wbag-autopublish-warning" style="margin-top: 8px; padding: 10px 14px; background: linear-gradient(135deg, rgba(234, 88, 12, 0.08), rgba(234, 88, 12, 0.03)); border: 1px solid rgba(234, 88, 12, 0.3); border-radius: 8px;">
										<p style="margin: 0; font-size: 12.5px; color: #9a3412; line-height: 1.6;">
											<span class="dashicons dashicons-warning" style="font-size: 15px; width: 15px; height: 15px; vertical-align: -2px; margin-right: 3px; color: #ea580c;"></span>
											<strong><?php esc_html_e( 'Achtung:', 'wp-blog-agent' ); ?></strong>
											<?php esc_html_e( 'Wenn diese Option aktiv ist, werden KI-generierte Beiträge ohne manuelle Prüfung direkt auf Ihrer Website veröffentlicht. Da KI-Texte gelegentlich inhaltliche Ungenauigkeiten, unpassende Formulierungen oder fehlende Kontextinformationen enthalten können, empfehlen wir dringend, jeden Beitrag vor der Veröffentlichung persönlich zu prüfen. Lassen Sie diese Option deaktiviert, um Beiträge zunächst als Entwurf zu erhalten — so behalten Sie die volle Kontrolle über das, was auf Ihrer Website erscheint.', 'wp-blog-agent' ); ?>
										</p>
									</div>
								</div>

								<div class="wbag-form-group">
									<label><?php esc_html_e( 'Abfragetage', 'wp-blog-agent' ); ?></label>
									<div class="wbag-days-checkboxes">
										<?php
										$days = array(
											'monday'    => __( 'Montag', 'wp-blog-agent' ),
											'tuesday'   => __( 'Dienstag', 'wp-blog-agent' ),
											'wednesday' => __( 'Mittwoch', 'wp-blog-agent' ),
											'thursday'  => __( 'Donnerstag', 'wp-blog-agent' ),
											'friday'    => __( 'Freitag', 'wp-blog-agent' ),
											'saturday'  => __( 'Samstag', 'wp-blog-agent' ),
											'sunday'    => __( 'Sonntag', 'wp-blog-agent' ),
										);
										$selected_days = isset( $settings['schedule_days'] ) ? $settings['schedule_days'] : array();
										foreach ( $days as $day_key => $day_label ) {
											?>
											<label>
												<input
													type="checkbox"
													name="wbag_settings[schedule_days][]"
													value="<?php echo esc_attr( $day_key ); ?>"
													<?php checked( in_array( $day_key, $selected_days, true ), true ); ?>
													<?php disabled( ! wbag_is_pro() ); ?>
												/>
												<?php echo esc_html( $day_label ); ?>
											</label>
											<?php
										}
										?>
									</div>
								</div>

								<div class="wbag-form-group">
									<label for="wbag_schedule_time">
										<?php esc_html_e( 'Abfragezeit', 'wp-blog-agent' ); ?>
									</label>
									<input
										type="time"
										id="wbag_schedule_time"
										name="wbag_settings[schedule_time]"
										value="<?php echo esc_attr( isset( $settings['schedule_time'] ) ? $settings['schedule_time'] : '09:00' ); ?>"
										<?php disabled( ! wbag_is_pro() ); ?>
									/>
									<p class="description">
										<?php esc_html_e( 'Wählen Sie die Uhrzeit, zu der die RSS-Feeds automatisch abgefragt werden', 'wp-blog-agent' ); ?>
									</p>
								</div>

								<div class="wbag-form-group">
									<label for="wbag_batch_size">
										<?php esc_html_e( 'Chargengröße', 'wp-blog-agent' ); ?>
										<span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Wie viele Beiträge pro automatischem Durchlauf gleichzeitig erstellt werden sollen. Höhere Werte = mehr API-Kosten pro Lauf.', 'wp-blog-agent' ); ?></span></span>
									</label>
									<input
										type="number"
										id="wbag_batch_size"
										name="wbag_settings[batch_size]"
										value="<?php echo esc_attr( isset( $settings['batch_size'] ) ? $settings['batch_size'] : '1' ); ?>"
										min="1"
										step="1"
										class="small-text"
										<?php disabled( ! wbag_is_pro() ); ?>
									/>
									<p class="description">
										<?php esc_html_e( 'Anzahl der Beiträge pro Chargenlauf', 'wp-blog-agent' ); ?>
									</p>
								</div>

								<!-- Manual Run Button -->
								<hr style="margin: 28px 0 20px; border: none; border-top: 1px solid #e2e8f0;" />

								<div class="wbag-form-group" style="text-align: center;">
									<button
										type="button"
										id="wbag-run-autopilot-now"
										class="button button-primary button-hero"
										style="background: linear-gradient(135deg, #2271b1, #135e96); border: none; padding: 12px 32px; font-size: 15px; border-radius: 8px; cursor: pointer; display: inline-flex; align-items: center; gap: 8px;"
										<?php disabled( ! wbag_is_pro() ); ?>
									>
										<span class="dashicons dashicons-controls-play" style="font-size: 20px; width: 20px; height: 20px;"></span>
										<?php esc_html_e( 'RSS-Abfrage jetzt starten', 'wp-blog-agent' ); ?>
									</button>
									<p class="description" style="margin-top: 10px;">
										<?php esc_html_e( 'Startet die RSS-Abfrage sofort: Feeds werden geprüft und bei relevanten Meldungen ein Beitrag als Entwurf erstellt. Unabhängig vom eingestellten Zeitplan.', 'wp-blog-agent' ); ?>
									</p>
									<div id="wbag-autopilot-now-result" style="display: none; margin-top: 14px; padding: 12px 16px; border-radius: 8px; font-size: 13px;"></div>
								</div>

							</div><!-- /pro-gated wrapper -->
						</div><!-- /wbag-card -->

						<!-- Autopilot Diagnostics (collapsible) -->
						<details class="wbag-diagnostics-details" style="margin-top:20px;">
							<summary class="wbag-diagnostics-summary" style="cursor: pointer; padding: 12px 16px; background: #f0f6fc; border: 1px solid #c3d8e8; border-left: 4px solid #2271b1; border-radius: 6px; font-size: 14px; font-weight: 600; color: #1d2327; list-style: none; display: flex; align-items: center; gap: 8px; user-select: none;">
								<span class="dashicons dashicons-admin-tools" style="font-size: 18px; width: 18px; height: 18px; color: #2271b1;"></span>
								<?php esc_html_e( 'Autopilot-Diagnose', 'wp-blog-agent' ); ?>
								<span class="wbag-diagnostics-arrow" style="margin-left: auto; transition: transform .2s;">&#9660;</span>
							</summary>
							<div style="padding: 16px; background: #f0f6fc; border: 1px solid #c3d8e8; border-top: none; border-radius: 0 0 6px 6px;">
								<?php
								$diag_next_run     = WBAG_Scheduler::get_next_run();
								$diag_cron_log     = WBAG_Scheduler::get_log();
								$diag_tz           = wp_timezone();
								$diag_disable_cron = defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON;
								$diag_alt_cron     = defined( 'ALTERNATE_WP_CRON' ) && ALTERNATE_WP_CRON;
								?>
								<table class="widefat striped" style="max-width:600px;">
									<tr>
										<td><strong>DISABLE_WP_CRON</strong></td>
										<td><?php echo $diag_disable_cron ? '<span style="color:red;">&#10060; true &mdash; WP-Cron ist deaktiviert!</span>' : '<span style="color:green;">&#9989; false</span>'; ?></td>
									</tr>
									<tr>
										<td><strong>ALTERNATE_WP_CRON</strong></td>
										<td><?php echo $diag_alt_cron ? 'true' : 'false'; ?></td>
									</tr>
									<tr>
										<td><strong>wbag_is_pro()</strong></td>
										<td><?php echo wbag_is_pro() ? '<span style="color:green;">&#9989; true</span>' : '<span style="color:red;">&#10060; false</span>'; ?></td>
									</tr>
									<tr>
										<td><strong><?php esc_html_e( 'Nächster geplanter Lauf', 'wp-blog-agent' ); ?></strong></td>
										<td>
										<?php
										if ( $diag_next_run ) {
											$diag_dt = new DateTime( '@' . $diag_next_run );
											$diag_dt->setTimezone( $diag_tz );
											echo esc_html( $diag_dt->format( 'D, d.m.Y H:i:s T' ) );
										} else {
											echo '<span style="color:red;">&#10060; ' . esc_html__( 'Kein Cron-Event eingeplant!', 'wp-blog-agent' ) . '</span>';
										}
										?>
										</td>
									</tr>
									<tr>
										<td><strong><?php esc_html_e( 'Serverzeit jetzt', 'wp-blog-agent' ); ?></strong></td>
										<td><?php $diag_now = new DateTime( 'now', $diag_tz ); echo esc_html( $diag_now->format( 'D, d.m.Y H:i:s T' ) ); ?></td>
									</tr>
									<tr>
										<td><strong><?php esc_html_e( 'Verfügbare Themen in der Queue', 'wp-blog-agent' ); ?></strong></td>
										<td>
										<?php
										$diag_queue = get_option( 'wbag_topic_queue', array() );
										$diag_pending_n = 0;
										$diag_total_n   = 0;
										if ( is_array( $diag_queue ) ) {
											$diag_total_n = count( $diag_queue );
											foreach ( $diag_queue as $dq_topic ) {
												if ( ! isset( $dq_topic['status'] ) || 'pending' === $dq_topic['status'] ) {
													++$diag_pending_n;
												}
											}
										}
										if ( 0 === $diag_total_n ) {
											echo '<span style="color:orange;">' . esc_html__( 'Queue leer — verwende „Themen finden", um Themen zu generieren.', 'wp-blog-agent' ) . '</span>';
										} elseif ( 0 === $diag_pending_n ) {
											echo '<span style="color:orange;">' . esc_html( $diag_total_n ) . ' ' . esc_html__( 'Themen vorhanden, aber alle bereits verarbeitet. Neue Themen suchen!', 'wp-blog-agent' ) . '</span>';
										} else {
											echo '<span style="color:green;">&#9989; ' . esc_html( $diag_pending_n ) . ' von ' . esc_html( $diag_total_n ) . ' ' . esc_html__( 'bereit zur Verarbeitung', 'wp-blog-agent' ) . '</span>';
										}
										?>
										</td>
									</tr>
								</table>
								<?php if ( ! empty( $diag_cron_log ) ) : ?>
								<h4 style="margin-top:16px;"><?php esc_html_e( 'Cron-Log (letzte Einträge)', 'wp-blog-agent' ); ?></h4>
								<pre style="background:#fff; padding:10px; border:1px solid #ccc; max-height:200px; overflow:auto; font-size:12px;"><?php echo esc_html( wp_json_encode( array_reverse( $diag_cron_log ), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE ) ); ?></pre>
								<?php else : ?>
								<p style="color:orange; margin-top:12px;"><?php esc_html_e( 'Noch keine Cron-Log-Einträge vorhanden.', 'wp-blog-agent' ); ?></p>
								<?php endif; ?>
							</div>
						</details>
					</div>

					<!-- Affiliate Links Tab -->
					<div class="wbag-tab-content" id="tab-affiliate">
						<div class="wbag-card">
							<h2><?php esc_html_e( 'Globale Affiliate-Links', 'wp-blog-agent' ); ?> <span class="wbag-pro-badge">PRO</span> <span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Verwalten Sie hier zentral alle Ihre Affiliate-Partnerschaften. Der Blog Agent wählt automatisch die passenden Links je nach Thema aus und baut sie natürlich in die Beiträge ein.', 'wp-blog-agent' ); ?></span></span></h2>

							<?php if ( ! wbag_is_pro() ) : ?>
							<div class="wbag-upgrade-notice">
								<span>&#9888;</span>
								<span>
									<?php esc_html_e( 'Globale Affiliate-Links sind ein Pro-Feature.', 'wp-blog-agent' ); ?>
									<a href="<?php echo esc_url( function_exists( 'wbag_fs' ) ? wbag_fs()->get_upgrade_url() : 'https://www.2fox4.de/automatischer-ki-wordpress-blog-agent/' ); ?>">
										<?php esc_html_e( 'Jetzt auf Pro upgraden &rarr;', 'wp-blog-agent' ); ?>
									</a>
								</span>
							</div>
							<?php endif; ?>

							<div <?php echo ! wbag_is_pro() ? 'style="opacity:.45;pointer-events:none;"' : ''; ?>>
							<p class="description" style="margin-bottom: 16px;">
								<?php esc_html_e( 'Tragen Sie hier alle Ihre Affiliate-Partner mit Link, Bezeichnung und einer kurzen Beschreibung ein. Bei der Erstellung neuer Blogbeiträge wählt die KI automatisch die thematisch passenden Links aus und integriert sie natürlich in den Text.', 'wp-blog-agent' ); ?>
							</p>

							<?php
							$global_affiliates = isset( $settings['global_affiliate_links'] ) && is_array( $settings['global_affiliate_links'] )
								? $settings['global_affiliate_links']
								: array();
							?>

							<div id="wbag-affiliate-list">
								<?php if ( ! empty( $global_affiliates ) ) : ?>
									<?php foreach ( $global_affiliates as $ai => $aff ) : ?>
										<div class="wbag-affiliate-row" data-index="<?php echo esc_attr( $ai ); ?>">
											<div class="wbag-affiliate-fields">
												<div class="wbag-affiliate-field">
													<label><?php esc_html_e( 'Bezeichnung', 'wp-blog-agent' ); ?></label>
													<input type="text"
														name="wbag_settings[global_affiliate_links][<?php echo esc_attr( $ai ); ?>][label]"
														value="<?php echo esc_attr( $aff['label'] ?? '' ); ?>"
														class="regular-text"
														placeholder="<?php esc_attr_e( 'z.B. Hosting-Anbieter XY', 'wp-blog-agent' ); ?>"
													/>
												</div>
												<div class="wbag-affiliate-field">
													<label><?php esc_html_e( 'Affiliate-URL', 'wp-blog-agent' ); ?></label>
													<input type="url"
														name="wbag_settings[global_affiliate_links][<?php echo esc_attr( $ai ); ?>][url]"
														value="<?php echo esc_attr( $aff['url'] ?? '' ); ?>"
														class="regular-text"
														placeholder="https://partner.example.com/?ref=abc"
													/>
												</div>
												<div class="wbag-affiliate-field wbag-affiliate-field-wide">
													<label><?php esc_html_e( 'Beschreibung / Kategorie', 'wp-blog-agent' ); ?> <span class="wbag-info-icon">?<span class="wbag-tooltip"><?php esc_html_e( 'Beschreiben Sie kurz, worum es bei diesem Partner geht (z.B. "WordPress Hosting", "SEO-Tool"). Die KI nutzt diese Info, um den Link nur bei passenden Themen einzusetzen.', 'wp-blog-agent' ); ?></span></span></label>
													<input type="text"
														name="wbag_settings[global_affiliate_links][<?php echo esc_attr( $ai ); ?>][description]"
														value="<?php echo esc_attr( $aff['description'] ?? '' ); ?>"
														class="regular-text"
														placeholder="<?php esc_attr_e( 'z.B. WordPress Hosting, SEO-Tool, E-Mail-Marketing', 'wp-blog-agent' ); ?>"
													/>
												</div>
											</div>
											<button type="button" class="button wbag-remove-affiliate" title="<?php esc_attr_e( 'Entfernen', 'wp-blog-agent' ); ?>">
												<span class="dashicons dashicons-trash"></span>
											</button>
										</div>
									<?php endforeach; ?>
								<?php endif; ?>
							</div>

							<button type="button" class="button button-secondary" id="wbag-add-affiliate" style="margin-top: 12px;">
								<span class="dashicons dashicons-plus-alt2" style="vertical-align: middle; margin-right: 4px;"></span>
								<?php esc_html_e( 'Affiliate-Partner hinzufügen', 'wp-blog-agent' ); ?>
							</button>

							<p class="description" style="margin-top: 16px;">
								<?php
								/* translators: %d: number of saved affiliate partners */
								printf(
									esc_html__( 'Aktuell %d Affiliate-Partner gespeichert. Alle Links erhalten automatisch rel="nofollow sponsored" und es wird ein Transparenzhinweis eingefügt.', 'wp-blog-agent' ),
									count( $global_affiliates )
								);
								?>
							</p>
							</div><!-- /pro-gate wrapper -->
						</div>
					</div>

					<!-- Save Button -->
					<div class="wbag-card wbag-action-buttons">
						<button type="submit" class="button button-primary button-large">
							<?php esc_html_e( 'Einstellungen speichern', 'wp-blog-agent' ); ?>
						</button>
					</div>
				</form>
			</div>

			<!-- Preview & Revise Modal -->
			<div id="wbag-preview-modal" class="wbag-modal" style="display:none;" aria-hidden="true">
				<div class="wbag-modal-backdrop"></div>
				<div class="wbag-modal-dialog" role="dialog" aria-labelledby="wbag-preview-modal-title">
					<div class="wbag-modal-header">
						<h2 id="wbag-preview-modal-title"><?php esc_html_e( 'Beitrags-Vorschau', 'wp-blog-agent' ); ?></h2>
						<div class="wbag-modal-header-meta">
							<span class="wbag-modal-wordcount"></span>
							<span class="wbag-modal-revision-badge" style="display:none;"></span>
						</div>
						<button type="button" class="wbag-modal-close" aria-label="<?php esc_attr_e( 'Schließen', 'wp-blog-agent' ); ?>">&times;</button>
					</div>
					<div class="wbag-modal-body">
						<div class="wbag-modal-preview-wrapper">
							<h1 class="wbag-modal-preview-title"></h1>
							<div class="wbag-modal-preview-featured" style="display:none;">
								<img src="" alt="" />
							</div>
							<div class="wbag-modal-preview-content"></div>
						</div>
					</div>
					<div class="wbag-modal-footer">
						<div class="wbag-modal-revise-box">
							<label for="wbag-modal-revise-input">
								<strong><?php esc_html_e( 'Änderungen anfordern', 'wp-blog-agent' ); ?></strong>
								<span class="wbag-pro-badge">PRO</span>
							</label>
							<p class="description" style="margin:4px 0 8px;">
								<?php esc_html_e( 'Beschreibe in eigenen Worten, was an diesem Artikel geändert werden soll. Beispiele: "Die Einleitung soll lockerer klingen", "Füge einen Abschnitt über XY hinzu", "Kürze den FAQ-Teil auf 5 Fragen", "Ersetze alle Du-Formen durch Sie".', 'wp-blog-agent' ); ?>
							</p>
							<?php if ( ! wbag_is_pro() ) : ?>
							<div class="wbag-upgrade-notice">
								<span>&#9888;</span>
								<span>
									<?php esc_html_e( 'Die KI-Überarbeitung ist ein Pro-Feature.', 'wp-blog-agent' ); ?>
									<a href="<?php echo esc_url( function_exists( 'wbag_fs' ) ? wbag_fs()->get_upgrade_url() : 'https://www.2fox4.de/automatischer-ki-wordpress-blog-agent/' ); ?>">
										<?php esc_html_e( 'Jetzt upgraden &rarr;', 'wp-blog-agent' ); ?>
									</a>
								</span>
							</div>
							<?php endif; ?>
							<textarea
								id="wbag-modal-revise-input"
								rows="4"
								class="large-text"
								placeholder="<?php esc_attr_e( 'z. B. Mach die Einleitung persönlicher und füge ein konkretes Beispiel hinzu...', 'wp-blog-agent' ); ?>"
								<?php disabled( ! wbag_is_pro() ); ?>
							></textarea>
							<div class="wbag-modal-revise-actions">
								<span class="spinner wbag-modal-spinner"></span>
								<span class="wbag-modal-revise-status"></span>
								<div class="wbag-modal-revise-buttons">
									<a href="#" class="button wbag-modal-edit-link" target="_blank" rel="noopener noreferrer">
										<?php esc_html_e( 'Im WordPress-Editor öffnen', 'wp-blog-agent' ); ?>
									</a>
									<button type="button" class="button button-primary wbag-modal-revise-submit" <?php disabled( ! wbag_is_pro() ); ?>>
										<span class="dashicons <?php echo wbag_is_pro() ? 'dashicons-update' : 'dashicons-lock'; ?>" style="vertical-align:middle;"></span>
										<?php esc_html_e( 'Überarbeiten lassen', 'wp-blog-agent' ); ?>
									</button>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>
		<?php
	}

	/**
	 * Render page header with branding and donate section
	 *
	 * @since 1.0.0
	 */
	private function render_header() {
		$settings = get_option( 'wbag_settings', array() );
		?>
		<div class="wbag-header">
			<div class="wbag-header-top">
				<div class="wbag-header-left">
					<div class="wbag-header-title-row">
						<h1><?php esc_html_e( '2fox4 WP Blog Agent', 'wp-blog-agent' ); ?></h1>
						<span class="wbag-version-badge">v<?php echo esc_html( $this->version ); ?></span>
					</div>
					<p class="wbag-header-subtitle"><?php esc_html_e( 'KI-gesteuerte Blogbeiträge mit automatischer Bildgenerierung', 'wp-blog-agent' ); ?></p>
				</div>
				<div class="wbag-header-donate">
					<span class="wbag-donate-text">
						<?php esc_html_e( 'Gefällt Ihnen das Plugin? Unterstützen Sie die Weiterentwicklung!', 'wp-blog-agent' ); ?>
					</span>
					<a href="https://www.paypal.com/paypalme/oliverdeppe" target="_blank" rel="noopener noreferrer" class="wbag-donate-button">
						<span class="dashicons dashicons-heart"></span>
						<?php esc_html_e( 'Projekt unterstützen', 'wp-blog-agent' ); ?>
					</a>
				</div>
			</div>
		</div>
		<?php
	}

	/**
	 * AJAX handler for testing API connection
	 *
	 * @since 1.0.0
	 */
	public function ajax_test_api() {
		// Check nonce
		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wbag_admin_nonce' ) ) {
			wp_send_json_error( array( 'message' => __( 'Nonce verification failed', 'wp-blog-agent' ) ) );
		}

		// Check capabilities
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'wp-blog-agent' ) ) );
		}

		$settings = get_option( 'wbag_settings', array() );

		// Check if API key is provided
		if ( empty( $settings['api_key'] ) ) {
			wp_send_json_error( array( 'message' => __( 'API key is not configured', 'wp-blog-agent' ) ) );
		}

		// Test API connection
		$api_key = $settings['api_key'];
		$valid_models = array( 'claude-sonnet-4-6', 'claude-opus-4-6', 'claude-haiku-4-5-20251001' );
		$model = ( ! empty( $settings['api_model'] ) && in_array( $settings['api_model'], $valid_models, true ) )
			? $settings['api_model']
			: 'claude-sonnet-4-6';

		// Make a simple test request to Claude API
		$response = wp_remote_post(
			'https://api.anthropic.com/v1/messages',
			array(
				'headers' => array(
					'Content-Type'              => 'application/json',
					'x-api-key'                 => $api_key,
					'anthropic-version'         => '2023-06-01',
				),
				'body'    => wp_json_encode(
					array(
						'model'      => $model,
						'max_tokens' => 100,
						'messages'   => array(
							array(
								'role'    => 'user',
								'content' => 'Say "Connection successful" in one sentence.',
							),
						),
					)
				),
				'timeout' => 30,
			)
		);

		// Handle API response
		if ( is_wp_error( $response ) ) {
			wp_send_json_error( array( 'message' => $response->get_error_message() ) );
		}

		$response_code = wp_remote_retrieve_response_code( $response );
		$response_body = wp_remote_retrieve_body( $response );

		if ( 200 === $response_code ) {
			wp_send_json_success( array( 'message' => __( 'API connection successful!', 'wp-blog-agent' ) ) );
		} else {
			$error_data = json_decode( $response_body, true );
			$error_message = $error_data['error']['message'] ?? __( 'Unknown error', 'wp-blog-agent' );
			wp_send_json_error( array( 'message' => $error_message ) );
		}
	}

	/**
	 * AJAX handler for testing Gemini API connection
	 *
	 * Uses the lightweight models.list endpoint which does not consume
	 * any image-generation quota — it only verifies the API key.
	 *
	 * @since 1.4.1
	 */
	public function ajax_test_gemini_api() {
		// Check nonce.
		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wbag_admin_nonce' ) ) {
			wp_send_json_error( array( 'message' => __( 'Nonce verification failed', 'wp-blog-agent' ) ) );
		}

		// Check capabilities.
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'wp-blog-agent' ) ) );
		}

		$settings = get_option( 'wbag_settings', array() );

		// Check if Gemini API key is provided.
		if ( empty( $settings['gemini_api_key'] ) ) {
			wp_send_json_error( array( 'message' => __( 'Gemini API-Schlüssel ist nicht konfiguriert.', 'wp-blog-agent' ) ) );
		}

		$gemini_key = $settings['gemini_api_key'];

		// Use models.list endpoint — lightweight, no quota cost.
		$response = wp_remote_get(
			'https://generativelanguage.googleapis.com/v1beta/models?key=' . urlencode( $gemini_key ),
			array(
				'headers' => array(
					'Content-Type' => 'application/json',
				),
				'timeout' => 15,
			)
		);

		if ( is_wp_error( $response ) ) {
			wp_send_json_error( array( 'message' => $response->get_error_message() ) );
		}

		$response_code = wp_remote_retrieve_response_code( $response );
		$response_body = wp_remote_retrieve_body( $response );

		if ( 200 === $response_code ) {
			// Check if Imagen models are available.
			$data          = json_decode( $response_body, true );
			$models        = $data['models'] ?? array();
			$imagen_found  = false;
			foreach ( $models as $model ) {
				if ( isset( $model['name'] ) && strpos( $model['name'], 'imagen' ) !== false ) {
					$imagen_found = true;
					break;
				}
			}

			if ( $imagen_found ) {
				wp_send_json_success( array( 'message' => __( 'Gemini API-Verbindung erfolgreich! Imagen-Modelle verfügbar.', 'wp-blog-agent' ) ) );
			} else {
				wp_send_json_success( array( 'message' => __( 'Gemini API-Verbindung erfolgreich! Hinweis: Imagen-Modelle wurden nicht in der Modellliste gefunden — die Bildgenerierung funktioniert möglicherweise trotzdem.', 'wp-blog-agent' ) ) );
			}
		} else {
			$error_data    = json_decode( $response_body, true );
			$error_message = $error_data['error']['message'] ?? __( 'Unbekannter Fehler', 'wp-blog-agent' );
			wp_send_json_error( array( 'message' => $error_message ) );
		}
	}

	/**
	 * AJAX handler for running topic scout
	 *
	 * @since 1.0.0
	 */
	public function ajax_run_topic_scout() {
		// Check nonce
		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wbag_admin_nonce' ) ) {
			wp_send_json_error( array( 'message' => __( 'Nonce verification failed', 'wp-blog-agent' ) ) );
		}

		// Check capabilities
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'wp-blog-agent' ) ) );
		}

		// Pro feature gate: Topic Scout
		if ( ! wbag_is_pro() ) {
			wp_send_json_error( array(
				'message'     => __( 'Der Topic Scout ist ein Pro-Feature. Bitte upgraden auf 2fox4 WP Blog Agent Pro.', 'wp-blog-agent' ),
				'upgrade_url' => function_exists( 'wbag_fs' ) ? wbag_fs()->get_upgrade_url() : 'https://www.2fox4.de/automatischer-ki-wordpress-blog-agent/',
			) );
		}

		$settings = get_option( 'wbag_settings', array() );

		// Override topic_count from inline field if provided
		if ( isset( $_POST['topic_count'] ) ) {
			$inline_count = max( 1, min( 20, intval( $_POST['topic_count'] ) ) );
			$settings['topic_count'] = $inline_count;
			// Persist for next time
			$all_settings = get_option( 'wbag_settings', array() );
			$all_settings['topic_count'] = $inline_count;
			update_option( 'wbag_settings', $all_settings );
		}

		// Validate required settings
		if ( empty( $settings['api_key'] ) || empty( $settings['niche'] ) ) {
			wp_send_json_error( array( 'message' => __( 'Please configure API key and niche first', 'wp-blog-agent' ) ) );
		}

		// Build prompt for topic scout
		$prompt = $this->build_topic_scout_prompt( $settings );

		// Call Claude API
		$topics = $this->call_claude_api( $settings['api_key'], $settings['api_model'] ?? 'claude-sonnet-4-6', $prompt );

		if ( is_wp_error( $topics ) ) {
			wp_send_json_error( array( 'message' => $topics->get_error_message() ) );
		}

		// Store topics in queue
		$topic_queue = get_option( 'wbag_topic_queue', array() );
		if ( ! is_array( $topic_queue ) ) {
			$topic_queue = array();
		}

		// Add new topics to queue
		if ( is_array( $topics ) ) {
			// Add metadata to each topic.
			$now = current_time( 'mysql' );
			foreach ( $topics as &$t ) {
				if ( ! isset( $t['created_at'] ) ) {
					$t['created_at'] = $now;
				}
				if ( ! isset( $t['source'] ) ) {
					$t['source'] = 'topic_scout';
				}
				if ( ! isset( $t['status'] ) ) {
					$t['status'] = 'pending';
				}
			}
			unset( $t );
			$topic_queue = array_merge( $topic_queue, $topics );
			update_option( 'wbag_topic_queue', $topic_queue );
		}

		wp_send_json_success( array(
			'message' => __( 'Topics found and added to queue', 'wp-blog-agent' ),
			'topics'  => $topics,
		) );
	}

	/**
	 * AJAX handler for running blog writer
	 *
	 * @since 1.0.0
	 */
	public function ajax_run_blog_writer() {
		// Check nonce
		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wbag_admin_nonce' ) ) {
			wp_send_json_error( array( 'message' => __( 'Nonce verification failed', 'wp-blog-agent' ) ) );
		}

		// Check capabilities
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'wp-blog-agent' ) ) );
		}

		// Increase PHP execution time and memory for long-running blog + image generation.
		set_time_limit( 600 ); // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- Required for long-running API calls.
		wp_raise_memory_limit( 'image' );
		// Imagen 4 returns large base64 payloads (~2-4 MB per image); ensure PHP has enough memory.
		if ( (int) @ini_get( 'memory_limit' ) < 512 ) {
			@ini_set( 'memory_limit', '512M' ); // phpcs:ignore WordPress.PHP.IniSet.memory_limit_Blacklisted,Squiz.PHP.DiscouragedFunctions.Discouraged
		}

		$settings = get_option( 'wbag_settings', array() );

		// Pick the topic to write.
		// If a specific topic_index is provided (new per-item "Beitrag schreiben" flow),
		// use that item from the queue. Otherwise fall back to the legacy behavior
		// (approved topics first, then queue[0]) for backward compatibility.
		$topic_source = '';
		$topic_index  = isset( $_POST['topic_index'] ) ? intval( $_POST['topic_index'] ) : -1;

		if ( $topic_index >= 0 ) {
			$topic_queue = get_option( 'wbag_topic_queue', array() );
			if ( ! is_array( $topic_queue ) || ! isset( $topic_queue[ $topic_index ] ) ) {
				wp_send_json_error( array( 'message' => __( 'Ausgewähltes Thema nicht mehr in der Warteschlange gefunden. Bitte Seite neu laden.', 'wp-blog-agent' ) ) );
			}
			$topic        = $topic_queue[ $topic_index ];
			$topic_source = 'queue_index';
		} else {
			$approved_topics = get_option( 'wbag_approved_topics', array() );
			if ( ! empty( $approved_topics ) && is_array( $approved_topics ) ) {
				$topic        = $approved_topics[0]; // Peek, don't shift yet.
				$topic_source = 'approved';
			} else {
				$topic_queue = get_option( 'wbag_topic_queue', array() );
				if ( empty( $topic_queue ) ) {
					wp_send_json_error( array( 'message' => __( 'Keine Themen in der Warteschlange. Bitte zuerst „Themen finden" verwenden.', 'wp-blog-agent' ) ) );
				}
				$topic        = $topic_queue[0]; // Peek, don't shift yet.
				$topic_source = 'queue';
			}
		}

		// Ensure the topic has a guaranteed focus keyword (fallback from title if missing).
		$topic['primary_keyword'] = $this->determine_focus_keyword( $topic );

		// Attach internal link candidates so Claude can weave them into the content
		// (fixes RankMath "no internal links" warning). Topic-aware: searches by
		// keyword and category first, then falls back to recent posts.
		$topic['internal_links'] = $this->get_internal_link_candidates( $topic, $settings, 0, 12 );

		// Build prompt for blog writer
		$prompt = $this->build_blog_writer_prompt( $settings, $topic );

		// Call Claude API (raw mode — blog content is HTML, not JSON; 16384 tokens for long posts)
		$blog_content = $this->call_claude_api( $settings['api_key'], $settings['api_model'] ?? 'claude-sonnet-4-6', $prompt, true, 16384 );

		if ( is_wp_error( $blog_content ) ) {
			wp_send_json_error( array( 'message' => $blog_content->get_error_message() ) );
		}

		// Sanitize: Remove <style> blocks that could break WordPress layout
		if ( is_string( $blog_content ) ) {
			$blog_content = preg_replace( '/<style[^>]*>[\s\S]*?<\/style>/i', '', $blog_content );
			// Also remove any <link> stylesheet references
			$blog_content = preg_replace( '/<link[^>]*rel=["\']stylesheet["\'][^>]*>/i', '', $blog_content );
			// Remove markdown code fences if Claude wrapped the HTML
			$blog_content = preg_replace( '/^```(?:html)?\s*/i', '', $blog_content );
			$blog_content = preg_replace( '/\s*```\s*$/i', '', $blog_content );
			$blog_content = trim( $blog_content );
		}

		// Determine category
		$post_category = array();
		$cat_slug = ! empty( $settings['post_category'] ) ? sanitize_title( $settings['post_category'] ) : '';
		if ( ! empty( $cat_slug ) ) {
			$cat = get_category_by_slug( $cat_slug );
			if ( $cat ) {
				$post_category = array( $cat->term_id );
			}
		}

		// Create draft post
		$configured_author = ! empty( $settings['post_author_id'] ) ? absint( $settings['post_author_id'] ) : get_current_user_id();
		$post_args = array(
			'post_title'   => $topic['title'] ?? 'Untitled',
			'post_content' => $blog_content,
			'post_status'  => 'draft',
			'post_author'  => $configured_author,
		);
		if ( ! empty( $post_category ) ) {
			$post_args['post_category'] = $post_category;
		}
		$post_id = wp_insert_post( $post_args );

		if ( is_wp_error( $post_id ) ) {
			wp_send_json_error( array( 'message' => $post_id->get_error_message() ) );
		}

		// Generate SEO metadata (focus keyword, SEO title, meta description)
		// from the finished content and write it to RankMath / Yoast / SEOPress.
		// The focus keyword is FORCED to the one we primed the prompt with,
		// so RankMath finds it in title/content/slug without ambiguity.
		$seo_hint_keyword = isset( $topic['primary_keyword'] ) ? $topic['primary_keyword'] : '';
		$seo_meta = $this->generate_seo_metadata_for_post(
			$topic['title'] ?? '',
			$blog_content,
			$settings,
			$seo_hint_keyword
		);
		if ( ! is_wp_error( $seo_meta ) ) {
			$this->apply_seo_meta_to_post( $post_id, $seo_meta, $settings );
			// Ensure the slug contains the focus keyword (fixes "Keyword not in URL" RankMath error).
			$this->ensure_keyword_in_slug( $post_id, $seo_meta['focus_keyword'] );
			update_post_meta( $post_id, '_wbag_seo_generated_at', current_time( 'mysql' ) );

			// Set tags from SEO metadata.
			if ( ! empty( $seo_meta['tags'] ) ) {
				wp_set_post_tags( $post_id, $seo_meta['tags'] );
			}
		} elseif ( ! empty( $seo_hint_keyword ) ) {
			// Even if Claude failed, still force the slug to contain the hint keyword.
			$this->ensure_keyword_in_slug( $post_id, $seo_hint_keyword );
		}

		// Build a clean 30-word excerpt from the generated content.
		$plain = wp_strip_all_tags( $blog_content );
		$plain = preg_replace( '/\s+/', ' ', trim( $plain ) );
		$words = preg_split( '/\s+/', $plain, 32, PREG_SPLIT_NO_EMPTY );
		if ( count( $words ) > 30 ) {
			$words = array_slice( $words, 0, 30 );
			$excerpt = implode( ' ', $words ) . ' ...';
		} else {
			$excerpt = implode( ' ', $words );
		}
		wp_update_post( array( 'ID' => $post_id, 'post_excerpt' => $excerpt ) );

		// Post created successfully — now remove the topic from storage.
		if ( 'approved' === $topic_source ) {
			$approved_topics = get_option( 'wbag_approved_topics', array() );
			array_shift( $approved_topics );
			update_option( 'wbag_approved_topics', $approved_topics );
		} elseif ( 'queue_index' === $topic_source ) {
			// Re-read the queue and remove by matching title+description to avoid
			// index drift if something else modified the queue in between.
			$topic_queue = get_option( 'wbag_topic_queue', array() );
			if ( isset( $topic_queue[ $topic_index ] )
				&& ( $topic_queue[ $topic_index ]['title'] ?? '' ) === ( $topic['title'] ?? '' ) ) {
				unset( $topic_queue[ $topic_index ] );
				$topic_queue = array_values( $topic_queue );
			} else {
				// Fallback: remove first item with matching title.
				foreach ( $topic_queue as $i => $q ) {
					if ( ( $q['title'] ?? '' ) === ( $topic['title'] ?? '' ) ) {
						unset( $topic_queue[ $i ] );
						$topic_queue = array_values( $topic_queue );
						break;
					}
				}
			}
			update_option( 'wbag_topic_queue', $topic_queue );
		} else {
			$topic_queue = get_option( 'wbag_topic_queue', array() );
			array_shift( $topic_queue );
			update_option( 'wbag_topic_queue', $topic_queue );
		}

		// Generate and attach images if Gemini API key is configured.
		// Register a shutdown handler to catch fatal errors (e.g. memory exhausted)
		// so we can still return a partial success response with the post ID.
		$image_result = array( 'featured_id' => 0, 'content_ids' => array(), 'errors' => array() );
		if ( ! empty( $settings['gemini_api_key'] ) ) {
			// Store post_id for the shutdown handler.
			$GLOBALS['wbag_current_post_id'] = $post_id;
			register_shutdown_function( function() {
				$error = error_get_last();
				if ( $error && in_array( $error['type'], array( E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR ), true ) ) {
					$post_id = $GLOBALS['wbag_current_post_id'] ?? 0;
					// Clear any output buffers.
					while ( ob_get_level() ) {
						ob_end_clean();
					}
					header( 'Content-Type: application/json; charset=utf-8' );
					echo wp_json_encode( array(
						'success' => true,
						'data'    => array(
							'message'       => 'Blog-Beitrag erstellt (Bilder konnten nicht verarbeitet werden: ' . $error['message'] . ')',
							'post_id'       => $post_id,
							'post_title'    => get_the_title( $post_id ),
							'post_url'      => get_permalink( $post_id ),
							'post_edit_url' => get_edit_post_link( $post_id, 'raw' ),
							'images'        => array(
								'featured_id' => 0,
								'content_ids' => array(),
								'errors'      => array( $error['message'] ),
							),
						),
					) );
				}
			} );

			$image_result = $this->generate_and_attach_images(
				$post_id,
				$topic['title'] ?? 'Untitled',
				$blog_content,
				$settings
			);
			unset( $GLOBALS['wbag_current_post_id'] );
		}

		$image_msg = '';
		if ( $image_result['featured_id'] ) {
			$img_count = 1 + count( $image_result['content_ids'] );
			$image_msg = sprintf(
				/* translators: %d: number of images */
				__( ' + %d Bilder generiert', 'wp-blog-agent' ),
				$img_count
			);
		}
		if ( ! empty( $image_result['errors'] ) ) {
			$image_msg .= ' (' . implode( '; ', $image_result['errors'] ) . ')';
		}

		wp_send_json_success( array(
			'message'       => __( 'Blog-Beitrag als Entwurf erstellt', 'wp-blog-agent' ) . $image_msg,
			'post_id'       => $post_id,
			'post_title'    => get_the_title( $post_id ),
			'post_url'      => get_permalink( $post_id ),
			'post_edit_url' => get_edit_post_link( $post_id, 'raw' ),
			'images'        => array(
				'featured_id' => $image_result['featured_id'],
				'content_ids' => $image_result['content_ids'],
				'errors'      => $image_result['errors'],
			),
		) );
	}

	/**
	 * AJAX handler: return the current rendered content of a draft post so
	 * the user can preview it in a modal before going into the WP editor.
	 *
	 * @since 1.3.14
	 */
	public function ajax_preview_post() {
		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wbag_admin_nonce' ) ) {
			wp_send_json_error( array( 'message' => __( 'Nonce verification failed', 'wp-blog-agent' ) ) );
		}
		if ( ! current_user_can( 'edit_posts' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'wp-blog-agent' ) ) );
		}

		$post_id = isset( $_POST['post_id'] ) ? absint( $_POST['post_id'] ) : 0;
		if ( ! $post_id ) {
			wp_send_json_error( array( 'message' => __( 'Ungültige Post-ID.', 'wp-blog-agent' ) ) );
		}

		$post = get_post( $post_id );
		if ( ! $post ) {
			wp_send_json_error( array( 'message' => __( 'Beitrag nicht gefunden.', 'wp-blog-agent' ) ) );
		}

		$featured_id  = get_post_thumbnail_id( $post_id );
		$featured_url = $featured_id ? wp_get_attachment_image_url( $featured_id, 'large' ) : '';

		wp_send_json_success( array(
			'post_id'      => $post_id,
			'title'        => get_the_title( $post_id ),
			'content'      => apply_filters( 'the_content', $post->post_content ), // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Core WP filter.
			'raw_content'  => $post->post_content,
			'edit_url'     => get_edit_post_link( $post_id, 'raw' ),
			'preview_url'  => get_preview_post_link( $post_id ),
			'featured_url' => $featured_url,
			'word_count'   => str_word_count( wp_strip_all_tags( $post->post_content ) ),
		) );
	}

	/**
	 * AJAX handler: let the user ask Claude for targeted revisions on an
	 * already-written draft. Sends the current HTML + the user's change
	 * instructions to Claude and overwrites the post with the revised HTML.
	 *
	 * @since 1.3.14
	 */
	public function ajax_revise_post() {
		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wbag_admin_nonce' ) ) {
			wp_send_json_error( array( 'message' => __( 'Nonce verification failed', 'wp-blog-agent' ) ) );
		}
		if ( ! current_user_can( 'edit_posts' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'wp-blog-agent' ) ) );
		}

		// Pro feature gate: KI-Überarbeitung
		if ( ! wbag_is_pro() ) {
			wp_send_json_error( array(
				'message'     => __( 'Die KI-Überarbeitung ist ein Pro-Feature. Bitte upgraden auf 2fox4 WP Blog Agent Pro.', 'wp-blog-agent' ),
				'upgrade_url' => function_exists( 'wbag_fs' ) ? wbag_fs()->get_upgrade_url() : 'https://www.2fox4.de/automatischer-ki-wordpress-blog-agent/',
			) );
		}

		$post_id      = isset( $_POST['post_id'] ) ? absint( $_POST['post_id'] ) : 0;
		$instructions = isset( $_POST['instructions'] ) ? trim( wp_kses_post( wp_unslash( $_POST['instructions'] ) ) ) : '';

		if ( ! $post_id ) {
			wp_send_json_error( array( 'message' => __( 'Ungültige Post-ID.', 'wp-blog-agent' ) ) );
		}
		if ( '' === $instructions ) {
			wp_send_json_error( array( 'message' => __( 'Bitte beschreibe, was geändert werden soll.', 'wp-blog-agent' ) ) );
		}

		$post = get_post( $post_id );
		if ( ! $post ) {
			wp_send_json_error( array( 'message' => __( 'Beitrag nicht gefunden.', 'wp-blog-agent' ) ) );
		}

		$settings = get_option( 'wbag_settings', array() );
		if ( empty( $settings['api_key'] ) ) {
			wp_send_json_error( array( 'message' => __( 'Claude API-Key fehlt in den Einstellungen.', 'wp-blog-agent' ) ) );
		}

		// Pull focus keyword from SEO meta so we can instruct Claude to keep it.
		$seo_plugin    = isset( $settings['seo_plugin'] ) ? $settings['seo_plugin'] : 'none';
		$focus_keyword = '';
		if ( 'rankmath' === $seo_plugin ) {
			$focus_keyword = (string) get_post_meta( $post_id, 'rank_math_focus_keyword', true );
		} elseif ( 'yoast' === $seo_plugin ) {
			$focus_keyword = (string) get_post_meta( $post_id, '_yoast_wpseo_focuskw', true );
		} elseif ( 'seopress' === $seo_plugin ) {
			$focus_keyword = (string) get_post_meta( $post_id, '_seopress_analysis_target_kw', true );
		}

		$title       = get_the_title( $post_id );
		$current_html = $post->post_content;

		$prompt = "Du bist ein Profi-Redakteur und überarbeitest einen bereits geschriebenen deutschen Blog-Artikel im HTML-Format.\n\n"
			. "=== AKTUELLER TITEL ===\n" . $title . "\n\n";

		if ( ! empty( $focus_keyword ) ) {
			$prompt .= "=== FOKUS-KEYWORD (MUSS ERHALTEN BLEIBEN) ===\n"
				. $focus_keyword . "\n"
				. "Dieses Keyword muss weiterhin in H1 (falls vorhanden), in den ersten 10 Wörtern des Fließtexts, mindestens einer H2 vorkommen.\n"
				. "Keyword-Dichte: Das Keyword muss ca. 1–1,5 % der Gesamtwörter ausmachen (bei 2000 Wörtern = 20–30 Mal). Zähle die Vorkommen und ergänze ggf. welche an natürlichen Stellen.\n\n";
		}

		$prompt .= "=== AKTUELLER HTML-INHALT ===\n" . $current_html . "\n\n"
			. "=== GEWÜNSCHTE ÄNDERUNGEN (vom Nutzer) ===\n" . $instructions . "\n\n"
			. "=== DEINE AUFGABE ===\n"
			. "1. Wende die oben genannten Änderungswünsche sorgfältig auf den Artikel an.\n"
			. "2. Erhalte die HTML-Struktur: Überschriften-Hierarchie (H1/H2/H3), Listen, Tabellen, Boxen, interne Links und Call-to-Actions.\n"
			. "3. FAQ-/Akkordeon-Bereiche (z. B. Elemente mit Klassen wie faq-item, faq-question, faq-answer, accordion usw.) DÜRFEN und SOLLEN inhaltlich bearbeitet werden, wenn der Nutzer das verlangt. "
			. "Behalte dabei exakt die HTML-Struktur (div/button/span-Verschachtelung, CSS-Klassen, aria-Attribute, Icons), aber ändere den Textinhalt gemäß den Anweisungen.\n"
			. "4. Behalte die ungefähre Länge (± 20 %) und den Stil des Originals.\n"
			. "5. Keine Meta-Kommentare, keine Erklärungen, keine Markdown-Code-Fences — gib AUSSCHLIESSLICH den kompletten überarbeiteten HTML-Body zurück.\n"
			. "6. Wenn eine Änderung in Konflikt mit der Keyword-Regel steht, priorisiere das Keyword und passe die Änderung entsprechend an.\n";

		$revised_html = $this->call_claude_api(
			$settings['api_key'],
			$settings['api_model'] ?? 'claude-sonnet-4-6',
			$prompt,
			true, // raw
			16384
		);

		if ( is_wp_error( $revised_html ) ) {
			wp_send_json_error( array( 'message' => $revised_html->get_error_message() ) );
		}

		// Strip markdown fences if Claude wrapped the HTML.
		$revised_html = preg_replace( '/^```(?:html)?\s*/i', '', $revised_html );
		$revised_html = preg_replace( '/\s*```\s*$/i', '', $revised_html );
		$revised_html = trim( $revised_html );

		if ( '' === $revised_html ) {
			wp_send_json_error( array( 'message' => __( 'Claude hat eine leere Antwort zurückgegeben.', 'wp-blog-agent' ) ) );
		}

		$update_result = wp_update_post( array(
			'ID'           => $post_id,
			'post_content' => $revised_html,
		), true );

		if ( is_wp_error( $update_result ) ) {
			wp_send_json_error( array( 'message' => $update_result->get_error_message() ) );
		}

		// Track revision count for diagnostics.
		$rev_count = (int) get_post_meta( $post_id, '_wbag_revision_count', true );
		update_post_meta( $post_id, '_wbag_revision_count', $rev_count + 1 );
		update_post_meta( $post_id, '_wbag_last_revision_at', current_time( 'mysql' ) );

		wp_send_json_success( array(
			'post_id'       => $post_id,
			'title'         => get_the_title( $post_id ),
			'content'       => apply_filters( 'the_content', $revised_html ), // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Core WP filter.
			'raw_content'   => $revised_html,
			'word_count'    => str_word_count( wp_strip_all_tags( $revised_html ) ),
			'revision_num'  => $rev_count + 1,
			'message'       => __( 'Überarbeitung erfolgreich angewendet.', 'wp-blog-agent' ),
		) );
	}

	/**
	 * AJAX handler for deleting a custom topic template from history.
	 *
	 * @since 1.8.1
	 */
	public function ajax_delete_custom_template() {
		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wbag_admin_nonce' ) ) {
			wp_send_json_error( array( 'message' => __( 'Nonce verification failed', 'wp-blog-agent' ) ) );
		}

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'wp-blog-agent' ) ) );
		}

		$index = isset( $_POST['template_index'] ) ? intval( $_POST['template_index'] ) : -1;
		$history = get_option( 'wbag_custom_topic_history', array() );

		if ( ! is_array( $history ) || ! isset( $history[ $index ] ) ) {
			wp_send_json_error( array( 'message' => __( 'Vorlage nicht gefunden.', 'wp-blog-agent' ) ) );
		}

		unset( $history[ $index ] );
		$history = array_values( $history ); // Re-index.
		update_option( 'wbag_custom_topic_history', $history );

		wp_send_json_success( array(
			'message' => __( 'Vorlage entfernt.', 'wp-blog-agent' ),
			'count'   => count( $history ),
		) );
	}

		/**
	 * AJAX handler for writing a blog post with a custom topic and optional affiliate links.
	 *
	 * @since 1.2.0
	 */
	public function ajax_write_custom_topic() {
		// Check nonce.
		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wbag_admin_nonce' ) ) {
			wp_send_json_error( array( 'message' => __( 'Nonce verification failed', 'wp-blog-agent' ) ) );
		}

		// Check capabilities.
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'wp-blog-agent' ) ) );
		}

		// Validate required field.
		$custom_title = isset( $_POST['custom_title'] ) ? sanitize_text_field( wp_unslash( $_POST['custom_title'] ) ) : '';
		if ( empty( $custom_title ) ) {
			wp_send_json_error( array( 'message' => __( 'Bitte geben Sie ein Thema ein.', 'wp-blog-agent' ) ) );
		}

		$custom_description = isset( $_POST['custom_description'] ) ? sanitize_textarea_field( wp_unslash( $_POST['custom_description'] ) ) : '';
		$custom_keyword     = isset( $_POST['custom_keyword'] ) ? sanitize_text_field( wp_unslash( $_POST['custom_keyword'] ) ) : '';

				// Increase PHP execution time and memory.
		set_time_limit( 600 ); // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- Required for long-running API calls.
		wp_raise_memory_limit( 'image' );
		if ( (int) @ini_get( 'memory_limit' ) < 512 ) {
			@ini_set( 'memory_limit', '512M' ); // phpcs:ignore WordPress.PHP.IniSet.memory_limit_Blacklisted,Squiz.PHP.DiscouragedFunctions.Discouraged
		}

		$settings = get_option( 'wbag_settings', array() );

		if ( empty( $settings['api_key'] ) ) {
			wp_send_json_error( array( 'message' => __( 'Claude API-Schlüssel fehlt. Bitte konfigurieren Sie ihn im API-Tab.', 'wp-blog-agent' ) ) );
		}

		// Build a topic array that matches the structure expected by build_blog_writer_prompt.
		$topic = array(
			'title'           => $custom_title,
			'description'     => $custom_description,
			'primary_keyword' => $custom_keyword,
		);

		// Ensure the topic has a guaranteed focus keyword (fallback from title if missing).
		$topic['primary_keyword'] = $this->determine_focus_keyword( $topic );
		$custom_keyword           = $topic['primary_keyword'];

		// Attach internal link candidates so Claude can weave them into the content.
		// Topic-aware: searches by keyword and category first.
		$topic['internal_links'] = $this->get_internal_link_candidates( $topic, $settings, 0, 12 );

		// Build prompt — with affiliate link instructions appended.
		$prompt = $this->build_blog_writer_prompt( $settings, $topic );

		// Call Claude API.
		$blog_content = $this->call_claude_api( $settings['api_key'], $settings['api_model'] ?? 'claude-sonnet-4-6', $prompt, true, 16384 );

		if ( is_wp_error( $blog_content ) ) {
			wp_send_json_error( array( 'message' => $blog_content->get_error_message() ) );
		}

		// Sanitize content.
		if ( is_string( $blog_content ) ) {
			$blog_content = preg_replace( '/<style[^>]*>[\s\S]*?<\/style>/i', '', $blog_content );
			$blog_content = preg_replace( '/<link[^>]*rel=["\']stylesheet["\'][^>]*>/i', '', $blog_content );
			$blog_content = preg_replace( '/^```(?:html)?\s*/i', '', $blog_content );
			$blog_content = preg_replace( '/\s*```\s*$/i', '', $blog_content );
			$blog_content = trim( $blog_content );
		}

		// Determine category.
		$post_category = array();
		$cat_slug = ! empty( $settings['post_category'] ) ? sanitize_title( $settings['post_category'] ) : '';
		if ( ! empty( $cat_slug ) ) {
			$cat = get_category_by_slug( $cat_slug );
			if ( $cat ) {
				$post_category = array( $cat->term_id );
			}
		}

		// Create draft post.
		$configured_author = ! empty( $settings['post_author_id'] ) ? absint( $settings['post_author_id'] ) : get_current_user_id();
		$post_args = array(
			'post_title'   => $custom_title,
			'post_content' => $blog_content,
			'post_status'  => 'draft',
			'post_author'  => $configured_author,
		);
		if ( ! empty( $post_category ) ) {
			$post_args['post_category'] = $post_category;
		}
		$post_id = wp_insert_post( $post_args );

		if ( is_wp_error( $post_id ) ) {
			wp_send_json_error( array( 'message' => $post_id->get_error_message() ) );
		}




		// Save to custom topic template history immediately (max 30 entries).
		// Done before SEO/images so it persists even if those fail.
		$tpl_history = get_option( 'wbag_custom_topic_history', array() );
		if ( ! is_array( $tpl_history ) ) {
			$tpl_history = array();
		}
		array_unshift( $tpl_history, array(
			'title'       => $custom_title,
			'description' => $custom_description,
			'keyword'     => $custom_keyword,
			'post_id'     => $post_id,
			'created_at'  => current_time( 'mysql' ),
		) );
		$tpl_history = array_slice( $tpl_history, 0, 30 );
		update_option( 'wbag_custom_topic_history', $tpl_history );

		// Generate SEO metadata and write it to the active SEO plugin.
		// Focus keyword is forced to $custom_keyword, so RankMath finds it reliably.
		$seo_meta = $this->generate_seo_metadata_for_post(
			$custom_title,
			$blog_content,
			$settings,
			$custom_keyword
		);
		if ( ! is_wp_error( $seo_meta ) ) {
			$this->apply_seo_meta_to_post( $post_id, $seo_meta, $settings );
			$this->ensure_keyword_in_slug( $post_id, $seo_meta['focus_keyword'] );
			update_post_meta( $post_id, '_wbag_seo_generated_at', current_time( 'mysql' ) );

			// Set tags from SEO metadata.
			if ( ! empty( $seo_meta['tags'] ) ) {
				wp_set_post_tags( $post_id, $seo_meta['tags'] );
			}
		} elseif ( ! empty( $custom_keyword ) ) {
			$this->ensure_keyword_in_slug( $post_id, $custom_keyword );
		}

		// Build a clean 30-word excerpt from the generated content.
		$plain_ex = wp_strip_all_tags( $blog_content );
		$plain_ex = preg_replace( '/\s+/', ' ', trim( $plain_ex ) );
		$words_ex = preg_split( '/\s+/', $plain_ex, 32, PREG_SPLIT_NO_EMPTY );
		if ( count( $words_ex ) > 30 ) {
			$words_ex = array_slice( $words_ex, 0, 30 );
			$excerpt_text = implode( ' ', $words_ex ) . ' ...';
		} else {
			$excerpt_text = implode( ' ', $words_ex );
		}
		wp_update_post( array( 'ID' => $post_id, 'post_excerpt' => $excerpt_text ) );

		// Generate and attach images if Gemini API key is configured.
		$image_result = array( 'featured_id' => 0, 'content_ids' => array(), 'errors' => array() );
		if ( ! empty( $settings['gemini_api_key'] ) ) {
			$GLOBALS['wbag_current_post_id'] = $post_id;
			register_shutdown_function( function() {
				$error = error_get_last();
				if ( $error && in_array( $error['type'], array( E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR ), true ) ) {
					$pid = $GLOBALS['wbag_current_post_id'] ?? 0;
					while ( ob_get_level() ) {
						ob_end_clean();
					}
					header( 'Content-Type: application/json; charset=utf-8' );
					echo wp_json_encode( array(
						'success' => true,
						'data'    => array(
							'message'       => 'Blog-Beitrag erstellt (Bilder konnten nicht verarbeitet werden: ' . $error['message'] . ')',
							'post_id'       => $pid,
							'post_title'    => get_the_title( $pid ),
							'post_url'      => get_permalink( $pid ),
							'post_edit_url' => get_edit_post_link( $pid, 'raw' ),
							'images'        => array( 'featured_id' => 0, 'content_ids' => array(), 'errors' => array( $error['message'] ) ),
						),
					) );
				}
			} );

			$image_result = $this->generate_and_attach_images(
				$post_id,
				$custom_title,
				$blog_content,
				$settings
			);
			unset( $GLOBALS['wbag_current_post_id'] );
		}

		$image_msg = '';
		if ( $image_result['featured_id'] ) {
			$img_count = 1 + count( $image_result['content_ids'] );
			/* translators: %d: number of generated images */
			$image_msg = sprintf( __( ' + %d Bilder generiert', 'wp-blog-agent' ), $img_count );
		}
		if ( ! empty( $image_result['errors'] ) ) {
			$image_msg .= ' (' . implode( '; ', $image_result['errors'] ) . ')';
		}

		wp_send_json_success( array(
			'message'       => __( 'Blog-Beitrag als Entwurf erstellt', 'wp-blog-agent' ) . $image_msg,
			'post_id'       => $post_id,
			'post_title'    => get_the_title( $post_id ),
			'post_url'      => get_permalink( $post_id ),
			'post_edit_url' => get_edit_post_link( $post_id, 'raw' ),
			'images'        => array(
				'featured_id' => $image_result['featured_id'],
				'content_ids' => $image_result['content_ids'],
				'errors'      => $image_result['errors'],
			),
		) );
	}

	/**
	 * Create a blog post in autopilot mode (called by the Scheduler).
	 *
	 * Replicates the ajax_write_custom_topic flow but without AJAX context.
	 * Generates a topic via Topic Scout (or uses the provided one), writes
	 * the post, generates SEO metadata, and attaches images.
	 *
	 * @since 1.3.22
	 *
	 * @param array|null $topic Optional topic array with 'title', 'description', 'primary_keyword'.
	 *                          If null, picks a pending topic from the queue or runs Topic Scout.
	 * @return array|WP_Error   Array with 'post_id', 'title' on success, WP_Error on failure.
	 */
	/**
	 * Fetch recent items from configured RSS feeds.
	 *
	 * Reads both predefined and custom feeds from settings and returns
	 * a flat array of news items: [ ['title' => ..., 'link' => ..., 'summary' => ..., 'source' => ...], ... ]
	 *
	 * @param array $settings Plugin settings.
	 * @param int   $max_items Maximum total items to return.
	 * @return array
	 * @since 1.3.51
	 */
	/**
	 * AJAX: Run autopilot manually (outside of cron schedule).
	 *
	 * Triggers the same create_post_autopilot() logic that the cron scheduler uses,
	 * but can be fired at any time via the admin UI button.
	 *
	 * @since 1.3.59
	 */
	public function ajax_run_autopilot_now() {
		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wbag_admin_nonce' ) ) {
			wp_send_json_error( array( 'message' => __( 'Nonce verification failed', 'wp-blog-agent' ) ) );
		}
		if ( ! current_user_can( 'edit_posts' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'wp-blog-agent' ) ) );
		}
		if ( ! wbag_is_pro() ) {
			wp_send_json_error( array(
				'message'     => __( 'Der Autopilot ist ein Pro-Feature.', 'wp-blog-agent' ),
				'upgrade_url' => function_exists( 'wbag_fs' ) ? wbag_fs()->get_upgrade_url() : 'https://www.2fox4.de/automatischer-ki-wordpress-blog-agent/',
			) );
		}

		$settings = get_option( 'wbag_settings', array() );

		// Check that at least one RSS feed is configured.
		$has_predefined = ! empty( $settings['rss_predefined'] ) && is_array( $settings['rss_predefined'] ) && count( $settings['rss_predefined'] ) > 0;
		$has_custom     = ! empty( $settings['rss_custom'] ) && trim( $settings['rss_custom'] ) !== '';
		if ( ! $has_predefined && ! $has_custom ) {
			wp_send_json_error( array( 'message' => __( 'Keine RSS-Feeds konfiguriert. Bitte wählen Sie mindestens einen Feed aus.', 'wp-blog-agent' ) ) );
		}

		// Check API key.
		if ( empty( $settings['api_key'] ) ) {
			wp_send_json_error( array( 'message' => __( 'Claude API-Key fehlt in den Einstellungen.', 'wp-blog-agent' ) ) );
		}

		// If the user pre-selected a specific RSS item, pass it through directly.
		$selected_item = null;
		if ( ! empty( $_POST['selected_item'] ) ) {
			$raw_item = json_decode( wp_unslash( $_POST['selected_item'] ), true );
			if ( is_array( $raw_item ) && ! empty( $raw_item['title'] ) ) {
				$selected_item = $raw_item;
			}
		}

		// Run the autopilot with force_fresh = true so we skip the old queue
		// and generate a brand-new topic based on current RSS headlines.
		$result = $this->create_post_autopilot( null, true, $selected_item );

		if ( is_wp_error( $result ) ) {
			wp_send_json_error( array( 'message' => $result->get_error_message() ) );
		}

		$post_status = get_post_status( $result['post_id'] );
		$status_label = ( 'publish' === $post_status )
			? __( 'veröffentlicht', 'wp-blog-agent' )
			: __( 'als Entwurf erstellt', 'wp-blog-agent' );
		$edit_url = get_edit_post_link( $result['post_id'], 'raw' );

		wp_send_json_success( array(
			'message'  => sprintf(
				/* translators: 1: post title, 2: status label */
				__( 'Beitrag „%1$s" wurde %2$s.', 'wp-blog-agent' ),
				$result['title'],
				$status_label
			),
			'post_id'  => $result['post_id'],
			'title'    => $result['title'],
			'edit_url' => $edit_url,
		) );
	}

	private function fetch_rss_news( $settings, $max_items = 15, &$raw_count = 0 ) {
		$feed_urls = array();

		// Predefined feeds mapping.
		$predefined_map = array(
			// Deutschland (DE)
			'google_news_de' => 'https://news.google.com/rss?hl=de&gl=DE&ceid=DE:de',
			'tagesschau'     => 'https://www.tagesschau.de/index~rss2.xml',
			'heise'          => 'https://www.heise.de/rss/heise-atom.xml',
			't3n'            => 'https://t3n.de/rss.xml',
			'golem'          => 'https://rss.golem.de/rss.php?feed=ATOM1.0',
			'spiegel'        => 'https://www.spiegel.de/schlagzeilen/index.rss',
			'welt'           => 'https://www.welt.de/feeds/latest.rss',
			// International (EN)
			'google_news_en' => 'https://news.google.com/rss?hl=en&gl=US&ceid=US:en',
			'bbc'            => 'https://feeds.bbci.co.uk/news/rss.xml',
			'reuters'        => 'https://feeds.reuters.com/reuters/topNews',
			'techcrunch'     => 'https://techcrunch.com/feed/',
			'the_verge'      => 'https://www.theverge.com/rss/index.xml',
			'ars_technica'   => 'https://feeds.arstechnica.com/arstechnica/index',
			'wired'          => 'https://www.wired.com/feed/rss',
			'hacker_news'    => 'https://hnrss.org/frontpage',
		);

		// Human-readable feed labels for progress display.
		$feed_labels = array(
			'google_news_de' => 'Google News DE',
			'tagesschau'     => 'tagesschau.de',
			'heise'          => 'heise online',
			't3n'            => 't3n Magazin',
			'golem'          => 'Golem.de',
			'spiegel'        => 'Spiegel Online',
			'welt'           => 'WELT',
			'google_news_en' => 'Google News EN',
			'bbc'            => 'BBC News',
			'reuters'        => 'Reuters',
			'techcrunch'     => 'TechCrunch',
			'the_verge'      => 'The Verge',
			'ars_technica'   => 'Ars Technica',
			'wired'          => 'WIRED',
			'hacker_news'    => 'Hacker News',
		);

		// Build a URL → label lookup (for predefined feeds).
		$url_to_label = array();

		// Collect selected predefined feeds.
		if ( ! empty( $settings['rss_predefined'] ) && is_array( $settings['rss_predefined'] ) ) {
			foreach ( $settings['rss_predefined'] as $id ) {
				if ( isset( $predefined_map[ $id ] ) ) {
					$feed_urls[] = $predefined_map[ $id ];
					$url_to_label[ $predefined_map[ $id ] ] = $feed_labels[ $id ] ?? $id;
				}
			}
		}

		// Collect custom feeds.
		if ( ! empty( $settings['rss_custom'] ) ) {
			$custom = array_filter( array_map( 'trim', explode( "\n", $settings['rss_custom'] ) ) );
			foreach ( $custom as $url ) {
				if ( filter_var( $url, FILTER_VALIDATE_URL ) ) {
					$feed_urls[] = $url;
					$url_to_label[ $url ] = wp_parse_url( $url, PHP_URL_HOST );
				}
			}
		}

		if ( empty( $feed_urls ) ) {
			return array();
		}

		// WordPress built-in SimplePie fetch.
		include_once ABSPATH . WPINC . '/feed.php';

		$items      = array();
		$per_feed   = max( 3, intval( ceil( $max_items / count( $feed_urls ) ) ) );
		$feed_index = 0;
		$feed_total = count( $feed_urls );

		foreach ( $feed_urls as $url ) {
			$feed_index++;
			$label = $url_to_label[ $url ] ?? wp_parse_url( $url, PHP_URL_HOST );
			$this->update_autopilot_progress(
				'rss',
				sprintf(
					/* translators: 1: current index, 2: total count, 3: feed name */
					__( 'RSS-Feed %1$d/%2$d: %3$s …', 'wp-blog-agent' ),
					$feed_index,
					$feed_total,
					$label
				)
			);

			$rss = fetch_feed( $url );
			if ( is_wp_error( $rss ) ) {
				continue;
			}

			$feed_items = $rss->get_items( 0, $per_feed );
			$source     = $rss->get_title() ?: wp_parse_url( $url, PHP_URL_HOST );

			foreach ( $feed_items as $item ) {
				$summary = $item->get_description();
				$summary = wp_strip_all_tags( $summary );
				$summary = mb_substr( $summary, 0, 200 );

				$items[] = array(
					'title'   => $item->get_title(),
					'link'    => $item->get_permalink(),
					'summary' => $summary,
					'source'  => $source,
					'date'    => $item->get_date( 'Y-m-d' ),
				);
			}

			if ( count( $items ) >= $max_items ) {
				break;
			}
		}

		// Store the total raw item count before keyword filtering (for diagnostics).
		$raw_count = count( $items );

		// Apply keyword filter if configured.
		// Supports three formats per line:
		//   "WordPress 7"   → exact phrase (quotes are stripped)
		//   WordPress 7     → ALL words must appear (AND logic)
		//   WordPress       → simple substring match
		$keywords_raw = ! empty( $settings['rss_keywords'] ) ? trim( $settings['rss_keywords'] ) : '';
		if ( ! empty( $keywords_raw ) ) {
			$keyword_lines = array_filter( array_map( 'trim', preg_split( '/[\n,]+/', $keywords_raw ) ) );
			if ( ! empty( $keyword_lines ) ) {
				$filtered = array();
				foreach ( $items as $item ) {
					$haystack = mb_strtolower( $item['title'] . ' ' . $item['summary'], 'UTF-8' );
					foreach ( $keyword_lines as $line ) {
						$line_lc = mb_strtolower( $line, 'UTF-8' );
						$matched = false;

						// Check for quoted exact phrase: "some phrase"
						if ( preg_match( '/^["\x{201C}\x{201E}](.+?)["\x{201D}\x{201C}]$/u', $line_lc, $m ) ) {
							// Exact phrase match (quotes stripped).
							$matched = ( mb_strpos( $haystack, $m[1] ) !== false );
						} elseif ( mb_strpos( $line_lc, ' ' ) !== false ) {
							// Multiple words without quotes → ALL must appear.
							$words = array_filter( explode( ' ', $line_lc ) );
							$all_found = true;
							foreach ( $words as $w ) {
								if ( mb_strpos( $haystack, $w ) === false ) {
									$all_found = false;
									break;
								}
							}
							$matched = $all_found;
						} else {
							// Single word → simple substring.
							$matched = ( mb_strpos( $haystack, $line_lc ) !== false );
						}

						if ( $matched ) {
							$item['matched_keyword'] = $line;
							$filtered[] = $item;
							break; // One keyword line match is enough.
						}
					}
				}
				return array_slice( $filtered, 0, $max_items );
			}
		}

		return array_slice( $items, 0, $max_items );
	}

	/**
	 * Update autopilot progress transient for live UI feedback.
	 *
	 * @param string $step   Short key (e.g. 'rss', 'topic', 'writing').
	 * @param string $detail Human-readable detail shown in the UI.
	 * @since 1.3.60
	 */
	private function update_autopilot_progress( $step, $detail ) {
		$data = array(
			'step'      => $step,
			'detail'    => $detail,
			'timestamp' => time(),
		);
		set_transient( 'wbag_autopilot_progress', $data, 600 );
	}

	/**
	 * AJAX: Return current autopilot progress (polled by the UI).
	 *
	 * @since 1.3.60
	 */
	public function ajax_autopilot_progress() {
		if ( ! current_user_can( 'edit_posts' ) ) {
			wp_send_json_error();
		}
		$progress = get_transient( 'wbag_autopilot_progress' );
		if ( $progress ) {
			wp_send_json_success( $progress );
		}
		wp_send_json_success( array( 'step' => 'idle', 'detail' => '' ) );
	}

	/**
	 * AJAX: Auto-save RSS keywords and feed selection without full settings form submit.
	 *
	 * @since 1.8.4
	 */
	public function ajax_save_rss_settings() {
		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wbag_admin_nonce' ) ) {
			wp_send_json_error( array( 'message' => 'Nonce verification failed' ) );
		}
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => 'Insufficient permissions' ) );
		}

		$settings = get_option( 'wbag_settings', array() );

		if ( isset( $_POST['rss_keywords'] ) ) {
			$settings['rss_keywords'] = sanitize_textarea_field( wp_unslash( $_POST['rss_keywords'] ) );
		}

		// rss_predefined comes as a JSON-encoded array from JS.
		if ( isset( $_POST['rss_predefined'] ) ) {
			$raw = wp_unslash( $_POST['rss_predefined'] );
			if ( is_array( $raw ) ) {
				$settings['rss_predefined'] = array_map( 'sanitize_key', $raw );
			} else {
				$decoded = json_decode( $raw, true );
				$settings['rss_predefined'] = is_array( $decoded ) ? array_map( 'sanitize_key', $decoded ) : array();
			}
		}

		update_option( 'wbag_settings', $settings );
		wp_send_json_success( array( 'message' => 'saved' ) );
	}

	/**
	 * AJAX: Fetch RSS matches and return them for user selection — no Claude API call.
	 *
	 * @since 1.8.5
	 */
	public function ajax_preview_rss_matches() {
		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wbag_admin_nonce' ) ) {
			wp_send_json_error( array( 'message' => 'Nonce verification failed' ) );
		}
		if ( ! current_user_can( 'edit_posts' ) ) {
			wp_send_json_error( array( 'message' => 'Insufficient permissions' ) );
		}
		if ( ! wbag_is_pro() ) {
			wp_send_json_error( array( 'message' => __( 'Der Autopilot ist ein Pro-Feature.', 'wp-blog-agent' ) ) );
		}

		$settings  = get_option( 'wbag_settings', array() );
		$raw_count = 0;
		$items     = $this->fetch_rss_news( $settings, 20, $raw_count );

		if ( empty( $items ) ) {
			$has_filter = ! empty( $settings['rss_keywords'] ) && trim( $settings['rss_keywords'] ) !== '';
			if ( $raw_count === 0 ) {
				wp_send_json_error( array(
					'message' => __( 'Die RSS-Feeds konnten nicht abgerufen werden oder enthielten keine aktuellen Beiträge. Bitte prüfen Sie die Netzwerkverbindung.', 'wp-blog-agent' ),
				) );
			} else {
				$keywords_display = implode( ', ', array_filter( array_map( 'trim', preg_split( '/[\n,]+/', $settings['rss_keywords'] ) ) ) );
				wp_send_json_error( array(
					'message' => sprintf(
						__( 'Der Schlagwortfilter „%1$s" hat in keinem der %2$d abgerufenen Beiträge einen Treffer ergeben.', 'wp-blog-agent' ),
						$keywords_display,
						$raw_count
					),
				) );
			}
		}

		wp_send_json_success( array(
			'items'     => $items,
			'raw_count' => $raw_count,
		) );
	}

	/**
	 * Create a blog post via autopilot.
	 *
	 * @param array|null $topic       Optional pre-selected topic.
	 * @param bool       $force_fresh When true, skip the existing queue and always
	 *                                generate a new RSS-informed topic via Topic Scout.
	 */
	public function create_post_autopilot( $topic = null, $force_fresh = false, $selected_item = null ) {
		$settings = get_option( 'wbag_settings', array() );

		if ( empty( $settings['api_key'] ) ) {
			return new WP_Error( 'wbag_no_api_key', __( 'Claude API-Schlüssel fehlt.', 'wp-blog-agent' ) );
		}

		// Clear any old progress and signal start.
		delete_transient( 'wbag_autopilot_progress' );
		$this->update_autopilot_progress( 'init', __( 'Autopilot wird gestartet …', 'wp-blog-agent' ) );

		// 1. Increase limits for long API calls ----------------------------------
		set_time_limit( 600 ); // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- Required for long-running API calls.
		wp_raise_memory_limit( 'image' );

		// 2. RSS news — either pre-selected by the user (skips fetch + Topic Scout)
		//    or fetched fresh from configured feeds.
		$rss_news = array();

		if ( ! empty( $selected_item ) && is_array( $selected_item ) && ! empty( $selected_item['title'] ) ) {
			// User already picked a specific article — use it directly.
			// This skips the RSS fetch AND the Topic Scout API call (saves tokens).
			$rss_news = array( $selected_item );
			$topic    = array(
				'title'       => sanitize_text_field( $selected_item['title'] ),
				'description' => isset( $selected_item['summary'] ) ? sanitize_textarea_field( $selected_item['summary'] ) : '',
				'keywords'    => array(),
				'source'      => isset( $selected_item['source'] ) ? sanitize_text_field( $selected_item['source'] ) : '',
				'date'        => isset( $selected_item['date'] ) ? sanitize_text_field( $selected_item['date'] ) : '',
			);
			$this->update_autopilot_progress( 'rss_done', sprintf(
				/* translators: %s article title */
				__( 'Ausgewählter Artikel: „%s"', 'wp-blog-agent' ),
				mb_substr( $selected_item['title'], 0, 80 )
			) );
		} else {
			// Standard flow: fetch RSS feeds and apply keyword filter.
			$has_keyword_filter = ! empty( $settings['rss_keywords'] ) && trim( $settings['rss_keywords'] ) !== '';
			if ( wbag_is_pro() ) {
				$this->update_autopilot_progress( 'rss', __( 'RSS-Feeds werden abgerufen …', 'wp-blog-agent' ) );
				$rss_raw_count = 0;
				$rss_news = $this->fetch_rss_news( $settings, 15, $rss_raw_count );

				if ( ! empty( $rss_news ) ) {
					$this->update_autopilot_progress( 'rss_done', sprintf(
						/* translators: %d number of matching articles */
						__( '%d passende Nachrichten gefunden.', 'wp-blog-agent' ),
						count( $rss_news )
					) );
				}

				if ( $has_keyword_filter && empty( $rss_news ) ) {
					$keywords_display = implode( ', ', array_filter( array_map( 'trim', preg_split( '/[\n,]+/', $settings['rss_keywords'] ) ) ) );
					if ( $rss_raw_count === 0 ) {
						$error_msg = sprintf(
							__( 'Die RSS-Feeds konnten nicht abgerufen werden oder enthielten keine aktuellen Beiträge. Schlagwortfilter „%s" wurde nicht angewendet. Bitte prüfen Sie die Netzwerkverbindung und ob die ausgewählten Feeds erreichbar sind.', 'wp-blog-agent' ),
							$keywords_display
						);
					} else {
						$error_msg = sprintf(
							__( 'Der Schlagwortfilter „%1$s" hat in keinem der %2$d abgerufenen Beiträge einen Treffer ergeben. Es wurde kein Beitrag erstellt. Tipp: Prüfen Sie, ob der Begriff wirklich in aktuellen Nachrichtentiteln vorkommt, oder verwenden Sie allgemeinere Schlagwörter.', 'wp-blog-agent' ),
							$keywords_display,
							$rss_raw_count
						);
					}
					$this->update_autopilot_progress( 'error', __( 'Keine passenden Nachrichten gefunden.', 'wp-blog-agent' ) );
					return new WP_Error( 'wbag_no_rss_match', $error_msg );
				}
			}
		}

		// 3. Get a topic ---------------------------------------------------------
		// When $force_fresh is true (manual RSS trigger), we always generate a new
		// topic informed by RSS headlines instead of reusing queue items.
		// Skipped when $selected_item was provided (topic already set above).
		if ( empty( $topic ) && ! $force_fresh ) {
			// Try to pick a pending topic from the queue.
			$queue = WBAG_Topic_Scout::get_queue();
			foreach ( $queue as $idx => $q_topic ) {
				if ( isset( $q_topic['status'] ) && 'pending' === $q_topic['status'] ) {
					$topic = $q_topic;
					// Mark it as 'writing' so it won't be picked again.
					$queue[ $idx ]['status'] = 'writing';
					update_option( 'wbag_topic_queue', $queue );
					break;
				}
			}
		}

		// If still no topic, run the Topic Scout to generate fresh ones.
		// Pass RSS news so the generated topics are based on current headlines.
		if ( empty( $topic ) ) {
			$this->update_autopilot_progress( 'topic', __( 'Themenvorschläge werden per KI generiert …', 'wp-blog-agent' ) );
			$scout = new WBAG_Topic_Scout();
			$new_topics = $scout->run( $rss_news );
			if ( is_wp_error( $new_topics ) ) {
				return $new_topics;
			}
			if ( ! empty( $new_topics ) && is_array( $new_topics ) ) {
				$topic = $new_topics[0];
				// Mark it as 'writing' in the queue.
				$queue = WBAG_Topic_Scout::get_queue();
				foreach ( $queue as $idx => $q_topic ) {
					if ( isset( $q_topic['title'] ) && $q_topic['title'] === $topic['title'] ) {
						$queue[ $idx ]['status'] = 'writing';
						update_option( 'wbag_topic_queue', $queue );
						break;
					}
				}
			}
		}

		if ( empty( $topic ) || empty( $topic['title'] ) ) {
			return new WP_Error( 'wbag_no_topic', __( 'Kein Thema gefunden oder generiert.', 'wp-blog-agent' ) );
		}

		// 4. Prepare topic array --------------------------------------------------
		$topic['primary_keyword'] = $this->determine_focus_keyword( $topic );
		$topic['internal_links']  = $this->get_internal_link_candidates( $topic, $settings, 0, 12 );

		// 4b. Inject RSS news as writing context for the blog writer prompt -------
		if ( ! empty( $rss_news ) ) {
			$rss_context = "\n\nAKTUELLE NACHRICHTEN AUS RSS-FEEDS (heute abgerufen — nutze relevante Meldungen als Kontext):\n";
			foreach ( $rss_news as $news_item ) {
				$rss_context .= "- [" . $news_item['source'] . ", " . $news_item['date'] . "] " . $news_item['title'];
				if ( ! empty( $news_item['summary'] ) ) {
					$rss_context .= " — " . $news_item['summary'];
				}
				$rss_context .= "\n";
			}
			$rss_context .= "\nWICHTIG: Verwende nur Nachrichten, die thematisch zum Beitrag passen. "
				. "Erwähne aktuelle Entwicklungen natürlich im Fließtext, ohne die Quellen-URLs zu verlinken. "
				. "Wenn keine der Nachrichten zum Thema passt, ignoriere sie und schreibe einen normalen Beitrag.\n";
			$topic['rss_news_context'] = $rss_context;
		}

		// 4. Generate blog content via Claude ------------------------------------
		$this->update_autopilot_progress( 'writing', sprintf(
			/* translators: %s is the topic title */
			__( 'Beitrag wird geschrieben: „%s" …', 'wp-blog-agent' ),
			mb_substr( $topic['title'], 0, 60 )
		) );
		$prompt       = $this->build_blog_writer_prompt( $settings, $topic );
		$blog_content = $this->call_claude_api(
			$settings['api_key'],
			$settings['api_model'] ?? 'claude-sonnet-4-6',
			$prompt,
			true,
			16384
		);

		if ( is_wp_error( $blog_content ) ) {
			return $blog_content;
		}

		// 5. Clean content -------------------------------------------------------
		if ( is_string( $blog_content ) ) {
			$blog_content = preg_replace( '/<style[^>]*>[\s\S]*?<\/style>/i', '', $blog_content );
			$blog_content = preg_replace( '/<link[^>]*rel=["\']stylesheet["\'][^>]*>/i', '', $blog_content );
			$blog_content = preg_replace( '/^```(?:html)?\s*/i', '', $blog_content );
			$blog_content = preg_replace( '/\s*```\s*$/i', '', $blog_content );
			$blog_content = trim( $blog_content );
		}

		// 6. Build excerpt from meta_description or content ----------------------
		$excerpt = '';

		// 7. Determine category --------------------------------------------------
		$post_category = array();
		$cat_slug = ! empty( $settings['post_category'] ) ? sanitize_title( $settings['post_category'] ) : '';
		if ( ! empty( $cat_slug ) ) {
			$cat = get_category_by_slug( $cat_slug );
			if ( $cat ) {
				$post_category = array( $cat->term_id );
			}
		}

		// 8. Create draft post ---------------------------------------------------
		// Use configured author from settings, fall back to first admin in cron context.
		$author_id = ! empty( $settings['post_author_id'] ) ? absint( $settings['post_author_id'] ) : 0;
		if ( empty( $author_id ) ) {
			$admins = get_users( array( 'role' => 'administrator', 'number' => 1, 'orderby' => 'ID', 'order' => 'ASC' ) );
			if ( ! empty( $admins ) ) {
				$author_id = $admins[0]->ID;
			}
		}

		$auto_publish = ! empty( $settings['schedule_enabled'] );
		$post_args = array(
			'post_title'   => $topic['title'],
			'post_content' => $blog_content,
			'post_status'  => $auto_publish ? 'publish' : 'draft',
			'post_author'  => $author_id,
		);
		if ( ! empty( $post_category ) ) {
			$post_args['post_category'] = $post_category;
		}
		$post_id = wp_insert_post( $post_args );

		if ( is_wp_error( $post_id ) ) {
			return $post_id;
		}

		// 9. SEO metadata --------------------------------------------------------
		$this->update_autopilot_progress( 'seo', __( 'SEO-Metadaten werden generiert …', 'wp-blog-agent' ) );
		$seo_meta = $this->generate_seo_metadata_for_post(
			$topic['title'],
			$blog_content,
			$settings,
			$topic['primary_keyword']
		);
		if ( ! is_wp_error( $seo_meta ) ) {
			$this->apply_seo_meta_to_post( $post_id, $seo_meta, $settings );
			$this->ensure_keyword_in_slug( $post_id, $seo_meta['focus_keyword'] );

			// Set tags from SEO metadata.
			if ( ! empty( $seo_meta['tags'] ) ) {
				wp_set_post_tags( $post_id, $seo_meta['tags'] );
			}
		}

		// Build a clean 30-word excerpt from the generated content.
		$plain = wp_strip_all_tags( $blog_content );
		$plain = preg_replace( '/\s+/', ' ', trim( $plain ) );
		$words = preg_split( '/\s+/', $plain, 32, PREG_SPLIT_NO_EMPTY );
		if ( count( $words ) > 30 ) {
			$words = array_slice( $words, 0, 30 );
			$excerpt = implode( ' ', $words ) . ' ...';
		} else {
			$excerpt = implode( ' ', $words );
		}
		wp_update_post( array( 'ID' => $post_id, 'post_excerpt' => $excerpt ) );

		// 10. Generate images ----------------------------------------------------
		if ( ! empty( $settings['gemini_api_key'] ) ) {
			$this->update_autopilot_progress( 'images', __( 'Bilder werden generiert …', 'wp-blog-agent' ) );
			$this->generate_and_attach_images( $post_id, $topic['title'], $blog_content, $settings );
		}

		// 11. Remove used topic from queue (same behaviour as manual flow) -------
		$queue = WBAG_Topic_Scout::get_queue();
		foreach ( $queue as $idx => $q_topic ) {
			if ( isset( $q_topic['title'] ) && $q_topic['title'] === $topic['title'] ) {
				unset( $queue[ $idx ] );
				$queue = array_values( $queue );
				update_option( 'wbag_topic_queue', $queue );
				break;
			}
		}

		update_post_meta( $post_id, '_wbag_generated', '1' );
		update_post_meta( $post_id, '_wbag_generated_at', current_time( 'mysql' ) );
		update_post_meta( $post_id, '_wbag_autopilot', '1' );
		/* translators: %s: blog post title (truncated to 60 characters) */
		$this->update_autopilot_progress( 'done', sprintf( __( 'Fertig: „%s"', 'wp-blog-agent' ), mb_substr( $topic['title'], 0, 60 ) ) );

		// 12. Email notification for RSS-based posts ------------------------------
		$has_rss_context = ! empty( $topic['rss_news_context'] );
		if ( $has_rss_context ) {
			update_post_meta( $post_id, '_wbag_rss_based', '1' );
		}
		if ( $has_rss_context && ! empty( $settings['rss_notify_email'] ) ) {
			$admin_email = get_option( 'admin_email' );
			$blog_name   = get_bloginfo( 'name' );
			$edit_link   = get_edit_post_link( $post_id, 'raw' );
			$subject     = sprintf(
				/* translators: %s blog name */
				__( '[%s] Neuer RSS-Beitrag zur Freigabe bereit', 'wp-blog-agent' ),
				$blog_name
			);
			$matched_kw = '';
			if ( ! empty( $rss_news ) ) {
				foreach ( $rss_news as $ni ) {
					if ( ! empty( $ni['matched_keyword'] ) ) {
						$matched_kw = $ni['matched_keyword'];
						break;
					}
				}
			}
			/* translators: %1$s: line break character */
			$body  = sprintf( __( 'Hallo,%1$s%1$sder Blog Agent hat einen neuen Beitrag als Entwurf erstellt, der auf aktuellen RSS-Nachrichten basiert.', 'wp-blog-agent' ), "\n" ) . "\n\n";
			$body .= __( 'Titel: ', 'wp-blog-agent' ) . $topic['title'] . "\n";
			if ( ! empty( $matched_kw ) ) {
				$body .= __( 'Gefundenes Schlagwort: ', 'wp-blog-agent' ) . $matched_kw . "\n";
			}
			$body .= "\n" . __( 'Beitrag bearbeiten und freigeben: ', 'wp-blog-agent' ) . $edit_link . "\n\n";
			$body .= __( 'Der Beitrag wurde als Entwurf gespeichert und wartet auf Ihre Prüfung.', 'wp-blog-agent' ) . "\n\n";
			$body .= '-- ' . "\n" . $blog_name . ' — WP Blog Agent Autopilot';

			wp_mail( $admin_email, $subject, $body );
		}

		return array(
			'post_id' => $post_id,
			'title'   => $topic['title'],
		);
	}

	/**
	 * AJAX handler for approving a topic
	 *
	 * @since 1.0.0
	 */
	public function ajax_approve_topic() {
		// Check nonce
		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wbag_admin_nonce' ) ) {
			wp_send_json_error( array( 'message' => __( 'Nonce verification failed', 'wp-blog-agent' ) ) );
		}

		// Check capabilities
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'wp-blog-agent' ) ) );
		}

		$index = isset( $_POST['topic_index'] ) ? intval( $_POST['topic_index'] ) : ( isset( $_POST['index'] ) ? intval( $_POST['index'] ) : -1 );

		if ( -1 === $index ) {
			wp_send_json_error( array( 'message' => __( 'Invalid topic index', 'wp-blog-agent' ) ) );
		}

		$topic_queue = get_option( 'wbag_topic_queue', array() );

		if ( ! isset( $topic_queue[ $index ] ) ) {
			wp_send_json_error( array( 'message' => __( 'Topic not found in queue', 'wp-blog-agent' ) ) );
		}

		// Move approved topic to a separate option
		$approved_topics = get_option( 'wbag_approved_topics', array() );
		if ( ! is_array( $approved_topics ) ) {
			$approved_topics = array();
		}

		$approved_topics[] = $topic_queue[ $index ];
		update_option( 'wbag_approved_topics', $approved_topics );

		// Remove from queue
		unset( $topic_queue[ $index ] );
		$topic_queue = array_values( $topic_queue ); // Re-index
		update_option( 'wbag_topic_queue', $topic_queue );

		wp_send_json_success( array( 'message' => __( 'Topic approved', 'wp-blog-agent' ) ) );
	}

	/**
	 * AJAX handler for removing an already-approved topic from the approved queue.
	 *
	 * Used when the user wants to unapprove / drop a topic that was approved
	 * but not yet written. Without this, approved topics could pile up unseen.
	 *
	 * @since 1.3.7
	 */
	public function ajax_remove_approved_topic() {
		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wbag_admin_nonce' ) ) {
			wp_send_json_error( array( 'message' => __( 'Nonce verification failed', 'wp-blog-agent' ) ) );
		}
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'wp-blog-agent' ) ) );
		}

		$index = isset( $_POST['topic_index'] ) ? intval( $_POST['topic_index'] ) : -1;
		if ( -1 === $index ) {
			wp_send_json_error( array( 'message' => __( 'Invalid topic index', 'wp-blog-agent' ) ) );
		}

		$approved = get_option( 'wbag_approved_topics', array() );
		if ( ! is_array( $approved ) || ! isset( $approved[ $index ] ) ) {
			wp_send_json_error( array( 'message' => __( 'Topic not found in approved list', 'wp-blog-agent' ) ) );
		}

		unset( $approved[ $index ] );
		$approved = array_values( $approved );
		update_option( 'wbag_approved_topics', $approved );

		wp_send_json_success( array( 'message' => __( 'Approved topic removed', 'wp-blog-agent' ) ) );
	}

	/**
	 * AJAX handler for deleting a topic
	 *
	 * @since 1.0.0
	 */
	public function ajax_delete_topic() {
		// Check nonce
		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wbag_admin_nonce' ) ) {
			wp_send_json_error( array( 'message' => __( 'Nonce verification failed', 'wp-blog-agent' ) ) );
		}

		// Check capabilities
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'wp-blog-agent' ) ) );
		}

		$index = isset( $_POST['topic_index'] ) ? intval( $_POST['topic_index'] ) : ( isset( $_POST['index'] ) ? intval( $_POST['index'] ) : -1 );

		if ( -1 === $index ) {
			wp_send_json_error( array( 'message' => __( 'Invalid topic index', 'wp-blog-agent' ) ) );
		}

		$topic_queue = get_option( 'wbag_topic_queue', array() );

		if ( ! isset( $topic_queue[ $index ] ) ) {
			wp_send_json_error( array( 'message' => __( 'Topic not found in queue', 'wp-blog-agent' ) ) );
		}

		// Add to dismissed topics blacklist before removing
		$dismissed_topic = $topic_queue[ $index ];
		$dismissed_list  = get_option( 'wbag_dismissed_topics', array() );
		if ( ! is_array( $dismissed_list ) ) {
			$dismissed_list = array();
		}
		$dismissed_list[] = array(
			'title'           => sanitize_text_field( $dismissed_topic['title'] ?? '' ),
			'primary_keyword' => sanitize_text_field( $dismissed_topic['primary_keyword'] ?? '' ),
			'dismissed_at'    => current_time( 'mysql' ),
		);
		update_option( 'wbag_dismissed_topics', $dismissed_list );

		// Remove from queue
		unset( $topic_queue[ $index ] );
		$topic_queue = array_values( $topic_queue ); // Re-index
		update_option( 'wbag_topic_queue', $topic_queue );

		wp_send_json_success( array( 'message' => __( 'Topic deleted', 'wp-blog-agent' ) ) );
	}

	/**
	 * AJAX handler for resetting the dismissed topics blacklist
	 *
	 * @since 1.3.36
	 */
	public function ajax_reset_dismissed_topics() {
		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wbag_admin_nonce' ) ) {
			wp_send_json_error( array( 'message' => __( 'Nonce verification failed', 'wp-blog-agent' ) ) );
		}
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'wp-blog-agent' ) ) );
		}

		delete_option( 'wbag_dismissed_topics' );

		wp_send_json_success( array( 'message' => __( 'Blacklist wurde zurückgesetzt. Alle Themen können erneut vorgeschlagen werden.', 'wp-blog-agent' ) ) );
	}

	/**
	 * AJAX handler for saving user notes on a queue topic
	 *
	 * @since 1.3.40
	 */
	public function ajax_save_topic_notes() {
		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wbag_admin_nonce' ) ) {
			wp_send_json_error( array( 'message' => __( 'Nonce verification failed', 'wp-blog-agent' ) ) );
		}
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'wp-blog-agent' ) ) );
		}

		$index = isset( $_POST['topic_index'] ) ? intval( $_POST['topic_index'] ) : -1;
		$notes = isset( $_POST['user_notes'] ) ? sanitize_textarea_field( wp_unslash( $_POST['user_notes'] ) ) : '';

		$queue = get_option( 'wbag_topic_queue', array() );
		if ( ! is_array( $queue ) || ! isset( $queue[ $index ] ) ) {
			wp_send_json_error( array( 'message' => __( 'Thema nicht gefunden.', 'wp-blog-agent' ) ) );
		}

		$queue[ $index ]['user_notes'] = $notes;
		update_option( 'wbag_topic_queue', $queue );

		wp_send_json_success( array( 'message' => __( 'Hinweise gespeichert.', 'wp-blog-agent' ) ) );
	}

	/**
	 * AJAX: Add an image attachment ID to a topic queue item.
	 *
	 * @since 1.3.44
	 */
	public function ajax_save_topic_images() {
		if ( ! wbag_is_pro() ) {
			wp_send_json_error( array( 'message' => __( 'Pro-Feature erforderlich.', 'wp-blog-agent' ) ) );
		}
		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wbag_admin_nonce' ) ) {
			wp_send_json_error( array( 'message' => __( 'Nonce verification failed', 'wp-blog-agent' ) ) );
		}
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'wp-blog-agent' ) ) );
		}

		$index    = isset( $_POST['topic_index'] ) ? intval( $_POST['topic_index'] ) : -1;
		$image_id = isset( $_POST['image_id'] ) ? intval( $_POST['image_id'] ) : 0;

		if ( $image_id <= 0 ) {
			wp_send_json_error( array( 'message' => __( 'Ungültige Bild-ID.', 'wp-blog-agent' ) ) );
		}

		$queue = get_option( 'wbag_topic_queue', array() );
		if ( ! is_array( $queue ) || ! isset( $queue[ $index ] ) ) {
			wp_send_json_error( array( 'message' => __( 'Thema nicht gefunden.', 'wp-blog-agent' ) ) );
		}

		if ( ! isset( $queue[ $index ]['user_images'] ) || ! is_array( $queue[ $index ]['user_images'] ) ) {
			$queue[ $index ]['user_images'] = array();
		}

		if ( ! in_array( $image_id, $queue[ $index ]['user_images'], true ) ) {
			$queue[ $index ]['user_images'][] = $image_id;
		}

		update_option( 'wbag_topic_queue', $queue );

		$thumb_url = wp_get_attachment_image_url( $image_id, 'thumbnail' );

		wp_send_json_success( array(
			'message'   => __( 'Bild hinzugefügt.', 'wp-blog-agent' ),
			'image_id'  => $image_id,
			'thumb_url' => $thumb_url ? $thumb_url : '',
		) );
	}

	/**
	 * AJAX: Remove a single image from a topic queue item.
	 *
	 * @since 1.3.44
	 */
	public function ajax_remove_topic_image() {
		if ( ! wbag_is_pro() ) {
			wp_send_json_error( array( 'message' => __( 'Pro-Feature erforderlich.', 'wp-blog-agent' ) ) );
		}
		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wbag_admin_nonce' ) ) {
			wp_send_json_error( array( 'message' => __( 'Nonce verification failed', 'wp-blog-agent' ) ) );
		}
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'wp-blog-agent' ) ) );
		}

		$index    = isset( $_POST['topic_index'] ) ? intval( $_POST['topic_index'] ) : -1;
		$image_id = isset( $_POST['image_id'] ) ? intval( $_POST['image_id'] ) : 0;

		$queue = get_option( 'wbag_topic_queue', array() );
		if ( ! is_array( $queue ) || ! isset( $queue[ $index ] ) ) {
			wp_send_json_error( array( 'message' => __( 'Thema nicht gefunden.', 'wp-blog-agent' ) ) );
		}

		if ( isset( $queue[ $index ]['user_images'] ) && is_array( $queue[ $index ]['user_images'] ) ) {
			$queue[ $index ]['user_images'] = array_values(
				array_filter( $queue[ $index ]['user_images'], function ( $id ) use ( $image_id ) {
					return (int) $id !== $image_id;
				} )
			);
		}

		update_option( 'wbag_topic_queue', $queue );

		wp_send_json_success( array( 'message' => __( 'Bild entfernt.', 'wp-blog-agent' ) ) );
	}

	/**
	 * Build prompt for topic scout
	 *
	 * @param array $settings Plugin settings.
	 * @return string The prompt for Claude API
	 *
	 * @since 1.0.0
	 */
	/**
	 * AJAX handler for analyzing a reference blog post URL
	 *
	 * Fetches the URL content, sends it to Claude to analyze the style,
	 * and returns a style description for use in blog writing.
	 *
	 * @since 1.1.0
	 */
	public function ajax_analyze_reference() {
		// Check nonce
		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wbag_admin_nonce' ) ) {
			wp_send_json_error( array( 'message' => __( 'Nonce verification failed', 'wp-blog-agent' ) ) );
		}

		// Check capabilities
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'wp-blog-agent' ) ) );
		}

		$url = isset( $_POST['url'] ) ? esc_url_raw( wp_unslash( $_POST['url'] ) ) : '';
		if ( empty( $url ) ) {
			wp_send_json_error( array( 'message' => __( 'Bitte geben Sie eine gültige URL ein.', 'wp-blog-agent' ) ) );
		}

		// Fetch the URL content
		$response = wp_remote_get( $url, array(
			'timeout'    => 30,
			'user-agent' => 'Mozilla/5.0 (compatible; WP Blog Agent/1.0)',
		) );

		if ( is_wp_error( $response ) ) {
			/* translators: %s: HTTP error message */
			wp_send_json_error( array( 'message' => sprintf( __( 'URL konnte nicht abgerufen werden: %s', 'wp-blog-agent' ), $response->get_error_message() ) ) );
		}

		$html = wp_remote_retrieve_body( $response );
		if ( empty( $html ) ) {
			wp_send_json_error( array( 'message' => __( 'Die Seite hat keinen Inhalt zurückgegeben.', 'wp-blog-agent' ) ) );
		}

		// Extract meaningful content from HTML (strip scripts, styles, nav, footer)
		$html = preg_replace( '/<script[^>]*>[\s\S]*?<\/script>/i', '', $html );
		$html = preg_replace( '/<style[^>]*>[\s\S]*?<\/style>/i', '', $html );
		$html = preg_replace( '/<nav[^>]*>[\s\S]*?<\/nav>/i', '', $html );
		$html = preg_replace( '/<footer[^>]*>[\s\S]*?<\/footer>/i', '', $html );
		$html = preg_replace( '/<header[^>]*>[\s\S]*?<\/header>/i', '', $html );

		// Extract inline styles and CSS classes for color/design analysis
		preg_match_all( '/style="([^"]*)"/', $html, $style_matches );
		$inline_styles = implode( "\n", array_unique( array_slice( $style_matches[1] ?? array(), 0, 50 ) ) );

		// Extract the article/main content area
		$content = '';
		if ( preg_match( '/<article[^>]*>([\s\S]*?)<\/article>/i', $html, $matches ) ) {
			$content = $matches[1];
		} elseif ( preg_match( '/<main[^>]*>([\s\S]*?)<\/main>/i', $html, $matches ) ) {
			$content = $matches[1];
		} elseif ( preg_match( '/<div[^>]*class="[^"]*(?:entry-content|post-content|article-content)[^"]*"[^>]*>([\s\S]*?)<\/div>/i', $html, $matches ) ) {
			$content = $matches[1];
		} else {
			// Fallback: use body content
			if ( preg_match( '/<body[^>]*>([\s\S]*?)<\/body>/i', $html, $matches ) ) {
				$content = $matches[1];
			}
		}

		// Limit content length for API (keep HTML structure for style analysis)
		$content_for_analysis = substr( $content, 0, 12000 );

		$settings = get_option( 'wbag_settings', array() );

		// Send to Claude for analysis
		$prompt = 'Analysiere den folgenden HTML-Blogbeitrag und erstelle eine detaillierte Stilvorlage auf Deutsch. Beschreibe:

1. **Struktur & Aufbau**: Wie ist der Beitrag gegliedert? (Einleitung, H2/H3-Abschnitte, Fazit, FAQ etc.)
2. **Design-Elemente**: Welche besonderen Elemente werden verwendet? (Info-Karten, Boxen, Hervorhebungen, Trennlinien, Icons, Zitate etc.)
3. **Farben**: Welche Farben werden verwendet? (Hex-Codes wenn erkennbar, z.B. für Buttons, Karten-Hintergründe, Akzente)
4. **Typografie & Formatierung**: Schriftgrößen, Abstände, fett/kursiv, Listen-Stil
5. **CTA-Stil**: Wie sehen Call-to-Action-Elemente aus? (Farbe, Form, Position)
6. **Ton & Sprache**: Formell/informell, Sie/Du, Emojis, technisch/einfach
7. **Besonderheiten**: Alles was den Stil einzigartig macht (z.B. Karten mit abgerundeten Ecken, Schatten, Gradient-Hintergründe)

Inline-Styles gefunden auf der Seite:
' . $inline_styles . '

HTML-Inhalt des Beitrags:
' . $content_for_analysis . '

Gib die Analyse als strukturierten Text zurück, der direkt als Anweisung an einen Blog-Writer verwendet werden kann. Beginne jeden Punkt mit einer klaren Anweisung (z.B. "Verwende Info-Karten mit abgerundeten Ecken...").';

		$style_analysis = $this->call_claude_api(
			$settings['api_key'],
			$settings['api_model'] ?? 'claude-sonnet-4-6',
			$prompt,
			true // Raw mode — return plain text, don't try JSON parsing
		);

		if ( is_wp_error( $style_analysis ) ) {
			wp_send_json_error( array( 'message' => $style_analysis->get_error_message() ) );
		}

		wp_send_json_success( array(
			'message' => __( 'Analyse abgeschlossen!', 'wp-blog-agent' ),
			'style'   => $style_analysis,
		) );
	}

	private function build_topic_scout_prompt( $settings ) {
		$niche = $settings['niche'] ?? '';
		$subtopics = $settings['subtopics'] ?? '';
		$target_audience = $settings['target_audience'] ?? '';
		$topic_count = ! empty( $settings['topic_count'] ) ? intval( $settings['topic_count'] ) : 5;

		$prompt = sprintf(
			/* translators: %1$d = topic count, %2$s = niche, %3$s = subtopics, %4$s = target audience */
			__( 'Generate %1$d blog topic ideas for the niche: %2$s. Subtopics: %3$s. Target audience: %4$s.

TITLE RULES (mandatory for Rank Math SEO):
- Every title MUST contain at least one number (e.g. a year like 2026, a count like "7 Tips", "Top 10", "5 Steps").
- Every title MUST contain at least one power word (emotionally charged words like: ultimate, incredible, surprising, proven, essential, secret, easy, powerful, effective, perfect, complete, new, important, free, exclusive, guaranteed, fast, best).
- Keep titles under 60 characters for optimal SEO display.

IMPORTANT: Return ONLY a valid JSON array, no additional text or explanation. Each object must have these keys: "title", "description", "primary_keyword".

Example format:
[{"title": "7 Ultimative Tipps für Keyword-Recherche 2026", "description": "Brief description", "primary_keyword": "main keyword"}]', 'wp-blog-agent' ),
			$topic_count,
			$niche,
			$subtopics,
			$target_audience
		);

		return $prompt;
	}

	/**
	 * Build prompt for blog writer
	 *
	 * @param array $settings Plugin settings.
	 * @param array $topic The topic to write about.
	 * @return string The prompt for Claude API
	 *
	 * @since 1.0.0
	 */
	/**
	 * Resolve which of the four style slots is currently active and return
	 * its text. Falls back to the detected style if the chosen slot is empty.
	 *
	 * @since 1.3.11
	 *
	 * @param array $settings Plugin settings.
	 * @return string Style text to send to the Blog Writer (can be empty).
	 */
	private function resolve_active_style( $settings ) {
		$active = isset( $settings['active_style'] ) ? $settings['active_style'] : 'detected';
		$map    = array(
			'detected' => 'reference_style',
			'custom_1' => 'custom_style_1',
			'custom_2' => 'custom_style_2',
			'custom_3' => 'custom_style_3',
		);
		$key = isset( $map[ $active ] ) ? $map[ $active ] : 'reference_style';
		$val = isset( $settings[ $key ] ) ? trim( (string) $settings[ $key ] ) : '';
		// Fallback: if the chosen slot is empty, try the detected one so we
		// don't silently lose the style reference.
		if ( '' === $val && 'reference_style' !== $key ) {
			$val = isset( $settings['reference_style'] ) ? trim( (string) $settings['reference_style'] ) : '';
		}
		return $val;
	}

	/**
	 * Determine a guaranteed-non-empty focus keyword for a topic.
	 *
	 * Uses $topic['primary_keyword'] if present, otherwise derives a short
	 * phrase from the topic title (first 2–4 words, stripped of punctuation).
	 *
	 * @since 1.3.5
	 *
	 * @param array $topic Topic array with title/description/primary_keyword.
	 * @return string Sanitized focus keyword (never empty unless title is empty).
	 */
	private function determine_focus_keyword( $topic ) {
		$candidate = isset( $topic['primary_keyword'] ) ? trim( (string) $topic['primary_keyword'] ) : '';
		if ( '' !== $candidate ) {
			return sanitize_text_field( $candidate );
		}

		$title = isset( $topic['title'] ) ? trim( (string) $topic['title'] ) : '';
		if ( '' === $title ) {
			return '';
		}

		// Strip common separators and take first 2-4 words as a reasonable keyword phrase.
		$clean = preg_replace( '/[^\p{L}\p{N}\s\-]/u', ' ', $title );
		$clean = preg_replace( '/\s+/', ' ', trim( $clean ) );
		$words = explode( ' ', $clean );
		$take  = min( 4, max( 2, count( $words ) ) );
		$phrase = implode( ' ', array_slice( $words, 0, $take ) );

		return sanitize_text_field( $phrase );
	}

	/**
	 * Ensure the post slug contains the focus keyword.
	 *
	 * If the existing slug does not include the slugified keyword, rebuilds
	 * the slug as "{keyword}-{title}" (trimmed to ~70 chars) and updates the post.
	 *
	 * @since 1.3.5
	 *
	 * @param int    $post_id       The post ID.
	 * @param string $focus_keyword The focus keyword.
	 * @return void
	 */
	private function ensure_keyword_in_slug( $post_id, $focus_keyword ) {
		if ( empty( $post_id ) ) {
			return;
		}

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

		// Maximum slug length (not including protocol/domain). 45 chars keeps the
		// full URL below ~75 characters on typical sites — RankMath's recommended
		// URL length limit.
		$max_slug_len = 45;

		$keyword_slug = ! empty( $focus_keyword ) ? sanitize_title( $focus_keyword ) : '';
		$title_slug   = sanitize_title( $post->post_title );
		$current_slug = ! empty( $post->post_name ) ? $post->post_name : $title_slug;

		// Prefer the keyword slug as the base — it's the most SEO-relevant and
		// guarantees the focus keyword appears in the URL. Fall back to the
		// title slug if no keyword is available.
		$new_slug = ! empty( $keyword_slug ) ? $keyword_slug : $title_slug;

		// Hard-trim to max length.
		if ( strlen( $new_slug ) > $max_slug_len ) {
			$new_slug = substr( $new_slug, 0, $max_slug_len );
			$new_slug = rtrim( $new_slug, '-' );
		}

		// If nothing to change, bail out.
		if ( $new_slug === $current_slug || empty( $new_slug ) ) {
			// Still enforce length on the current slug if it's too long.
			if ( strlen( $current_slug ) > $max_slug_len ) {
				$trimmed = rtrim( substr( $current_slug, 0, $max_slug_len ), '-' );
				wp_update_post( array( 'ID' => $post_id, 'post_name' => $trimmed ) );
			}
			return;
		}

		wp_update_post(
			array(
				'ID'        => $post_id,
				'post_name' => $new_slug,
			)
		);
	}

	/**
	 * Fetch thematically relevant published posts to suggest as internal link targets.
	 *
	 * Combines three strategies and deduplicates:
	 *   1. Keyword search in title/content using the focus keyword and topic title
	 *      (via WP_Query's `s` parameter — searches post_title + post_content).
	 *   2. Posts in the same category (if a category slug is configured in settings).
	 *   3. Fallback: recent posts, to guarantee enough candidates for RankMath's
	 *      "internal links" check even on new sites without thematic matches.
	 *
	 * The result is a pre-filtered, topic-aware list (max $limit posts). Claude
	 * then picks 2-4 of these to weave into the content with natural anchor texts.
	 *
	 * @since 1.3.6
	 *
	 * @param array $topic      Topic array with title, description, primary_keyword.
	 * @param array $settings   Plugin settings (used for category filter).
	 * @param int   $exclude_id Post ID to exclude (0 for none).
	 * @param int   $limit      Maximum number of posts to return.
	 * @return array Array of ['title' => ..., 'url' => ..., 'reason' => ...] entries.
	 */
	private function get_internal_link_candidates( $topic = array(), $settings = array(), $exclude_id = 0, $limit = 12 ) {
		$results     = array();
		$seen_ids    = array();
		$exclude_ids = $exclude_id ? array( (int) $exclude_id ) : array();

		$focus_keyword = '';
		$topic_title   = '';
		if ( is_array( $topic ) ) {
			$focus_keyword = ! empty( $topic['primary_keyword'] ) ? (string) $topic['primary_keyword'] : '';
			$topic_title   = ! empty( $topic['title'] ) ? (string) $topic['title'] : '';
		}

		$add_post = function( $pid, $reason ) use ( &$results, &$seen_ids, $limit ) {
			if ( count( $results ) >= $limit || in_array( (int) $pid, $seen_ids, true ) ) {
				return;
			}
			$title = get_the_title( $pid );
			$url   = get_permalink( $pid );
			if ( empty( $title ) || empty( $url ) ) {
				return;
			}
			$seen_ids[] = (int) $pid;
			$results[]  = array(
				'title'  => $title,
				'url'    => $url,
				'reason' => $reason,
			);
		};

		$base_args = array(
			'post_type'      => 'post',
			'post_status'    => 'publish',
			'no_found_rows'  => true,
			'fields'         => 'ids',
			'posts_per_page' => (int) $limit,
		);
		if ( ! empty( $exclude_ids ) ) {
			$base_args['post__not_in'] = $exclude_ids; // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in -- Small exclude list for duplicate avoidance.
		}

		// --- Strategy 1: Keyword search ---
		// WP_Query's `s` parameter searches post_title, post_excerpt, post_content.
		if ( ! empty( $focus_keyword ) ) {
			$q1 = new WP_Query( array_merge( $base_args, array(
				's'       => $focus_keyword,
				'orderby' => 'relevance',
			) ) );
			foreach ( $q1->posts as $pid ) {
				$add_post( $pid, 'Keyword-Match: ' . $focus_keyword );
			}
			wp_reset_postdata();
		}

		// Also try searching for the topic title (more specific than the keyword sometimes).
		if ( count( $results ) < $limit && ! empty( $topic_title ) && $topic_title !== $focus_keyword ) {
			// Use only the first 3-4 meaningful words of the title to keep the search broad.
			$title_words = preg_split( '/\s+/', trim( $topic_title ) );
			$search_term = implode( ' ', array_slice( $title_words, 0, 4 ) );

			$q2 = new WP_Query( array_merge( $base_args, array(
				's'       => $search_term,
				'orderby' => 'relevance',
			) ) );
			foreach ( $q2->posts as $pid ) {
				$add_post( $pid, 'Titel-Match: ' . $search_term );
			}
			wp_reset_postdata();
		}

		// --- Strategy 2: Same category (if configured) ---
		if ( count( $results ) < $limit && ! empty( $settings['post_category'] ) ) {
			$cat_slug = sanitize_title( $settings['post_category'] );
			$cat      = get_category_by_slug( $cat_slug );
			if ( $cat ) {
				$q3 = new WP_Query( array_merge( $base_args, array(
					'cat'     => $cat->term_id,
					'orderby' => 'date',
					'order'   => 'DESC',
				) ) );
				foreach ( $q3->posts as $pid ) {
					$add_post( $pid, 'Gleiche Kategorie: ' . $cat->name );
				}
				wp_reset_postdata();
			}
		}

		// --- Strategy 3: Fallback — recent posts ---
		if ( count( $results ) < $limit ) {
			$q4 = new WP_Query( array_merge( $base_args, array(
				'orderby' => 'date',
				'order'   => 'DESC',
			) ) );
			foreach ( $q4->posts as $pid ) {
				$add_post( $pid, 'Aktuell' );
			}
			wp_reset_postdata();
		}

		return $results;
	}

	private function build_blog_writer_prompt( $settings, $topic ) {
		$min_words        = $settings['word_count_min'] ?? 1000;
		$max_words        = $settings['word_count_max'] ?? 3000;
		$faq_count        = $settings['faq_count'] ?? 5;
		$include_toc      = $settings['include_toc'] ?? 0;
		$language         = $settings['language'] ?? 'de';
		$formal           = $settings['formal_address'] ?? 'sie';
		$cta_text         = $settings['cta_text'] ?? '';
		$cta_url          = $settings['cta_url'] ?? '';
		$cta_color        = $settings['cta_color'] ?? '#FF6B35';
		$content_max_width = ! empty( $settings['content_max_width'] ) ? intval( $settings['content_max_width'] ) : 0;
		$ref_style        = $this->resolve_active_style( $settings );

		$focus_keyword = trim( (string) ( $topic['primary_keyword'] ?? '' ) );

		$user_notes = trim( (string) ( $topic['user_notes'] ?? '' ) );

		$prompt = 'Schreibe einen vollständigen, SEO-optimierten Blogbeitrag auf ' . ( 'de' === $language ? 'Deutsch' : $language ) . '.

THEMA: ' . ( $topic['title'] ?? 'Untitled' ) . '
BESCHREIBUNG: ' . ( $topic['description'] ?? '' ) . '
FOKUS-KEYWORD: ' . $focus_keyword . '';

		if ( ! empty( $user_notes ) ) {
			$prompt .= "\n\nBENUTZER-HINWEISE (PFLICHT — UNBEDINGT BERÜCKSICHTIGEN):\n"
				. "Der Nutzer hat folgende Anweisungen für diesen Beitrag hinterlegt. "
				. "Baue diese Informationen sinnvoll und natürlich in den Beitrag ein:\n"
				. $user_notes . "\n";
		}

		// RSS news context for Autopilot — Pro only.
		if ( ! empty( $topic['rss_news_context'] ) ) {
			$prompt .= $topic['rss_news_context'];
		}

		// User-uploaded images for this topic — Pro only.
		$user_images = ( wbag_is_pro() && isset( $topic['user_images'] ) && is_array( $topic['user_images'] ) ) ? $topic['user_images'] : array();
		if ( ! empty( $user_images ) ) {
			$image_lines = array();
			foreach ( $user_images as $img_id ) {
				$img_url = wp_get_attachment_url( (int) $img_id );
				$img_alt = get_post_meta( (int) $img_id, '_wp_attachment_image_alt', true );
				$img_title = get_the_title( (int) $img_id );
				if ( $img_url ) {
					$line = '- URL: ' . $img_url;
					if ( ! empty( $img_alt ) ) {
						$line .= ' | Alt: ' . $img_alt;
					}
					if ( ! empty( $img_title ) ) {
						$line .= ' | Titel: ' . $img_title;
					}
					$image_lines[] = $line;
				}
			}
			if ( ! empty( $image_lines ) ) {
				$prompt .= "\n\nBENUTZER-BILDER (PFLICHT — IN DEN BEITRAG EINBAUEN):\n"
					. "Der Nutzer hat eigene Bilder für diesen Beitrag bereitgestellt. "
					. "Baue ALLE folgenden Bilder sinnvoll und an thematisch passenden Stellen in den Beitrag ein.\n"
					. "Verwende <img>-Tags mit den EXAKTEN URLs. Setze sinnvolle alt-Attribute (nutze den angegebenen Alt-Text oder formuliere einen passenden).\n"
					. "Style die Bilder mit inline-styles: max-width: 100%; height: auto; border-radius: 8px; margin: 20px 0;\n"
					. "Verteile die Bilder gleichmäßig über den Beitrag — NICHT alle am Anfang oder Ende.\n\n"
					. implode( "\n", $image_lines ) . "\n";
			}
		}

		$prompt .= '

ANFORDERUNGEN:
- Länge: ' . $min_words . '-' . $max_words . ' Wörter
- ' . $faq_count . ' FAQ-Fragen am Ende als aufklappbares Akkordeon
- ' . ( $include_toc ? 'Inhaltsverzeichnis am Anfang einfügen' : 'Kein Inhaltsverzeichnis' ) . '
- Verwende semantisches HTML (h1, h2, h3, p, ul, ol)
- Schreibe den kompletten HTML-Code, der direkt in WordPress eingefügt werden kann

KRITISCHE CSS-REGELN:
- Verwende AUSSCHLIESSLICH inline-styles (style="...") direkt an den HTML-Elementen
- KEINE <style>-Tags oder <style>-Blöcke verwenden! Das zerstört das WordPress-Layout
- KEINE globalen CSS-Selektoren wie body, *, html, nav, header, footer, main, .wp-*, #wp-*
- KEIN externes CSS verlinken
- Alle Styles müssen als inline-style am jeweiligen Element stehen (z.B. <div style="background: #f5f5f5; padding: 20px; border-radius: 8px;">)';

		// Strong, explicit address rule.
		if ( 'du' === $formal ) {
			$prompt .= "\n\nPFLICHT — ANSPRACHE (KONSISTENT IM GESAMTEN BEITRAG):\n"
				. "- Sprich den Leser DURCHGEHEND mit \"Du\" an — informell, direkt und persönlich.\n"
				. "- Verwende IMMER die Du-Form: \"du\", \"dir\", \"dein\", \"deine\", \"deinem\", \"deinen\".\n"
				. "- Verwende NIEMALS die Sie-Form (\"Sie\", \"Ihnen\", \"Ihr\") — auch nicht versehentlich in einzelnen Sätzen.\n"
				. "- Dies gilt für den GESAMTEN Beitrag: Einleitung, Fließtext, Zwischenüberschriften, FAQ-Antworten, CTA-Texte und Bildunterschriften.\n"
				. "- Prüfe vor der Ausgabe JEDEN Absatz: Wurde der Leser überall mit \"Du\" angesprochen?\n";
		} else {
			$prompt .= "\n\nPFLICHT — ANSPRACHE (KONSISTENT IM GESAMTEN BEITRAG):\n"
				. "- Sprechen Sie den Leser DURCHGEHEND mit \"Sie\" an — höflich, respektvoll und professionell.\n"
				. "- Verwenden Sie IMMER die Sie-Form: \"Sie\", \"Ihnen\", \"Ihr\", \"Ihre\", \"Ihrem\", \"Ihren\".\n"
				. "- Verwenden Sie NIEMALS die Du-Form (\"du\", \"dir\", \"dein\") — auch nicht versehentlich in einzelnen Sätzen.\n"
				. "- Dies gilt für den GESAMTEN Beitrag: Einleitung, Fließtext, Zwischenüberschriften, FAQ-Antworten, CTA-Texte und Bildunterschriften.\n"
				. "- Prüfen Sie vor der Ausgabe JEDEN Absatz: Wurde der Leser überall mit \"Sie\" angesprochen?\n";
		}

		if ( $content_max_width > 0 ) {
			$prompt .= "\n\nPFLICHT — BEITRAGSBREITE:\n"
				. "- Wickle den GESAMTEN Beitragsinhalt (von H1 bis zum letzten Element) in ein einziges äußeres Wrapper-Div:\n"
				. '  <div style="max-width: ' . $content_max_width . 'px; margin: 0 auto;">' . "\n"
				. "- Dieses Wrapper-Div ist das ERSTE und LETZTE Element deiner Ausgabe.\n"
				. "- ALLE Inhalte (Überschriften, Absätze, Listen, Boxen, FAQ, CTA) MÜSSEN innerhalb dieses Wrappers liegen.\n"
				. "- Setze KEIN weiteres max-width auf innere Elemente, die den Textfluss begrenzen würden.\n";
		}

		$prompt .= "\n\nPFLICHT — TITEL / SEO-TITEL (RANK MATH OPTIMIERUNG):\n"
			. "- Der H1-Titel MUSS mindestens eine Zahl enthalten (z. B. eine Jahreszahl wie 2026, eine Anzahl wie \"7 Tipps\", \"Top 10\", \"5 Schritte\" o. Ä.).\n"
			. "- Der H1-Titel MUSS mindestens ein Power Word enthalten. Power Words sind emotional aufgeladene Wörter, die Aufmerksamkeit erzeugen, z. B.: "
			. "ultimativ, unglaublich, erstaunlich, geheim, bewährt, sofort, kostenlos, exklusiv, essentiell, unverzichtbar, "
			. "einfach, schnell, mächtig, überraschend, effektiv, perfekt, garantiert, komplett, neu, wichtig.\n"
			. "- Halte den Titel unter 60 Zeichen für optimale SEO-Darstellung.\n"
			. "- Diese Titel-Regeln gelten auch für den SEO-Titel (falls separat generiert).\n";

		if ( ! empty( $focus_keyword ) ) {
			// Calculate required keyword occurrences for ~1–1.5% density (Rank Math optimal range).
			// Keyword length in words determines how many "slots" each occurrence fills.
			$kw_word_count  = str_word_count( $focus_keyword );
			$avg_words      = (int) round( ( (int) $min_words + (int) $max_words ) / 2 );
			$kw_min_count   = max( 8, (int) round( $avg_words * 0.01 / max( 1, $kw_word_count ) ) );
			$kw_max_count   = max( $kw_min_count + 4, (int) round( $avg_words * 0.015 / max( 1, $kw_word_count ) ) );

			$prompt .= "\nPFLICHT — FOKUS-KEYWORD PLATZIERUNG (KRITISCH FÜR SEO / RANK MATH, NICHT OPTIONAL):\n"
				. "- Das Fokus-Keyword \"" . $focus_keyword . "\" MUSS WÖRTLICH und in EXAKT dieser Schreibweise (Groß-/Kleinschreibung egal) im H1-Titel vorkommen.\n"
				. "- Das Fokus-Keyword MUSS WÖRTLICH im allerersten Satz des Einleitungsabsatzes erscheinen — innerhalb der ersten 10 Wörter nach dem H1.\n"
				. "- Das Fokus-Keyword MUSS wörtlich in mindestens EINER H2-Überschrift vorkommen.\n"
				. "- KEYWORD-DICHTE (SEHR WICHTIG): Das Fokus-Keyword MUSS insgesamt " . $kw_min_count . "–" . $kw_max_count . " Mal im gesamten Fließtext wörtlich erscheinen.\n"
				. "  Das entspricht einer Keyword-Dichte von ca. 1–1,5 %, was Rank Math als optimal bewertet.\n"
				. "  Verteile das Keyword gleichmäßig über den gesamten Text (Einleitung, Mitte, Schluss), nicht gehäuft in einem Abschnitt.\n"
				. "- Verwende AUSSCHLIESSLICH die exakte Schreibweise des Fokus-Keywords, KEINE Synonyme, KEINE Umstellungen, KEINE Abkürzungen als Ersatz.\n"
				. "- Zähle vor der Ausgabe selbst: Kommt das Keyword mindestens " . $kw_min_count . " Mal vor? Falls nicht, füge es an natürlichen Stellen ein.\n"
				. "- Diese Regeln haben höchste Priorität.";
		}

		// Internal link suggestions (to fix RankMath "no internal links" warning).
		if ( ! empty( $topic['internal_links'] ) && is_array( $topic['internal_links'] ) ) {
			$prompt .= "\n\nPFLICHT — INTERNE VERLINKUNG (WICHTIG FÜR SEO):\n"
				. "- Baue 2–4 der folgenden bereits veröffentlichten Beiträge als natürliche interne Links in den Fließtext ein.\n"
				. "- Wähle BEVORZUGT die Beiträge mit dem Hinweis \"Keyword-Match\" oder \"Titel-Match\" aus — diese sind am thematisch nächsten.\n"
				. "- NUR wenn ein Beitrag inhaltlich wirklich nicht passt, darfst du ihn überspringen. Verlinke NIE etwas, das thematisch daneben liegt.\n"
				. "- Verlinke sie mit passenden, thematisch sinnvollen Ankertexten (NICHT \"hier klicken\", sondern beschreibende Wörter aus dem Satz).\n"
				. "- Verwende die EXAKTEN URLs (1:1) wie unten angegeben in <a href=\"...\"> tags.\n"
				. "- KEIN rel=\"nofollow\" für interne Links — interne Links sollen dofollow sein.\n"
				. "- Setze die Links thematisch passend mitten im Text, nicht als Liste am Ende.\n"
				. "VERFÜGBARE INTERNE BEITRÄGE (mit Relevanz-Hinweis):\n";
			foreach ( $topic['internal_links'] as $link ) {
				if ( ! empty( $link['title'] ) && ! empty( $link['url'] ) ) {
					$reason    = ! empty( $link['reason'] ) ? ' [' . $link['reason'] . ']' : '';
					$prompt .= '- ' . $link['title'] . ': ' . $link['url'] . $reason . "\n";
				}
			}
		}

		// External outgoing links (fixes RankMath "no outgoing links" + "all nofollow" warnings).
		$ext_link_count = $settings['external_links'] ?? 2;
		if ( (int) $ext_link_count > 0 ) {
			$prompt .= "\n\nPFLICHT — EXTERNE VERLINKUNG (WICHTIG FÜR SEO):\n"
				. "- Baue " . (int) $ext_link_count . "–" . ( (int) $ext_link_count + 1 ) . " ausgehende Links zu seriösen, thematisch passenden externen Quellen ein.\n"
				. "- Verwende vertrauenswürdige Quellen (z. B. Wikipedia, offizielle Hersteller-Seiten, Fachmagazine, Studien, Statistik-Portale wie Statista).\n"
				. "- Setze die Links thematisch passend mitten im Fließtext mit beschreibendem Ankertext (NICHT \"hier klicken\").\n"
				. "- Die externen Links MÜSSEN dofollow sein — KEIN rel=\"nofollow\" setzen.\n"
				. "- Öffne externe Links in einem neuen Tab: target=\"_blank\" rel=\"noopener\".\n"
				. "- Verlinke KEINE Konkurrenz-Seiten oder Spam-Domains.";
		}

		if ( ! empty( $cta_text ) && ! empty( $cta_url ) ) {
			$prompt .= "\n\nCALL-TO-ACTION:\n- Füge 1-2 CTA-Buttons ein mit dem Text \"" . $cta_text . '", verlinkt auf ' . $cta_url . "\n- Button-Farbe: " . $cta_color . ' (inline-style: background-color, color: white, padding, border-radius)';
		}

		// Include reference style if available
		if ( ! empty( $ref_style ) ) {
			$prompt .= "\n\n=== STILVORLAGE (WICHTIG - orientiere dich genau an diesem Stil) ===\n" . $ref_style . "\n=== ENDE STILVORLAGE ===\n\nBitte halte dich so genau wie möglich an die beschriebene Stilvorlage. Verwende die gleichen Design-Elemente, Farben und Formatierungen.";
		} else {
			$prompt .= '

FORMATIERUNGS-HINWEISE:
- Verwende Info-Karten/Boxen mit leichtem Hintergrund und abgerundeten Ecken zur Auflockerung
- Wichtige Aussagen in hervorgehobenen Boxen (z.B. mit border-left und Hintergrundfarbe)
- Nummerierte Schritte oder Tipps in visuell ansprechenden Karten
- Trennlinien zwischen Hauptabschnitten
- FAQ als klickbares Akkordeon mit <details>/<summary> HTML-Tags';
		}

		// Global affiliate links (from settings) — Pro only.
		$global_affiliates = ( wbag_is_pro() && isset( $settings['global_affiliate_links'] ) && is_array( $settings['global_affiliate_links'] ) )
			? $settings['global_affiliate_links']
			: array();

		if ( ! empty( $global_affiliates ) ) {
			$prompt .= "\n\nGLOBALE AFFILIATE-LINKS (KONTEXTBASIERT EINSETZEN):\n";
			$prompt .= "Dir stehen die folgenden Affiliate-Partner zur Verfügung. ";
			$prompt .= "Wähle NUR die Links aus, die thematisch zum Beitragsthema passen. ";
			$prompt .= "Ignoriere Links, die nicht zum Thema passen — es ist besser, KEINEN Affiliate-Link zu setzen als einen unpassenden.\n";
			$prompt .= "Integriere passende Links natürlich in den Fließtext (z. B. als Empfehlung, Vergleich oder Tipp). ";
			$prompt .= "Erstelle KEINE separate Werbe-Sektion, sondern verwebe die Links organisch in den Inhalt.\n\n";
			foreach ( $global_affiliates as $ga ) {
				$prompt .= '- ' . ( $ga['label'] ?? '' ) . ': ' . ( $ga['url'] ?? '' );
				if ( ! empty( $ga['description'] ) ) {
					$prompt .= ' (' . $ga['description'] . ')';
				}
				$prompt .= "\n";
			}
			$prompt .= "\nAlle Affiliate-Links MÜSSEN das Attribut rel=\"nofollow sponsored\" erhalten.\n";
			$prompt .= "\nTRANSPARENZHINWEIS (PFLICHT — nur wenn mindestens ein Affiliate-Link im Beitrag verwendet wird):\n";
			$prompt .= "Füge GANZ AM ENDE des Beitrags (nach dem FAQ, vor dem Schluss) folgenden Transparenzhinweis als Info-Box ein:\n";
			$prompt .= '<div style="background: #f0f9ff; border: 1px solid #bae6fd; border-radius: 8px; padding: 16px 20px; margin: 32px 0; font-size: 14px; color: #334155;">';
			$prompt .= '<strong style="display: block; margin-bottom: 6px;">&#9432; ' . __( 'Transparenzhinweis', 'wp-blog-agent' ) . '</strong>';
			$prompt .= __( 'Einige Links in diesem Beitrag sind Affiliate-Links. Wenn Sie über diese Links ein Produkt kaufen, erhalten wir eine kleine Provision. Für Sie entstehen dadurch keine Mehrkosten. Wir empfehlen nur Produkte, von denen wir überzeugt sind.', 'wp-blog-agent' );
			$prompt .= "</div>\n";
		}

		$prompt .= "\n\nSchreibe NUR den HTML-Code des Beitrags, keine Erklärungen davor oder danach.";

		return $prompt;
	}

	/**
	 * Call Claude API
	 *
	 * @param string $api_key The Claude API key.
	 * @param string $model The Claude model to use.
	 * @param string $prompt The prompt for Claude.
	 * @return array|WP_Error Response from API or error
	 *
	 * @since 1.0.0
	 */
	/**
	 * Generate an image using Nano Banana 2 (Gemini) API
	 *
	 * @param string $gemini_key The Gemini API key.
	 * @param string $prompt     The image generation prompt.
	 * @return string|WP_Error Base64-encoded image data or WP_Error on failure.
	 *
	 * @since 1.1.0
	 */
	private function generate_image( $gemini_key, $prompt ) {
		$last_error = '';

		// --- Strategy 1: Imagen 4 (predict API) — has free-tier quota (25 RPD). ---
		$imagen_models = array(
			'imagen-4.0-fast-generate-001',
			'imagen-4.0-generate-001',
		);

		foreach ( $imagen_models as $model_id ) {
			$api_url  = 'https://generativelanguage.googleapis.com/v1beta/models/' . $model_id . ':predict';
			$response = wp_remote_post(
				$api_url,
				array(
					'headers' => array(
						'Content-Type'   => 'application/json',
						'x-goog-api-key' => $gemini_key,
					),
					'body'    => wp_json_encode(
						array(
							'instances'  => array(
								array( 'prompt' => $prompt ),
							),
							'parameters' => array(
								'numberOfImages' => 1,
								'aspectRatio'    => '16:9',
							),
						)
					),
					'timeout' => 120,
				)
			);

			if ( is_wp_error( $response ) ) {
				$last_error = $response->get_error_message();
				continue; // Try next Imagen model.
			}

			$code = wp_remote_retrieve_response_code( $response );
			$body = wp_remote_retrieve_body( $response );

			if ( 429 === $code ) {
				$error_data = json_decode( $body, true );
				$last_error = $error_data['error']['message'] ?? 'HTTP 429';
				if ( strpos( $last_error, 'limit: 0' ) !== false ) {
					continue; // Zero quota — try next model.
				}
				sleep( 5 );
				continue;
			}

			if ( 200 !== $code ) {
				$error_data = json_decode( $body, true );
				$last_error = $error_data['error']['message'] ?? "HTTP $code";
				continue; // Try next model.
			}

			// Parse Imagen 4 response: predictions[].bytesBase64Encoded
			$data = json_decode( $body, true );
			unset( $body, $response ); // Free the raw response — can be several MB.

			$predictions = $data['predictions'] ?? array();
			if ( ! empty( $predictions[0]['bytesBase64Encoded'] ) ) {
				$result = array(
					'data'      => $predictions[0]['bytesBase64Encoded'],
					'mime_type' => $predictions[0]['mimeType'] ?? 'image/png',
				);
				unset( $data ); // Free parsed JSON.
				return $result;
			}
			// Alternative response format.
			$generated = $data['generatedImages'] ?? array();
			if ( ! empty( $generated[0]['image']['imageBytes'] ) ) {
				$result = array(
					'data'      => $generated[0]['image']['imageBytes'],
					'mime_type' => 'image/png',
				);
				unset( $data ); // Free parsed JSON.
				return $result;
			}
			unset( $data );

			$last_error = __( 'Keine Bilddaten in der Imagen-Antwort gefunden.', 'wp-blog-agent' ) . " ($model_id)";
		}

		// --- Strategy 2: Nano Banana models (generateContent API) — fallback. ---
		$nano_models = array(
			'gemini-2.5-flash-image',
			'gemini-3.1-flash-image-preview',
			'gemini-3-pro-image-preview',
		);

		foreach ( $nano_models as $model_id ) {
			$api_url  = 'https://generativelanguage.googleapis.com/v1beta/models/' . $model_id . ':generateContent';
			$response = wp_remote_post(
				$api_url,
				array(
					'headers' => array(
						'Content-Type'   => 'application/json',
						'x-goog-api-key' => $gemini_key,
					),
					'body'    => wp_json_encode(
						array(
							'contents'         => array(
								array(
									'parts' => array(
										array( 'text' => $prompt ),
									),
								),
							),
							'generationConfig' => array(
								'responseModalities' => array( 'TEXT', 'IMAGE' ),
							),
						)
					),
					'timeout' => 90,
				)
			);

			if ( is_wp_error( $response ) ) {
				$last_error = $response->get_error_message();
				continue;
			}

			$code = wp_remote_retrieve_response_code( $response );
			$body = wp_remote_retrieve_body( $response );

			if ( 429 === $code ) {
				$error_data = json_decode( $body, true );
				$last_error = $error_data['error']['message'] ?? 'HTTP 429';
				if ( strpos( $last_error, 'limit: 0' ) !== false ) {
					continue; // Zero quota — skip.
				}
				sleep( 5 );
				continue;
			}

			if ( 200 !== $code ) {
				$error_data = json_decode( $body, true );
				$last_error = $error_data['error']['message'] ?? "HTTP $code";
				continue;
			}

			// Parse Nano Banana response.
			$data  = json_decode( $body, true );
			unset( $body, $response ); // Free raw response.

			$parts = $data['candidates'][0]['content']['parts'] ?? array();
			foreach ( $parts as $part ) {
				if ( isset( $part['inlineData']['data'] ) ) {
					$result = array(
						'data'      => $part['inlineData']['data'],
						'mime_type' => $part['inlineData']['mimeType'] ?? 'image/png',
					);
					unset( $data );
					return $result;
				}
				if ( isset( $part['inline_data']['data'] ) ) {
					$result = array(
						'data'      => $part['inline_data']['data'],
						'mime_type' => $part['inline_data']['mime_type'] ?? 'image/png',
					);
					unset( $data );
					return $result;
				}
			}
			unset( $data );

			$last_error = __( 'Keine Bilddaten in der API-Antwort gefunden.', 'wp-blog-agent' ) . " ($model_id)";
		}

		return new WP_Error( 'gemini_error', $last_error );
	}

	/**
	 * Upload a base64 image to the WordPress media library
	 *
	 * @param array  $image_data Array with 'data' (base64) and 'mime_type'.
	 * @param string $filename   Desired filename without extension.
	 * @param int    $post_id    Optional post ID to attach the image to.
	 * @return int|WP_Error Attachment ID or WP_Error on failure.
	 *
	 * @since 1.1.0
	 */
	/**
	 * Generate SEO-optimized image metadata using Claude API
	 *
	 * @param string $post_title      The blog post title.
	 * @param string $image_type      'featured' or 'content'.
	 * @param int    $image_num       Image number (1, 2, etc.) for content images.
	 * @param array  $settings        Plugin settings.
	 * @param string $section_context Optional. The blog section text for context-aware metadata.
	 * @return array With keys: alt_text, title, caption, description.
	 *
	 * @since 1.1.0
	 */
	private function generate_image_seo_metadata( $post_title, $image_type, $image_num, $settings, $section_context = '' ) {
		$api_key    = $settings['api_key'] ?? '';
		$niche      = $settings['niche'] ?? '';
		$language   = $settings['language'] ?? 'de';

		$defaults = array(
			'alt_text'    => $post_title,
			'title'       => $post_title,
			'caption'     => '',
			'description' => '',
		);

		if ( empty( $api_key ) ) {
			return $defaults;
		}

		$type_label = ( 'featured' === $image_type )
			? 'das Vorschaubild (Featured Image)'
			: 'Inhaltsbild Nr. ' . $image_num;

		$context_block = '';
		if ( ! empty( $section_context ) ) {
			$context_block = "\n" . 'Abschnitt-Kontext (das Bild illustriert diesen Teil des Artikels):' . "\n"
				. '"""' . mb_substr( $section_context, 0, 400 ) . '"""' . "\n";
		}

		$prompt = 'Erstelle SEO-optimierte Metadaten für ' . $type_label . ' eines Blogbeitrags.

Beitragstitel: "' . $post_title . '"
' . ( $niche ? 'Nische: ' . $niche : '' )
. $context_block . '

Antworte ausschließlich im folgenden JSON-Format (keine Erklärungen, kein Markdown):
{
  "alt_text": "Beschreibender Alt-Text für Barrierefreiheit und SEO, 5-15 Wörter, enthält das Haupt-Keyword natürlich",
  "title": "Prägnanter Bildtitel mit Keyword, 3-8 Wörter",
  "caption": "Kurze Bildunterschrift die den Kontext zum Beitrag herstellt, 1 Satz",
  "description": "Ausführliche Bildbeschreibung für die Mediathek, 2-3 Sätze, SEO-relevant mit Long-Tail-Keywords"
}

Regeln:
- Sprache: Deutsch
- Alt-Text: Beschreibt was auf dem Bild zu sehen wäre, enthält Haupt-Keyword natürlich eingebaut
- Title: Kürzer als Alt-Text, keyword-fokussiert
- Caption: Kontextbezogen zum Beitragsthema' . ( ! empty( $section_context ) ? ' und speziell zum oben genannten Abschnitt' : '' ) . '
- Description: Ausführlich, mit relevanten Suchbegriffen, für Mediathek-SEO';

		$response = $this->call_claude_api(
			$api_key,
			$settings['api_model'] ?? 'claude-sonnet-4-6',
			$prompt,
			false, // JSON parsing enabled
			512
		);

		if ( is_wp_error( $response ) || ! is_array( $response ) ) {
			return $defaults;
		}

		return array(
			'alt_text'    => sanitize_text_field( ! empty( $response['alt_text'] ) ? $response['alt_text'] : $defaults['alt_text'] ),
			'title'       => sanitize_text_field( ! empty( $response['title'] ) ? $response['title'] : $defaults['title'] ),
			'caption'     => sanitize_textarea_field( $response['caption'] ?? '' ),
			'description' => sanitize_textarea_field( $response['description'] ?? '' ),
		);
	}

	/**
	 * Upload a base64 image to the WordPress media library with SEO metadata
	 *
	 * @param array  $image_data Array with 'data' (base64) and 'mime_type'.
	 * @param string $filename   Desired filename without extension.
	 * @param int    $post_id    Optional post ID to attach the image to.
	 * @param array  $seo_meta   SEO metadata: alt_text, title, caption, description.
	 * @return int|WP_Error Attachment ID or WP_Error on failure.
	 *
	 * @since 1.1.0
	 */
	private function upload_image_to_media( $image_data, $filename, $post_id = 0, $seo_meta = array() ) {
		// Determine file extension from MIME type
		$extensions = array(
			'image/png'  => 'png',
			'image/jpeg' => 'jpg',
			'image/webp' => 'webp',
		);
		$ext  = $extensions[ $image_data['mime_type'] ] ?? 'png';
		$file = $filename . '.' . $ext;

		// Decode base64 data
		$decoded = base64_decode( $image_data['data'] );
		if ( false === $decoded ) {
			return new WP_Error( 'decode_failed', __( 'Base64-Dekodierung fehlgeschlagen.', 'wp-blog-agent' ) );
		}

		// Upload to WordPress uploads directory
		$upload = wp_upload_bits( $file, null, $decoded );
		unset( $decoded ); // Free memory immediately — decoded images can be several MB.
		if ( ! empty( $upload['error'] ) ) {
			return new WP_Error( 'upload_failed', $upload['error'] );
		}

		// Create attachment post with SEO metadata
		$attachment = array(
			'post_mime_type' => $image_data['mime_type'],
			'post_title'     => ! empty( $seo_meta['title'] ) ? $seo_meta['title'] : sanitize_file_name( $filename ),
			'post_excerpt'   => $seo_meta['caption'] ?? '',    // WordPress Caption field
			'post_content'   => $seo_meta['description'] ?? '', // WordPress Description field
			'post_status'    => 'inherit',
		);

		$attach_id = wp_insert_attachment( $attachment, $upload['file'], $post_id );
		if ( is_wp_error( $attach_id ) ) {
			return $attach_id;
		}

		// Generate attachment metadata (thumbnails etc.) — memory-intensive for large PNGs.
		require_once ABSPATH . 'wp-admin/includes/image.php';
		$attach_data = wp_generate_attachment_metadata( $attach_id, $upload['file'] );
		wp_update_attachment_metadata( $attach_id, $attach_data );
		unset( $attach_data, $upload ); // Free GD/Imagick buffers.

		// Set alt text (stored as post meta, critical for SEO and accessibility)
		$alt_text = ! empty( $seo_meta['alt_text'] ) ? $seo_meta['alt_text'] : $filename;
		update_post_meta( $attach_id, '_wp_attachment_image_alt', sanitize_text_field( $alt_text ) );

		return $attach_id;
	}

	/**
	 * Generate SEO metadata (focus keyword, SEO title, meta description) from
	 * an already-written blog post using Claude.
	 *
	 * Analyzes the finished HTML content and derives the main keyword plus a
	 * click-worthy SEO title and meta description, all optimized for length
	 * and keyword placement per RankMath / Yoast conventions.
	 *
	 * @since 1.3.2
	 *
	 * @param string $title          The post title used as hint.
	 * @param string $content        The generated blog post HTML.
	 * @param array  $settings       Plugin settings.
	 * @param string $hint_keyword   Optional keyword hint from the user.
	 * @return array|WP_Error Array with focus_keyword, meta_title, meta_description on success.
	 */
	private function generate_seo_metadata_for_post( $title, $content, $settings, $hint_keyword = '' ) {
		if ( empty( $settings['api_key'] ) ) {
			return new WP_Error( 'wbag_missing_api_key', __( 'Claude API-Schlüssel fehlt.', 'wp-blog-agent' ) );
		}

		$language = ! empty( $settings['language'] ) ? sanitize_text_field( $settings['language'] ) : 'de';
		$niche    = ! empty( $settings['niche'] ) ? sanitize_text_field( $settings['niche'] ) : '';

		// Strip HTML and take a representative excerpt (intro + first sections).
		$plain = wp_strip_all_tags( $content );
		$plain = preg_replace( '/\s+/', ' ', $plain );
		$excerpt = mb_substr( $plain, 0, 4000 );

		$hint_keyword = trim( (string) $hint_keyword );
		$forced       = ! empty( $hint_keyword );

		$power_word_rule = "\n\nPFLICHT — RANK MATH TITEL-OPTIMIERUNG:\n"
			. "- Der meta_title MUSS mindestens eine Zahl enthalten (z. B. Jahreszahl 2026, Anzahl wie \"7 Tipps\", \"Top 10\", \"5 Schritte\").\n"
			. "- Der meta_title MUSS mindestens ein Power Word enthalten. Power Words sind: "
			. "ultimativ, unglaublich, erstaunlich, geheim, bewährt, sofort, kostenlos, exklusiv, essentiell, unverzichtbar, "
			. "einfach, schnell, mächtig, überraschend, effektiv, perfekt, garantiert, komplett, neu, wichtig, beste, genial, "
			. "professionell, entscheidend, bewiesen, sensationell, fantastisch, unschlagbar, beeindruckend, revolutionär.\n"
			. "- Ohne Zahl UND Power Word wird der Titel von Rank Math als schlecht bewertet. Beides ist PFLICHT.\n";

		if ( $forced ) {
			$task_lines = "Aufgabe:\n"
				. "1. Das Fokus-Keyword ist FEST VORGEGEBEN und lautet: \"" . $hint_keyword . "\". Verwende es EXAKT so (keine Synonyme, keine Umstellungen).\n"
				. "2. Erstelle einen klickstarken SEO-Titel mit 50–60 Zeichen, der das Fokus-Keyword \"" . $hint_keyword . "\" WÖRTLICH und möglichst weit vorne enthält.\n"
				. "3. Erstelle eine Meta-Beschreibung mit 140–160 Zeichen, die das Fokus-Keyword \"" . $hint_keyword . "\" WÖRTLICH enthält, konkret, aktiv und neugierig macht.\n"
				. $power_word_rule . "\n"
				. "4. Erstelle 3–6 passende Schlagwörter (Tags) für den Beitrag. Jedes Tag ist 1–3 Wörter lang, thematisch relevant und suchmaschinenfreundlich.\n"
				. $power_word_rule . "\n"
				. "WICHTIG: Antworte AUSSCHLIESSLICH mit einem einzigen validen JSON-Objekt, ohne Code-Fences, ohne Erklärungen, im folgenden Format:\n"
				. '{"focus_keyword": "' . $hint_keyword . '", "meta_title": "...", "meta_description": "...", "tags": ["Tag1", "Tag2", "Tag3"]}';
		} else {
			$task_lines = "Aufgabe:\n"
				. "1. Ermittle das THEMATISCH DOMINANTE Hauptkeyword (focus keyword) — genau ein kurzer Begriff oder eine kurze Phrase (1–4 Wörter), der den Beitrag am besten beschreibt.\n"
				. "2. Erstelle einen klickstarken SEO-Titel mit 50–60 Zeichen, der das Hauptkeyword möglichst weit vorne enthält.\n"
				. "3. Erstelle eine Meta-Beschreibung mit 140–160 Zeichen, die das Hauptkeyword enthält, konkret, aktiv und neugierig macht.\n"
				. "4. Erstelle 3–6 passende Schlagwörter (Tags) für den Beitrag. Jedes Tag ist 1–3 Wörter lang, thematisch relevant und suchmaschinenfreundlich.\n"
				. $power_word_rule . "\n"
				. "WICHTIG: Antworte AUSSCHLIESSLICH mit einem einzigen validen JSON-Objekt, ohne Code-Fences, ohne Erklärungen, im folgenden Format:\n"
				. '{"focus_keyword": "...", "meta_title": "...", "meta_description": "...", "tags": ["Tag1", "Tag2", "Tag3"]}';
		}

		$prompt = "Du bist ein SEO-Experte und analysierst einen fertigen Blogbeitrag, um optimale SEO-Metadaten zu erzeugen.\n\n"
			. "Sprache: " . $language . "\n"
			. ( $niche ? "Nische: " . $niche . "\n" : '' )
			. "Titel des Beitrags: " . $title . "\n\n"
			. "Beitragstext (gekürzt):\n" . $excerpt . "\n\n"
			. $task_lines;

		$model    = $settings['api_model'] ?? 'claude-sonnet-4-6';
		$response = $this->call_claude_api( $settings['api_key'], $model, $prompt, false, 512 );

		if ( is_wp_error( $response ) ) {
			return $response;
		}

		// Extract JSON from response (handle code fences defensively).
		$raw = is_string( $response ) ? $response : wp_json_encode( $response );
		$raw = preg_replace( '/^```(?:json)?\s*/i', '', trim( $raw ) );
		$raw = preg_replace( '/\s*```\s*$/i', '', $raw );

		// Find first { ... last } to isolate the JSON body if the model added text.
		$first = strpos( $raw, '{' );
		$last  = strrpos( $raw, '}' );
		if ( false !== $first && false !== $last && $last > $first ) {
			$raw = substr( $raw, $first, $last - $first + 1 );
		}

		$data = json_decode( $raw, true );
		if ( ! is_array( $data ) || empty( $data['focus_keyword'] ) || empty( $data['meta_title'] ) || empty( $data['meta_description'] ) ) {
			return new WP_Error( 'wbag_seo_parse_failed', __( 'SEO-Metadaten konnten nicht aus der Claude-Antwort geparst werden.', 'wp-blog-agent' ) );
		}

		$final_keyword = $forced ? $hint_keyword : sanitize_text_field( $data['focus_keyword'] );

		// Parse tags from response (array of strings).
		$tags = array();
		if ( ! empty( $data['tags'] ) && is_array( $data['tags'] ) ) {
			foreach ( $data['tags'] as $tag ) {
				$clean = sanitize_text_field( $tag );
				if ( ! empty( $clean ) ) {
					$tags[] = $clean;
				}
			}
		}

		return array(
			'focus_keyword'    => $final_keyword,
			'meta_title'       => sanitize_text_field( $data['meta_title'] ),
			'meta_description' => sanitize_text_field( $data['meta_description'] ),
			'tags'             => $tags,
		);
	}

	/**
	 * Write SEO metadata to the active SEO plugin's post meta fields.
	 *
	 * Supports RankMath, Yoast SEO and SEOPress. Selection is driven by the
	 * wbag_settings['seo_plugin'] value, with automatic fallback to whichever
	 * SEO plugin is active if the configured one isn't installed.
	 *
	 * @since 1.3.2
	 *
	 * @param int   $post_id  The post ID.
	 * @param array $seo_meta Array with focus_keyword, meta_title, meta_description.
	 * @param array $settings Plugin settings.
	 * @return string The plugin key that was written to ("rankmath"/"yoast"/"seopress"/"none").
	 */
	private function apply_seo_meta_to_post( $post_id, $seo_meta, $settings ) {
		$configured = isset( $settings['seo_plugin'] ) ? $settings['seo_plugin'] : 'none';
		$target     = $configured;

		// Auto-detect fallback: if configured plugin isn't active, try active ones.
		if ( 'none' === $target || ! class_exists( 'WBAG_RankMath' ) || ! WBAG_RankMath::is_seo_plugin_active( $target ) ) {
			foreach ( array( 'rankmath', 'yoast', 'seopress' ) as $candidate ) {
				if ( class_exists( 'WBAG_RankMath' ) && WBAG_RankMath::is_seo_plugin_active( $candidate ) ) {
					$target = $candidate;
					break;
				}
			}
		}

		if ( 'none' === $target ) {
			return 'none';
		}

		$focus_keyword    = isset( $seo_meta['focus_keyword'] ) ? $seo_meta['focus_keyword'] : '';
		$meta_title       = isset( $seo_meta['meta_title'] ) ? $seo_meta['meta_title'] : '';
		$meta_description = isset( $seo_meta['meta_description'] ) ? $seo_meta['meta_description'] : '';

		switch ( $target ) {
			case 'rankmath':
				update_post_meta( $post_id, 'rank_math_focus_keyword', $focus_keyword );
				update_post_meta( $post_id, 'rank_math_title', $meta_title );
				update_post_meta( $post_id, 'rank_math_description', $meta_description );
				break;

			case 'yoast':
				update_post_meta( $post_id, '_yoast_wpseo_focuskw', $focus_keyword );
				update_post_meta( $post_id, '_yoast_wpseo_title', $meta_title );
				update_post_meta( $post_id, '_yoast_wpseo_metadesc', $meta_description );
				break;

			case 'seopress':
				update_post_meta( $post_id, '_seopress_analysis_target_kw', $focus_keyword );
				update_post_meta( $post_id, '_seopress_titles_title', $meta_title );
				update_post_meta( $post_id, '_seopress_titles_desc', $meta_description );
				break;
		}

		// Always store a generic copy on the post for reference / debugging.
		update_post_meta( $post_id, '_wbag_seo_focus_keyword', $focus_keyword );
		update_post_meta( $post_id, '_wbag_seo_meta_title', $meta_title );
		update_post_meta( $post_id, '_wbag_seo_meta_description', $meta_description );

		return $target;
	}

	/**
	 * Generate images for a blog post and attach them
	 *
	 * Generates a featured image + content images, uploads to media library,
	 * sets featured image, and inserts content images into post.
	 *
	 * @param int    $post_id  The post ID.
	 * @param string $title    The post title.
	 * @param string $content  The post content (HTML).
	 * @param array  $settings Plugin settings.
	 * @return array Result info with 'featured_id', 'content_ids', 'errors'.
	 *
	 * @since 1.1.0
	 */
	private function generate_and_attach_images( $post_id, $title, $content, $settings ) {
		$gemini_key   = $settings['gemini_api_key'] ?? '';
		$image_style  = $settings['image_style'] ?? 'modernes, professionelles Design';
		$image_lang   = $settings['image_prompt_lang'] ?? 'en';
		$niche        = $settings['niche'] ?? '';

		$result = array(
			'featured_id' => 0,
			'content_ids' => array(),
			'errors'      => array(),
		);

		if ( empty( $gemini_key ) ) {
			$result['errors'][] = __( 'Kein Gemini API-Schlüssel konfiguriert.', 'wp-blog-agent' );
			return $result;
		}

		$slug = sanitize_title( $title );

		// ---------------------------------------------------------------
		// Build context-aware image prompts using Claude API.
		//
		// Instead of generic prompts based only on the title, we extract
		// the text around the H2 headings where images will be inserted
		// and ask Claude to generate a precise, section-specific prompt.
		// ---------------------------------------------------------------

		// Determine which H2 sections will receive images (same logic as insertion below).
		preg_match_all( '/<\/h2>/i', $content, $h2_pre_matches, PREG_OFFSET_CAPTURE );
		$total_h2_pre = ! empty( $h2_pre_matches[0] ) ? count( $h2_pre_matches[0] ) : 0;

		// Content image 1 goes after H2 #2 (index 1), image 2 after H2 #5 (index 4) or middle.
		$content_1_h2_index = ( $total_h2_pre >= 2 ) ? 1 : 0;
		$content_2_h2_index = ( $total_h2_pre >= 5 ) ? 4 : ( ( $total_h2_pre >= 3 ) ? intval( $total_h2_pre / 2 ) : max( 0, $total_h2_pre - 1 ) );

		// Extract section text for context-aware prompts.
		$intro_text     = $this->extract_h2_section_text( $content, -1, 600 ); // Text before first H2.
		$section_1_text = $this->extract_h2_section_text( $content, $content_1_h2_index, 800 );
		$section_2_text = $this->extract_h2_section_text( $content, $content_2_h2_index, 800 );

		// For the intro text (before first H2), use the beginning of the content.
		if ( empty( $intro_text ) ) {
			$intro_text = wp_strip_all_tags( mb_substr( $content, 0, 600 ) );
		}

		// Respect image_count setting: 0 = no images, 1 = featured only, 2 = featured + 1 content, 3 = featured + 2 content.
		$image_count = isset( $settings['image_count'] ) ? intval( $settings['image_count'] ) : 1;
		if ( $image_count < 1 ) {
			return $result; // No images requested.
		}

		// Generate context-aware prompts via Claude.
		$prompts = array();

		// Always generate featured image (if image_count >= 1).
		$featured_prompt = $this->generate_context_image_prompt( $intro_text, $title, 'featured', $image_style, $image_lang, $settings );
		$prompts[] = array( 'prompt' => $featured_prompt, 'name' => $slug . '-featured', 'type' => 'featured', 'num' => 0, 'context' => $intro_text );

		// Content image 1 (if image_count >= 2).
		if ( $image_count >= 2 ) {
			$content_prompt_1 = $this->generate_context_image_prompt( $section_1_text, $title, 'content', $image_style, $image_lang, $settings );
			$prompts[] = array( 'prompt' => $content_prompt_1, 'name' => $slug . '-bild-1', 'type' => 'content', 'num' => 1, 'context' => $section_1_text );
		}

		// Content image 2 (if image_count >= 3).
		if ( $image_count >= 3 ) {
			$content_prompt_2 = $this->generate_context_image_prompt( $section_2_text, $title, 'content', $image_style, $image_lang, $settings );
			$prompts[] = array( 'prompt' => $content_prompt_2, 'name' => $slug . '-bild-2', 'type' => 'content', 'num' => 2, 'context' => $section_2_text );
		}

		foreach ( $prompts as $img ) {
			// Step 1: Generate the image via Gemini
			$image_data = $this->generate_image( $gemini_key, $img['prompt'] );

			if ( is_wp_error( $image_data ) ) {
				$result['errors'][] = sprintf(
					/* translators: %1$s: image name, %2$s: error message */
					__( 'Bild "%1$s" fehlgeschlagen: %2$s', 'wp-blog-agent' ),
					$img['name'],
					$image_data->get_error_message()
				);
				continue;
			}

			// Step 2: Generate SEO metadata via Claude (with section context)
			$seo_meta = $this->generate_image_seo_metadata( $title, $img['type'], $img['num'], $settings, $img['context'] );

			// Step 3: Upload to WordPress with full SEO metadata
			$attach_id = $this->upload_image_to_media( $image_data, $img['name'], $post_id, $seo_meta );
			unset( $image_data ); // Free large base64 payload from memory.

			if ( is_wp_error( $attach_id ) ) {
				$result['errors'][] = sprintf(
					/* translators: %1$s: image name, %2$s: error message */
					__( 'Upload "%1$s" fehlgeschlagen: %2$s', 'wp-blog-agent' ),
					$img['name'],
					$attach_id->get_error_message()
				);
				continue;
			}

			if ( 'featured' === $img['type'] ) {
				set_post_thumbnail( $post_id, $attach_id );
				$result['featured_id'] = $attach_id;
			} else {
				$result['content_ids'][] = $attach_id;
			}

			// Force garbage collection between images to stay within 256M memory limit.
			if ( function_exists( 'gc_collect_cycles' ) ) {
				gc_collect_cycles();
			}
		}

		// ---------------------------------------------------------------
		// Insert images into the post body using Gutenberg block markup.
		//
		// This produces proper WordPress block comments so that images
		// render with "alignwide" alignment — matching the wide layout
		// of production themes (e.g. Geekfolio / Elementor).
		// ---------------------------------------------------------------
		$updated_content = $content;

		// NOTE: The featured image is NOT inserted into the post content.
		// It is already set via set_post_thumbnail() (line above) and most
		// themes (including Elementor-based ones like Geekfolio) display it
		// automatically. Embedding it in the content would cause duplication.

		// Step B: Insert content images after H2 headings using Gutenberg block markup.
		if ( ! empty( $result['content_ids'] ) ) {
			// Find H2 headings to insert images after
			preg_match_all( '/<\/h2>/i', $updated_content, $h2_matches, PREG_OFFSET_CAPTURE );

			if ( ! empty( $h2_matches[0] ) ) {
				$insert_points = array();
				$total_h2      = count( $h2_matches[0] );

				// Insert first image after the 2nd H2, second image after the 5th H2 (or middle)
				if ( $total_h2 >= 2 ) {
					$insert_points[] = $h2_matches[0][1][1] + strlen( $h2_matches[0][1][0] );
				}
				if ( $total_h2 >= 5 ) {
					$insert_points[] = $h2_matches[0][4][1] + strlen( $h2_matches[0][4][0] );
				} elseif ( $total_h2 >= 3 ) {
					$mid = intval( $total_h2 / 2 );
					$insert_points[] = $h2_matches[0][ $mid ][1] + strlen( $h2_matches[0][ $mid ][0] );
				}

				// Sort insert points in reverse order to not break offsets
				rsort( $insert_points );

				$img_index = count( $result['content_ids'] ) - 1;
				foreach ( $insert_points as $offset ) {
					if ( $img_index < 0 ) {
						break;
					}
					$aid         = $result['content_ids'][ $img_index ];
					$img_url     = wp_get_attachment_url( $aid );
					$img_alt     = get_post_meta( $aid, '_wp_attachment_image_alt', true );
					$img_caption = get_post( $aid )->post_excerpt ?? '';

					// Build Gutenberg image block with wide alignment.
					$img_html = "\n" . '<!-- wp:image {"id":' . $aid . ',"sizeSlug":"full","linkDestination":"none","align":"wide"} -->' . "\n"
						. '<figure class="wp-block-image alignwide size-full">'
						. '<img src="' . esc_url( $img_url ) . '" alt="' . esc_attr( $img_alt ) . '" class="wp-image-' . $aid . '"/>';
					if ( ! empty( $img_caption ) ) {
						$img_html .= '<figcaption class="wp-element-caption">' . esc_html( $img_caption ) . '</figcaption>';
					}
					$img_html .= '</figure>' . "\n"
						. '<!-- /wp:image -->' . "\n";

					$updated_content = substr_replace( $updated_content, $img_html, $offset, 0 );
					$img_index--;
				}
			} else {
				// Fallback: append images at the end
				foreach ( $result['content_ids'] as $aid ) {
					$img_url     = wp_get_attachment_url( $aid );
					$img_alt     = get_post_meta( $aid, '_wp_attachment_image_alt', true );
					$img_caption = get_post( $aid )->post_excerpt ?? '';

					$updated_content .= "\n" . '<!-- wp:image {"id":' . $aid . ',"sizeSlug":"full","linkDestination":"none","align":"wide"} -->' . "\n"
						. '<figure class="wp-block-image alignwide size-full">'
						. '<img src="' . esc_url( $img_url ) . '" alt="' . esc_attr( $img_alt ) . '" class="wp-image-' . $aid . '"/>';
					if ( ! empty( $img_caption ) ) {
						$updated_content .= '<figcaption class="wp-element-caption">' . esc_html( $img_caption ) . '</figcaption>';
					}
					$updated_content .= '</figure>' . "\n"
						. '<!-- /wp:image -->' . "\n";
				}
			}
		}

		// Update the post with images inserted
		wp_update_post(
			array(
				'ID'           => $post_id,
				'post_content' => $updated_content,
			)
		);

		return $result;
	}

	/**
	 * Call Claude API
	 *
	 * @param string $api_key The Claude API key.
	 * @param string $model   The model to use.
	 * @param string $prompt  The prompt.
	 * @param bool   $raw     If true, return raw text without JSON parsing.
	 * @param int    $max_tokens Maximum tokens for the response.
	 * @return mixed Parsed JSON array, string, or WP_Error.
	 *
	 * @since 1.0.0
	 */

	/**
	 * Extract the text content of a specific H2 section from the blog HTML.
	 *
	 * Returns the text between the given H2 and the next H2 (or end of content),
	 * stripped of HTML tags, trimmed to a maximum length.
	 *
	 * @param string $content  Full blog HTML content.
	 * @param int    $h2_index Zero-based index of the H2 heading (0 = first H2, 1 = second H2, etc.).
	 * @param int    $max_len  Maximum character length of the extracted text.
	 * @return string Plain text of the section, or empty string if the H2 doesn't exist.
	 *
	 * @since 1.2.0
	 */
	private function extract_h2_section_text( $content, $h2_index, $max_len = 800 ) {
		// Split content by H2 tags — each segment starts after an </h2>.
		$parts = preg_split( '/<h2[^>]*>.*?<\/h2>/is', $content );
		// $parts[0] = text before first H2, $parts[1] = text after first H2, etc.
		// Special case: $h2_index = -1 means "text before first H2" (intro/featured).
		$section_index = $h2_index + 1;
		if ( ! isset( $parts[ $section_index ] ) ) {
			return '';
		}

		$section_text = wp_strip_all_tags( $parts[ $section_index ] );
		$section_text = preg_replace( '/\s+/', ' ', trim( $section_text ) );

		// For regular H2 sections (not the intro), prepend the H2 heading text.
		$heading = '';
		if ( $h2_index >= 0 ) {
			preg_match_all( '/<h2[^>]*>(.*?)<\/h2>/is', $content, $h2_texts );
			$heading = isset( $h2_texts[1][ $h2_index ] ) ? wp_strip_all_tags( $h2_texts[1][ $h2_index ] ) : '';
		}

		if ( mb_strlen( $section_text ) > $max_len ) {
			$section_text = mb_substr( $section_text, 0, $max_len ) . '…';
		}

		return $heading ? $heading . ': ' . $section_text : $section_text;
	}

	/**
	 * Generate a context-aware image prompt using Claude API.
	 *
	 * Instead of using only the blog title, this method sends the section text
	 * surrounding the image insertion point to Claude, which returns a precise,
	 * descriptive image generation prompt tailored to the specific content.
	 *
	 * @param string $section_text  Plain text of the blog section where the image will be placed.
	 * @param string $title         The full blog post title.
	 * @param string $image_type    'featured' or 'content'.
	 * @param string $image_style   User-configured style preference.
	 * @param string $image_lang    'en' or 'de' — language for the generated prompt.
	 * @param array  $settings      Plugin settings (needs api_key, api_model).
	 * @return string The generated image prompt, or a generic fallback on failure.
	 *
	 * @since 1.2.0
	 */
	private function generate_context_image_prompt( $section_text, $title, $image_type, $image_style, $image_lang, $settings ) {
		$api_key = $settings['api_key'] ?? '';
		$niche   = $settings['niche'] ?? '';

		// Anti-text instruction — strongly worded to prevent text rendering in images.
		$no_text_rule = 'CRITICAL: The image must contain absolutely NO text, NO letters, NO words, NO numbers, NO typography, NO watermarks, NO labels, NO captions, NO signs with writing. Pure visual/photographic content only.';

		// Fallback prompt if Claude call fails.
		$style_suffix = $image_style ? ' Style: ' . $image_style . '.' : '';
		$fallback     = ( 'featured' === $image_type )
			? 'Create a high-quality, professional featured image for: "' . $title . '". ' . $no_text_rule . $style_suffix
			: 'Create an illustrative image for a blog section about: "' . mb_substr( $section_text, 0, 150 ) . '". ' . $no_text_rule . $style_suffix;

		if ( empty( $api_key ) || empty( $section_text ) ) {
			return $fallback;
		}

		$lang_instruction = ( 'de' === $image_lang )
			? 'Schreibe den Bild-Prompt auf Englisch (Imagen versteht Englisch am besten).'
			: 'Write the image prompt in English.';

		$claude_prompt = 'Du bist ein Experte für KI-Bildgenerierung (Imagen 4 / Stable Diffusion). '
			. 'Erstelle einen präzisen, detaillierten Bild-Prompt basierend auf dem folgenden Blog-Abschnitt.' . "\n\n"
			. 'Beitragstitel: "' . $title . '"' . "\n"
			. ( $niche ? 'Nische/Branche: ' . $niche . "\n" : '' )
			. 'Bildtyp: ' . ( 'featured' === $image_type ? 'Featured Image (Hauptbild, repräsentiert den gesamten Artikel)' : 'Inhaltsbild (illustriert diesen spezifischen Abschnitt)' ) . "\n"
			. ( $image_style ? 'Gewünschter Stil: ' . $image_style . "\n" : '' ) . "\n"
			. 'Blog-Abschnitt:' . "\n"
			. '"""' . "\n"
			. $section_text . "\n"
			. '"""' . "\n\n"
			. 'Regeln:' . "\n"
			. '- ' . $lang_instruction . "\n"
			. '- Der Prompt soll eine konkrete visuelle Szene beschreiben, die den Abschnitt illustriert.' . "\n"
			. '- Nenne spezifische Objekte, Farben, Perspektiven, Lichtverhältnisse.' . "\n"
			. '- KEINE abstrakten Begriffe wie "Innovation" oder "Zukunft" — stattdessen konkrete visuelle Elemente.' . "\n"
			. '- ' . $no_text_rule . "\n"
			. '- Maximal 2-3 Sätze.' . "\n\n"
			. 'Antworte NUR mit dem Bild-Prompt, ohne Anführungszeichen, ohne Erklärung, ohne Präfix.';

		$response = $this->call_claude_api(
			$api_key,
			$settings['api_model'] ?? 'claude-sonnet-4-6',
			$claude_prompt,
			true, // raw text response
			256
		);

		if ( is_wp_error( $response ) || empty( $response ) ) {
			return $fallback;
		}

		// Append the no-text instruction to Claude's prompt to reinforce it.
		$generated_prompt = trim( $response );
		$generated_prompt .= ' ' . $no_text_rule . $style_suffix;

		return $generated_prompt;
	}

	private function call_claude_api( $api_key, $model, $prompt, $raw = false, $max_tokens = 4096 ) {
		// Validate model ID — old/invalid model IDs cause "credit balance too low" errors
		$valid_models = array( 'claude-sonnet-4-6', 'claude-opus-4-6', 'claude-haiku-4-5-20251001' );
		if ( ! in_array( $model, $valid_models, true ) ) {
			$model = 'claude-sonnet-4-6';
		}

		$max_retries    = 3;
		$retry_delay    = 5; // seconds
		$last_error_msg = '';

		for ( $attempt = 1; $attempt <= $max_retries; $attempt++ ) {
			$response = wp_remote_post(
				'https://api.anthropic.com/v1/messages',
				array(
					'headers' => array(
						'Content-Type'      => 'application/json',
						'x-api-key'         => $api_key,
						'anthropic-version' => '2023-06-01',
					),
					'body'    => wp_json_encode(
						array(
							'model'      => $model,
							'max_tokens' => $max_tokens,
							'messages'   => array(
								array(
									'role'    => 'user',
									'content' => $prompt,
								),
							),
						)
					),
					'timeout' => $max_tokens > 4096 ? 480 : 180,
				)
			);

			if ( is_wp_error( $response ) ) {
				$last_error_msg = $response->get_error_message();
				if ( $attempt < $max_retries ) {
					sleep( $retry_delay * $attempt );
					continue;
				}
				return $response;
			}

			$response_code = wp_remote_retrieve_response_code( $response );
			$response_body = wp_remote_retrieve_body( $response );

			// Retry on overloaded (529) or rate limited (429) or server error (500+)
			if ( in_array( $response_code, array( 429, 529 ), true ) || $response_code >= 500 ) {
				$error_data     = json_decode( $response_body, true );
				$last_error_msg = $error_data['error']['message'] ?? "HTTP $response_code";
				if ( $attempt < $max_retries ) {
					sleep( $retry_delay * $attempt );
					continue;
				}
				return new WP_Error( 'api_error', sprintf(
					/* translators: 1: error message, 2: number of retries */
					__( '%1$s (nach %2$d Versuchen)', 'wp-blog-agent' ),
					$last_error_msg,
					$max_retries
				) );
			}

			if ( 200 !== $response_code ) {
				$error_data    = json_decode( $response_body, true );
				$error_message = $error_data['error']['message'] ?? __( 'Unknown API error', 'wp-blog-agent' );
				return new WP_Error( 'api_error', $error_message );
			}

			// Success — break out of retry loop
			break;
		}

		$data = json_decode( $response_body, true );
		unset( $response_body, $response ); // Free raw API response.
		$content = $data['content'][0]['text'] ?? '';
		unset( $data ); // Free parsed JSON.

		// If raw mode requested, return plain text without JSON parsing
		if ( $raw ) {
			return $content;
		}

		// Try to extract and parse JSON from response
		$json_content = trim( $content );

		// Remove markdown code blocks if present (```json ... ``` or ``` ... ```)
		if ( preg_match( '/```(?:json)?\s*([\s\S]*?)```/i', $json_content, $matches ) ) {
			$json_content = trim( $matches[1] );
		}

		// Try to find JSON array or object anywhere in the text
		if ( preg_match( '/(\[[\s\S]*\]|\{[\s\S]*\})/s', $json_content, $matches ) ) {
			$parsed = json_decode( $matches[1], true );
			if ( is_array( $parsed ) ) {
				return $parsed;
			}
		}

		// Direct JSON decode as fallback
		$parsed = json_decode( $json_content, true );
		if ( is_array( $parsed ) ) {
			return $parsed;
		}

		return $content;
	}
}
