<?php

defined( 'ABSPATH' ) || exit;

class AIEO_Merchant_Chat {

	
	const MENU_SLUG = 'aieo-insights-chat';

	
	const TRANSIENT_PREFIX = 'aieo_merchant_chat_msgs_';

	
	const NONCE_ACTION = 'aieo_merchant_chat';

	
	const MAX_ITERATIONS = 8;

	
	const DEFAULT_MODEL = 'claude-sonnet-4-5';

	
	const SYSTEM_PROMPT_HEADER = <<<'PROMPT'
You are AIEO Insights — an analytics assistant embedded inside a WordPress merchant's wp-admin. Your job is to answer questions about their store using ONLY data returned by the available tools. Never invent numbers, never speculate beyond what the data shows.

# How to work

1. Read the merchant's question carefully.
2. Pick the smallest set of tools that answers it. Most questions need 1–3 tool calls. Audit-class questions need 3–6.
3. Call tools in parallel when they don't depend on each other.
4. Once you have the data, write the answer in Markdown.

# Skills you can run

When the merchant asks one of these classes of questions, follow the steps below:

- "audience priorities / what F18 told us about the silent base" → call `aieo_list_audiences`, then `aieo_get_audience_priorities` for the top 2–3 audiences. If a specific customer is named, also call `aieo_get_customer_archetype`.
- "are products tagged with needs / which products miss needs" → call `aieo_list_needs` first to see the taxonomy, then `aieo_find_uncovered_products` and/or `aieo_find_need_gaps`. Surface the top-selling uncovered products as the priority list.
- "which recommendation rails convert" → call `aieo_get_reco_impressions` + `aieo_get_reco_conversion` for the requested period, then `aieo_top_converting_pairs` for the best pairs. Compute conversion = cart-adds / impressions per surface.
- "which archive pages drive cart-adds" → call `aieo_cart_adds_by_source` + `aieo_archive_conversion_rates` + `aieo_dead_archive_detection`. Cross-reference: a high-traffic archive with low conversion is a merchandising fix candidate; zero-cart-add archives are dead-zone candidates.
- "customer X — who are lookalikes / what to recommend" → call `aieo_get_customer_profile` for X, then `aieo_find_lookalikes` (always with `limit: 10` minimum), then `aieo_recommend_products_for_customer`.
- "brand portfolio review / underperforming brands" → call `aieo_list_brands` with `order: sales_desc`, then `aieo_brand_performance_tiers`, then `aieo_brand_gift_coverage`. Cross-reference top-tier with no gift coverage as the priority list.
- "star ratings distribution / where to seed votes" → call `aieo_get_rating_leaderboard` + `aieo_rating_coverage_by_category` + `aieo_find_products_needing_ratings`. The find-products result is your seeding shortlist.
- "abandoned carts / who abandoned / lost cart value / cart recovery / most-dropped products" → call `aieo_abandoned_cart_triage` (use `min_value` to surface high-value carts) and `aieo_cart_removal_analysis` for what shoppers add then remove. That pair is the live session view. For the permanent record (open / converted / expired carts, conversion rate, accumulated lost revenue, and the most-abandoned products) point the merchant to the **Saved & Abandoned Carts** report (see "Pointing to admin features" below) — it captures WooCommerce block-checkout drafts and tracks each through to converted or expired.

## Generic WooCommerce questions

When the merchant asks generic store questions (revenue, refunds, channels, payment methods, shipping, customers, inventory, coupons, tax) the WooCommerce-Claude plugin exposes its own analytics tools under prefixes `wc_analytics_*` and `woocommerce_claude_*`. Use those tools for generic WC questions and `aieo_*` tools for the AIEO-specific surfaces (needs, audiences, ratings, attribution, recommendation surfaces, brand vitals). Examples:

- "How did the store do this week?" → `wc_analytics_analytics_totals` + `wc_analytics_analytics_breakdown` (call them via the weekly-store-review pattern WC Claude defines).
- "What's my revenue this month?" → `wc_analytics_analytics_totals` with `period: last_30_days`.
- "Top products by revenue last 30 days" → `wc_analytics_analytics_rows` with `dimension: product`.
- "Refund rate / refund pressure" → `wc_analytics_analytics_totals` (refunds slice) + `wc_analytics_analytics_rows`.
- "New vs returning customer mix" → `wc_analytics_analytics_breakdown` with the customer-acquisition dimensions.
- "Failed / on-hold order triage" → `wc_analytics_analytics_rows` with order-status dimensions.
- "AI readiness audit of my catalog" → `woocommerce_claude_get_readiness_score` + `woocommerce_claude_get_recommendations`.
- "Catalog completeness / store profile" → `woocommerce_claude_get_store_profile` + `woocommerce_claude_search_products`.

Cross-skill questions ("what changed this week?", "what are my 3 next actions?") may combine BOTH AIEO and WC tools — use whichever produces useful data, then synthesise.

## Content & blog questions

For content/blog ideation questions ("suggest summer content", "what trending search terms could I write about", "draft a gift-guide outline") you don't have specific tools — answer from your training knowledge, but always reference the merchant's actual product/brand mix when available by calling `aieo_list_brands` or `aieo_list_needs` first so suggestions are concrete to THIS store, not generic. For blog-post performance questions ("top blog posts by views"), no tool exists yet — say so briefly and offer to discuss content strategy instead.

For free-form questions, pick the most relevant 1–3 tools and chain them. If the merchant asks for ROAS / paid-channel data, say "I don't have access to ad-platform data, only AIEO + WooCommerce."

## Pointing to admin features

You don't reconfigure the store yourself, but when a merchant asks "how/where do I…" about a feature, give a one- or two-line pointer to where it lives, then return to the data:

- Reorder or reposition product-page elements — title, price, add-to-cart, rating, short description, brand, SKU/barcode, categories, and the info modules (waitlist, gifts, payment logos, trust badges, etc.) — including placing modules UNDER the product image: **e-shop Manager → Storefront Display → Dashboard → "Product info-column layout — order & placement"** (drag-and-drop; one list governs both the FSE roosterx theme and classic botiga/femmefatale themes).
- Permanent saved / shared / abandoned cart record + lost-demand report: **e-shop Manager → Catalog Sync → Saved & Abandoned Carts**.
- Choose products for a discount, gift, or free-shipping rule (include or exclude specific products / variations): use the **Browse / choose products** picker on those offer forms (Pricing & Offers).

# Output rules

- Return Markdown that renders cleanly in wp-admin (`#`, `##`, lists, **bold**, tables with `|`).
- Always cite numbers from tool results — show the tool's output value AS the number, never round aggressively.
- If a tool returns empty: say so, explain what the merchant could check (typically: data not yet populated, or no events in the chosen period).
- Pseudonymise customers — never name or email, always "Customer #N" using the `customer_id`.
- Be direct and brief. 2–5 sentences for simple questions; ~200 words for review/audit-class questions.
- End with one "Next action" line when relevant.

# Don't

- Don't fabricate metrics.
- Don't reveal raw SQL or table names.
- Don't apologise for limitations more than once per response.
- Don't ask follow-up questions unless absolutely necessary — answer with what you have.

# Available tools

The full list with one-line purposes is below. Each tool's complete input schema is on its definition (parallel-tool-call section above).

PROMPT;

	
	public static function init() {
		
		
		
		
		add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_assets' ) );
		add_action( 'wp_ajax_aieo_merchant_chat_send', array( __CLASS__, 'ajax_send_message' ) );
		add_action( 'wp_ajax_aieo_merchant_chat_clear', array( __CLASS__, 'ajax_clear' ) );
	}

	
	public static function enqueue_assets( $hook ) {
		
		
		
		
		
		$page    = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : '';
		
		$dmm_tab = isset( $_GET['dmm_tab'] ) ? sanitize_key( wp_unslash( $_GET['dmm_tab'] ) ) : '';
		if ( 'ai-eshop-dmm' !== $page || 'insights_chat' !== $dmm_tab ) {
			return;
		}

		
		
		if ( defined( 'AIEO_URI' ) && defined( 'AIEO_VERSION' ) ) {
			wp_enqueue_style(
				'aieo-admin-panel',
				AIEO_URI . 'assets/css/aieo-admin-panel.css',
				array(),
				AIEO_VERSION
			);
		}
		if ( defined( 'AIEO_CHAT_URI' ) && defined( 'AIEO_CHAT_VERSION' ) ) {
			wp_enqueue_style(
				'aieo-chat-admin',
				AIEO_CHAT_URI . 'assets/css/chat-admin.css',
				array(),
				AIEO_CHAT_VERSION
			);
		}

		
		wp_register_style( 'aieo-merchant-chat', false, array(), defined( 'AIEO_VERSION' ) ? AIEO_VERSION : '1.0' );
		wp_enqueue_style( 'aieo-merchant-chat' );
		wp_add_inline_style( 'aieo-merchant-chat', self::inline_css() );

		
		wp_register_script( 'aieo-merchant-chat', '', array( 'jquery' ), defined( 'AIEO_VERSION' ) ? AIEO_VERSION : '1.0', true );
		wp_enqueue_script( 'aieo-merchant-chat' );
		wp_localize_script(
			'aieo-merchant-chat',
			'AIEOMerchantChat',
			array(
				'ajaxUrl'      => admin_url( 'admin-ajax.php' ),
				'nonce'        => wp_create_nonce( self::NONCE_ACTION ),
				'thinking'     => __( 'Analysing your store data…', 'ai-eshop-optimizer' ),
				'error'        => __( 'Something went wrong:', 'ai-eshop-optimizer' ),
				'clearConf'    => __( 'Clear the conversation? This cannot be undone.', 'ai-eshop-optimizer' ),
				'unknownError' => __( 'Unknown error.', 'ai-eshop-optimizer' ),
				'networkError' => __( 'Network error.', 'ai-eshop-optimizer' ),
				'emptyPrompt'  => __( 'Your conversation starts here. Ask a question to begin.', 'ai-eshop-optimizer' ),
			)
		);
		wp_add_inline_script( 'aieo-merchant-chat', self::inline_js() );
	}

	
	public static function render_page() {
		if ( ! current_user_can( 'manage_woocommerce' ) ) {
			wp_die( esc_html__( 'You do not have permission to access this page.', 'ai-eshop-optimizer' ) );
		}

		$user_id      = get_current_user_id();
		$stored       = get_transient( self::TRANSIENT_PREFIX . $user_id );
		$history      = is_array( $stored ) ? $stored : array();
		$has_key      = (bool) self::get_anthropic_api_key();

		
		
		
		
		
		
		
		$suggestion_groups = array(
			
			
			
			
			
			array(
				'label'     => __( 'Store analytics (WooCommerce)', 'ai-eshop-optimizer' ),
				'questions' => array(
					__( 'How did the store do this week?', 'ai-eshop-optimizer' ),
					__( 'What\'s my revenue this month vs last month?', 'ai-eshop-optimizer' ),
					__( 'Show me my top products by revenue (last 30 days)', 'ai-eshop-optimizer' ),
					__( 'What\'s my refund rate and where is refund pressure?', 'ai-eshop-optimizer' ),
					__( 'New vs returning customer mix this quarter', 'ai-eshop-optimizer' ),
					__( 'Triage my failed and on-hold orders', 'ai-eshop-optimizer' ),
					__( 'Which payment methods carry the most paid revenue?', 'ai-eshop-optimizer' ),
					__( 'Which shipping methods carry the most order value?', 'ai-eshop-optimizer' ),
					__( 'Country-level revenue concentration', 'ai-eshop-optimizer' ),
					__( 'Coupon performance — which codes are driving revenue?', 'ai-eshop-optimizer' ),
					__( 'Tax reconciliation — collected vs refunded', 'ai-eshop-optimizer' ),
					__( 'Inventory risk — out-of-stock or slow movers', 'ai-eshop-optimizer' ),
					__( 'Run an AI readiness audit of my product catalog', 'ai-eshop-optimizer' ),
					__( 'Channel performance — which sources drive sales?', 'ai-eshop-optimizer' ),
					__( 'Customer lifetime value + cohort retention', 'ai-eshop-optimizer' ),
					__( 'Average order value trend over the last 90 days', 'ai-eshop-optimizer' ),
					__( 'Orders breakdown by status (completed / failed / on-hold / refunded)', 'ai-eshop-optimizer' ),
					__( 'Compare revenue this week vs last week vs same week last year', 'ai-eshop-optimizer' ),
					__( 'Top product categories by units sold this quarter', 'ai-eshop-optimizer' ),
					__( 'Cart-add → checkout → completed-order conversion rates', 'ai-eshop-optimizer' ),
					__( 'Hour-of-day heatmap — when do orders happen?', 'ai-eshop-optimizer' ),
					__( 'Which products have the highest revenue per visitor?', 'ai-eshop-optimizer' ),
					__( 'Show me discount usage — how much margin am I giving away?', 'ai-eshop-optimizer' ),
				),
			),
			array(
				'label'     => __( 'Brand portfolio', 'ai-eshop-optimizer' ),
				'questions' => array(
					__( 'Which brands are underperforming this quarter?', 'ai-eshop-optimizer' ),
					__( 'Tier my brands into top / mid / bottom performers', 'ai-eshop-optimizer' ),
					__( 'Which brands have no free-gift offer set up?', 'ai-eshop-optimizer' ),
					__( 'Show me brand revenue concentration — top 5 share of sales', 'ai-eshop-optimizer' ),
					__( 'Find brands trending up or down vs last quarter', 'ai-eshop-optimizer' ),
					__( 'Which brands have grown vs declined year-over-year?', 'ai-eshop-optimizer' ),
					__( 'Newest brands added — are any gaining traction?', 'ai-eshop-optimizer' ),
					__( 'Brand × category coverage matrix — where are catalog gaps?', 'ai-eshop-optimizer' ),
					__( 'Which brands have the best engagement-to-conversion ratio?', 'ai-eshop-optimizer' ),
					__( 'Brand cart-add rate vs revenue share — efficiency winners / losers', 'ai-eshop-optimizer' ),
					__( 'Which brands lack `pa_need` term coverage?', 'ai-eshop-optimizer' ),
					__( 'Show me brands with the highest customer repeat rate', 'ai-eshop-optimizer' ),
					__( 'Which brand pages have the most archive cart-adds?', 'ai-eshop-optimizer' ),
				),
			),
			array(
				
				
				
				
				
				
				'label'     => __( 'Promotions, launches & sales', 'ai-eshop-optimizer' ),
				'questions' => array(
					
					__( 'Show me products added in the last 30 days — any traction?', 'ai-eshop-optimizer' ),
					__( 'Newest products by cart-add count this month', 'ai-eshop-optimizer' ),
					__( 'How are new products performing vs catalog average?', 'ai-eshop-optimizer' ),
					__( 'Which new products got featured in recommendation rails?', 'ai-eshop-optimizer' ),
					__( 'Best-performing new products by view-to-cart ratio', 'ai-eshop-optimizer' ),
					__( 'Which new products have no reviews yet — seed candidates?', 'ai-eshop-optimizer' ),
					
					__( 'How effective is my complementary-products picker overall?', 'ai-eshop-optimizer' ),
					__( 'Which complementary pairings drive the most cart-adds?', 'ai-eshop-optimizer' ),
					__( 'Compare complementary vs upsell vs cross-sell conversion rates', 'ai-eshop-optimizer' ),
					__( 'Which source products have no complementary picker configured?', 'ai-eshop-optimizer' ),
					__( 'Top-selling source products without complementary recommendations', 'ai-eshop-optimizer' ),
					__( 'Complementary pairs ordered together most often', 'ai-eshop-optimizer' ),
					
					__( 'How did my "Offers of the Month" products perform vs non-sale?', 'ai-eshop-optimizer' ),
					__( 'Which sale-priced products converted best last month?', 'ai-eshop-optimizer' ),
					__( 'Show me cart-adds during the sale period vs after it ended', 'ai-eshop-optimizer' ),
					__( 'Is the discount margin justified by the cart-add uplift?', 'ai-eshop-optimizer' ),
					__( 'Which products are on sale right now and how are they doing?', 'ai-eshop-optimizer' ),
					__( 'Products that lost momentum after their sale ended', 'ai-eshop-optimizer' ),
					__( 'Show me sale-period revenue vs full-price revenue per brand', 'ai-eshop-optimizer' ),
				),
			),
			array(
				'label'     => __( 'Customers', 'ai-eshop-optimizer' ),
				'questions' => array(
					__( 'What do my silent customers probably want?', 'ai-eshop-optimizer' ),
					__( 'Show me the priority distribution across my customer base', 'ai-eshop-optimizer' ),
					__( 'Find lookalike customers similar to my top spender', 'ai-eshop-optimizer' ),
					__( 'Who are my top 10 customers by lifetime value?', 'ai-eshop-optimizer' ),
					__( 'Which customer segments are growing vs shrinking?', 'ai-eshop-optimizer' ),
					__( 'Show me customers with no purchase in the last 90 days', 'ai-eshop-optimizer' ),
					__( 'Which customers are most likely to redeem a free-gift offer?', 'ai-eshop-optimizer' ),
					__( 'What\'s the dominant priority across my F18 responders?', 'ai-eshop-optimizer' ),
					__( 'Recommend products for my best repeat customer', 'ai-eshop-optimizer' ),
					__( 'Show me one-time vs repeat customer mix', 'ai-eshop-optimizer' ),
					__( 'Which customers bought from the most distinct brands?', 'ai-eshop-optimizer' ),
					__( 'Show me abandoned carts > €50 in the last 7 days', 'ai-eshop-optimizer' ),
					__( 'Customers at risk of churn (no activity 60+ days)', 'ai-eshop-optimizer' ),
					__( 'VIP cohort — top 5% of customers by lifetime spend', 'ai-eshop-optimizer' ),
					__( 'Customers who only buy on discount', 'ai-eshop-optimizer' ),
					__( 'Find lookalikes of customers who buy a specific brand', 'ai-eshop-optimizer' ),
					__( 'Average orders per customer by segment', 'ai-eshop-optimizer' ),
					__( 'Which customers are most active in the last 7 days?', 'ai-eshop-optimizer' ),
					__( 'Customers with abandoned carts I should email today', 'ai-eshop-optimizer' ),
					__( 'Spending concentration — how much do my top 10% drive?', 'ai-eshop-optimizer' ),
					__( 'Show me the customer-priority overlap with brand affinity', 'ai-eshop-optimizer' ),
				),
			),
			array(
				'label'     => __( 'Recommendation rails', 'ai-eshop-optimizer' ),
				'questions' => array(
					__( 'Which recommendation rail converts best?', 'ai-eshop-optimizer' ),
					__( 'Show me the top converting product pairs', 'ai-eshop-optimizer' ),
					__( 'How well does the "same-needs" rail perform vs upsells?', 'ai-eshop-optimizer' ),
					__( 'Which rails have impressions but no cart-adds?', 'ai-eshop-optimizer' ),
					__( 'What\'s the impression → cart-add ratio per surface?', 'ai-eshop-optimizer' ),
					__( 'If a visitor scrolls 75%+ and stays 60s+, how likely are they to buy?', 'ai-eshop-optimizer' ),
					__( 'Which products have the steepest engagement → conversion gradient?', 'ai-eshop-optimizer' ),
					__( 'Which products appear most often as upsell targets?', 'ai-eshop-optimizer' ),
					__( 'Top anchor → target product pairs in same_needs', 'ai-eshop-optimizer' ),
					__( 'Click-through rate per rail surface', 'ai-eshop-optimizer' ),
					__( 'Rails with high traffic but low conversion — fix candidates', 'ai-eshop-optimizer' ),
					__( 'Which products show up most often in side-cart recommendations?', 'ai-eshop-optimizer' ),
					__( 'Compare related vs cross-sells vs upsells per surface', 'ai-eshop-optimizer' ),
					
					
					__( 'Products with the highest sales-per-view ratio (efficient converters)', 'ai-eshop-optimizer' ),
					__( 'Products with high traffic but low sales (low conversion outliers)', 'ai-eshop-optimizer' ),
					__( 'Compare 10 sales/10 views vs 10 sales/1000 views — show me both ends', 'ai-eshop-optimizer' ),
					__( 'Find products underperforming relative to their visit volume', 'ai-eshop-optimizer' ),
					__( 'Top 10 products by views × bottom 10 by conversion rate', 'ai-eshop-optimizer' ),
				),
			),
			array(
				'label'     => __( 'Attribution & archives', 'ai-eshop-optimizer' ),
				'questions' => array(
					__( 'What sources drive my cart-adds this week?', 'ai-eshop-optimizer' ),
					__( 'Which need / brand archive pages convert best?', 'ai-eshop-optimizer' ),
					__( 'Show me dead archive pages (traffic but no cart-adds)', 'ai-eshop-optimizer' ),
					__( 'Compare same_needs vs related vs recently_viewed surfaces', 'ai-eshop-optimizer' ),
					__( 'Which "need" archives need promotional pushes?', 'ai-eshop-optimizer' ),
					__( 'Which sources have high cart-adds AND high engagement?', 'ai-eshop-optimizer' ),
					__( 'Show me the landing-page → cart-add funnel', 'ai-eshop-optimizer' ),
					__( 'Which sources have grown month-over-month?', 'ai-eshop-optimizer' ),
					__( 'Attribution mix for variable products only', 'ai-eshop-optimizer' ),
					__( 'Top archives by cart-add count', 'ai-eshop-optimizer' ),
					__( 'Which sticky-bar / side-cart adds outperformed PDP adds?', 'ai-eshop-optimizer' ),
					__( 'Cart-adds attributed to blog posts vs product pages', 'ai-eshop-optimizer' ),
					__( 'Show me the journey for the most recent cart-add', 'ai-eshop-optimizer' ),
				),
			),
			array(
				'label'     => __( 'Product needs audit', 'ai-eshop-optimizer' ),
				'questions' => array(
					__( 'Audit my product needs coverage', 'ai-eshop-optimizer' ),
					__( 'Find products that should carry need terms but don\'t', 'ai-eshop-optimizer' ),
					__( 'Which need terms have too few products assigned?', 'ai-eshop-optimizer' ),
					__( 'Find needs with no products at all (empty taxonomy terms)', 'ai-eshop-optimizer' ),
					__( 'Suggest the right need terms for my newest products', 'ai-eshop-optimizer' ),
					__( 'Which need terms drive the highest cart-add rate?', 'ai-eshop-optimizer' ),
					__( 'Top need terms by product count', 'ai-eshop-optimizer' ),
					__( 'Need-to-need affinity — customers who bought X also bought Y', 'ai-eshop-optimizer' ),
					__( 'Which products span the most need terms?', 'ai-eshop-optimizer' ),
					__( 'Need term performance — cart-adds × visitors per need', 'ai-eshop-optimizer' ),
					__( 'Find duplicate or overlapping need terms', 'ai-eshop-optimizer' ),
					__( 'Which needs are over-represented (could be split into sub-needs)?', 'ai-eshop-optimizer' ),
				),
			),
			array(
				'label'     => __( 'Star ratings', 'ai-eshop-optimizer' ),
				'questions' => array(
					__( 'Where are my star ratings sparse?', 'ai-eshop-optimizer' ),
					__( 'Show me my top and bottom rated products', 'ai-eshop-optimizer' ),
					__( 'Which high-traffic products have zero ratings yet?', 'ai-eshop-optimizer' ),
					__( 'Which categories have the highest average rating?', 'ai-eshop-optimizer' ),
					__( 'Find products with 1-2 ratings only (need more votes)', 'ai-eshop-optimizer' ),
					__( 'Do my higher-rated products convert better?', 'ai-eshop-optimizer' ),
					__( 'Rating distribution histogram (1, 2, 3, 4, 5 stars)', 'ai-eshop-optimizer' ),
					__( 'Which brands have the highest avg rating?', 'ai-eshop-optimizer' ),
					__( 'Recent rating events — last 7 days', 'ai-eshop-optimizer' ),
					__( 'Products with sudden rating drops', 'ai-eshop-optimizer' ),
					__( 'Average rating by price tier (cheap vs premium)', 'ai-eshop-optimizer' ),
					__( 'Which rated products carry the most reviews from repeat customers?', 'ai-eshop-optimizer' ),
				),
			),
			array(
				
				
				
				
				
				'label'     => __( 'Newsletters & campaigns', 'ai-eshop-optimizer' ),
				'questions' => array(
					
					__( 'Which newsletters drove the most cart-adds?', 'ai-eshop-optimizer' ),
					__( 'Compare newsletter campaign performance over the last 90 days', 'ai-eshop-optimizer' ),
					__( 'Newsletter open rate × cart-add correlation', 'ai-eshop-optimizer' ),
					__( 'Which newsletter dispatches had the highest revenue lift?', 'ai-eshop-optimizer' ),
					__( 'Top-performing email subject lines / preview text', 'ai-eshop-optimizer' ),
					__( 'Newsletter → product affinity — which products were featured + bought', 'ai-eshop-optimizer' ),
					
					__( 'Which stories or story angles performed best in newsletters?', 'ai-eshop-optimizer' ),
					__( 'Story performance by season', 'ai-eshop-optimizer' ),
					__( 'Which newsletter sections drive the most clicks?', 'ai-eshop-optimizer' ),
					
					__( 'Which need terms appear most in high-performing newsletters?', 'ai-eshop-optimizer' ),
					__( 'Newsletters that drove cart-adds for products in a specific need', 'ai-eshop-optimizer' ),
					__( 'Per-need newsletter ROI — which needs respond to email content?', 'ai-eshop-optimizer' ),
					__( 'Compare newsletter performance by featured need category', 'ai-eshop-optimizer' ),
					
					__( 'Subscriber growth — net adds vs unsubscribes by week', 'ai-eshop-optimizer' ),
					__( 'Which audience segments engage most with newsletter content?', 'ai-eshop-optimizer' ),
					__( 'Send-time experiments — when does this list convert best?', 'ai-eshop-optimizer' ),
					__( 'Which lapsed customers re-engaged after a recent newsletter?', 'ai-eshop-optimizer' ),
					__( 'Newsletter × priority-profile crossover — what do my F18 responders click?', 'ai-eshop-optimizer' ),
				),
			),
			array(
				'label'     => __( 'Content & blog', 'ai-eshop-optimizer' ),
				'questions' => array(
					
					__( 'Suggest summer blog post ideas based on my product catalog', 'ai-eshop-optimizer' ),
					__( 'What seasonal content should I publish this month?', 'ai-eshop-optimizer' ),
					__( 'Generate 5 content angles for my top-selling brand', 'ai-eshop-optimizer' ),
					__( 'Suggest content topics for the "Ξάνοιγμα" need archive', 'ai-eshop-optimizer' ),
					__( 'What trending search terms could I write blog posts around?', 'ai-eshop-optimizer' ),
					__( 'Recommend back-to-school content for my catalogue', 'ai-eshop-optimizer' ),
					__( 'Draft a holiday gift-guide outline for my top brands', 'ai-eshop-optimizer' ),
					__( 'Generate a content calendar for the next 3 months', 'ai-eshop-optimizer' ),
					__( 'Top trending search terms in beauty / haircare this month', 'ai-eshop-optimizer' ),
					__( 'Draft an FAQ for my top-selling product', 'ai-eshop-optimizer' ),
					__( 'Repurpose my best-selling brand into a blog post outline', 'ai-eshop-optimizer' ),
					__( 'Newsletter topic suggestions for my top brand', 'ai-eshop-optimizer' ),
					__( 'Social media post ideas highlighting my top categories', 'ai-eshop-optimizer' ),
					__( 'Suggest how-to guide topics for the most popular needs', 'ai-eshop-optimizer' ),
					__( 'Generate product comparison content for my variable products', 'ai-eshop-optimizer' ),
					
					__( 'Show me my top blog posts by recent engagement', 'ai-eshop-optimizer' ),
					__( 'Which blog topics could drive traffic to my underperforming brands?', 'ai-eshop-optimizer' ),
					__( 'Which blog posts hold attention longest?', 'ai-eshop-optimizer' ),
					__( 'Which blog posts drive product cart-adds?', 'ai-eshop-optimizer' ),
				),
			),
			array(
				'label'     => __( 'Cross-skill / overview', 'ai-eshop-optimizer' ),
				'questions' => array(
					__( 'Give me a weekly store health check', 'ai-eshop-optimizer' ),
					__( 'What are my 3 highest-impact next actions?', 'ai-eshop-optimizer' ),
					__( 'Summarise everything that changed this week', 'ai-eshop-optimizer' ),
					__( 'Where is the biggest revenue opportunity I\'m missing?', 'ai-eshop-optimizer' ),
					__( 'Which products are most often added then removed?', 'ai-eshop-optimizer' ),
					__( 'Monthly executive summary across all surfaces', 'ai-eshop-optimizer' ),
					__( 'Year-over-year performance summary', 'ai-eshop-optimizer' ),
					__( 'What 3 changes would have the biggest revenue impact?', 'ai-eshop-optimizer' ),
					__( 'Show me everything that\'s broken or underperforming right now', 'ai-eshop-optimizer' ),
					__( 'Quick wins I can implement in the next 7 days', 'ai-eshop-optimizer' ),
					__( 'Compare this month against last month — what shifted?', 'ai-eshop-optimizer' ),
					__( 'Identify cross-functional bottlenecks (catalog × attribution × ratings)', 'ai-eshop-optimizer' ),
				),
			),
		);
		?>
		<div class="wrap aieo-merchant-chat-wrap">
			<div class="aieo-mchat-header">
				<h1>
					<span class="aieo-mchat-bot-avatar" aria-hidden="true">🛍</span>
					<?php esc_html_e( 'AIEO Insights Chat', 'ai-eshop-optimizer' ); ?>
				</h1>
				<p class="aieo-mchat-tagline">
					<?php esc_html_e( 'Ask anything about your store. Backed by 32 AIEO read-only analytics tools — brands, needs, customers, recommendations, attribution, ratings, blog engagement, buy-probability.', 'ai-eshop-optimizer' ); ?>
				</p>
			</div>

			<?php if ( ! $has_key ) : ?>
				<div class="notice notice-warning inline">
					<p>
						<?php
						printf(
							
							esc_html__( 'Anthropic API key not configured. %s before using Insights Chat.', 'ai-eshop-optimizer' ),
							'<a href="' . esc_url( admin_url( 'admin.php?page=ai-eshop-chat' ) ) . '">' . esc_html__( 'Set it in AI Chat → Settings', 'ai-eshop-optimizer' ) . '</a>'
						);
						?>
					</p>
				</div>
			<?php endif; ?>

			<div class="aieo-mchat-layout">
				<div class="aieo-mchat-main">
					<div id="aieo-mchat-transcript" class="aieo-mchat-transcript" aria-live="polite">
						<?php self::render_transcript( $history ); ?>
					</div>

					<form id="aieo-mchat-form" class="aieo-mchat-form" autocomplete="off">
						<textarea
							id="aieo-mchat-input"
							class="aieo-mchat-input"
							rows="3"
							placeholder="<?php esc_attr_e( 'Ask about brands, customers, needs, recommendations…', 'ai-eshop-optimizer' ); ?>"
							required></textarea>
						<div class="aieo-mchat-toolbar">
							<button type="submit" class="button button-primary aieo-mchat-send">
								<?php esc_html_e( 'Send', 'ai-eshop-optimizer' ); ?>
							</button>
							<button type="button" id="aieo-mchat-clear" class="button">
								<?php esc_html_e( 'Clear conversation', 'ai-eshop-optimizer' ); ?>
							</button>
							<span class="aieo-mchat-footer-note">
								<?php esc_html_e( 'Powered by Claude · ~$0.03 / question', 'ai-eshop-optimizer' ); ?>
							</span>
						</div>
					</form>
				</div>

				<aside class="aieo-mchat-sidebar">
					<h3><?php esc_html_e( 'Try asking', 'ai-eshop-optimizer' ); ?></h3>
					<div class="aieo-mchat-suggestion-groups">
						<?php foreach ( $suggestion_groups as $group ) : ?>
							<details class="aieo-mchat-suggestion-group">
								<summary>
									<span class="aieo-mchat-group-label"><?php echo esc_html( $group['label'] ); ?></span>
									<span class="aieo-mchat-group-count">(<?php echo count( $group['questions'] ); ?>)</span>
								</summary>
								<ul class="aieo-mchat-suggestions">
									<?php foreach ( $group['questions'] as $s ) : ?>
										<li>
											<button type="button" class="aieo-mchat-chip" data-prompt="<?php echo esc_attr( $s ); ?>">
												<?php echo esc_html( $s ); ?>
											</button>
										</li>
									<?php endforeach; ?>
								</ul>
							</details>
						<?php endforeach; ?>
					</div>
					<h3><?php esc_html_e( 'Notes', 'ai-eshop-optimizer' ); ?></h3>
					<ul class="aieo-mchat-notes">
						<li><?php esc_html_e( 'All data is read-only — the chat never modifies your store.', 'ai-eshop-optimizer' ); ?></li>
						<li><?php esc_html_e( 'Customers are pseudonymised as Customer #N.', 'ai-eshop-optimizer' ); ?></li>
						<li><?php esc_html_e( 'No ad-platform / ROAS data is available.', 'ai-eshop-optimizer' ); ?></li>
					</ul>
				</aside>
			</div>
		</div>
		<?php
	}

	
	private static function render_transcript( $history ) {
		if ( empty( $history ) ) {
			echo '<div class="aieo-mchat-empty">';
			echo '<p>' . esc_html__( 'Your conversation starts here. Ask a question to begin.', 'ai-eshop-optimizer' ) . '</p>';
			echo '</div>';
			return;
		}
		foreach ( $history as $msg ) {
			if ( ! is_array( $msg ) || empty( $msg['role'] ) ) {
				continue;
			}
			$role    = $msg['role'];
			$content = $msg['content'] ?? '';

			
			
			
			if ( 'user' === $role ) {
				if ( is_array( $content ) ) {
					
					
					continue;
				}
				$body = (string) $content;
			} elseif ( 'assistant' === $role ) {
				$body = '';
				if ( is_array( $content ) ) {
					foreach ( $content as $block ) {
						if ( isset( $block['type'] ) && 'text' === $block['type'] && isset( $block['text'] ) ) {
							$body .= $block['text'];
						}
					}
				} else {
					$body = (string) $content;
				}
				if ( '' === trim( $body ) ) {
					continue;
				}
			} else {
				continue;
			}
			self::render_bubble( $role, $body );
		}
	}

	
	public static function render_bubble( $role, $body ) {
		$is_user = ( 'user' === $role );
		$avatar  = $is_user ? '👤' : '🛍';
		$class   = $is_user ? 'aieo-mchat-bubble aieo-mchat-bubble--user' : 'aieo-mchat-bubble aieo-mchat-bubble--bot';
		$rendered = $is_user
			? esc_html( $body )
			: self::render_markdown( $body );
		?>
		<div class="<?php echo esc_attr( $class ); ?>">
			<div class="aieo-mchat-avatar" aria-hidden="true"><?php echo esc_html( $avatar ); ?></div>
			<div class="aieo-mchat-body">
				<?php
				
				echo $rendered; 
				?>
			</div>
		</div>
		<?php
	}

	
	public static function ajax_send_message() {
		check_ajax_referer( self::NONCE_ACTION, 'nonce' );
		if ( ! current_user_can( 'manage_woocommerce' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions.', 'ai-eshop-optimizer' ) ), 403 );
		}

		
		$msg = isset( $_POST['message'] ) ? sanitize_textarea_field( wp_unslash( $_POST['message'] ) ) : '';
		if ( '' === $msg ) {
			wp_send_json_error( array( 'message' => __( 'Empty message.', 'ai-eshop-optimizer' ) ), 400 );
		}

		if ( ! self::get_anthropic_api_key() ) {
			wp_send_json_error(
				array( 'message' => __( 'Anthropic API key not configured. Set it in AI Chat → Settings.', 'ai-eshop-optimizer' ) ),
				500
			);
		}

		$user_id   = get_current_user_id();
		$transient = self::TRANSIENT_PREFIX . $user_id;
		$stored    = get_transient( $transient );
		
		
		
		$history   = is_array( $stored ) ? $stored : array();
		$history[] = array(
			'role'    => 'user',
			'content' => $msg,
		);

		$result = self::run_tool_use_loop( $history, self::build_system_prompt() );
		if ( is_wp_error( $result ) ) {
			wp_send_json_error( array( 'message' => $result->get_error_message() ), 500 );
		}

		
		
		$trimmed = array_slice( $result['messages'], -20 );
		set_transient( $transient, $trimmed, HOUR_IN_SECONDS );

		wp_send_json_success(
			array(
				'text'       => $result['text'],
				'html'       => self::render_markdown( $result['text'] ),
				'tool_count' => self::count_tool_uses( $result['messages'] ),
			)
		);
	}

	
	public static function ajax_clear() {
		check_ajax_referer( self::NONCE_ACTION, 'nonce' );
		if ( ! current_user_can( 'manage_woocommerce' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions.', 'ai-eshop-optimizer' ) ), 403 );
		}
		delete_transient( self::TRANSIENT_PREFIX . get_current_user_id() );
		wp_send_json_success();
	}

	
	public static function build_system_prompt() {
		$lines    = array();
		$registry = function_exists( 'wp_get_abilities' ) ? wp_get_abilities() : array();
		ksort( $registry );
		foreach ( $registry as $name => $ability ) {
			if ( 0 !== strpos( $name, 'aieo/' ) ) {
				continue;
			}
			$anthropic_name = self::to_anthropic_tool_name( $name );
			$desc           = method_exists( $ability, 'get_description' ) ? $ability->get_description() : '';
			if ( '' === trim( (string) $desc ) && method_exists( $ability, 'get_label' ) ) {
				$desc = $ability->get_label();
			}
			$lines[] = sprintf( '- %s: %s', $anthropic_name, $desc );
		}
		return self::SYSTEM_PROMPT_HEADER . "\n" . implode( "\n", $lines );
	}

	
	public static function build_tool_list() {
		$tools    = array();
		$registry = function_exists( 'wp_get_abilities' ) ? wp_get_abilities() : array();
		
		
		
		
		
		
		
		$prefixes = (array) apply_filters( 'aieo_merchant_chat_tool_prefixes', array(
			'aieo/',
			'woocommerce-claude/',
			'wc-analytics/',
		) );
		foreach ( $registry as $name => $ability ) {
			$ok = false;
			foreach ( $prefixes as $p ) {
				if ( 0 === strpos( $name, $p ) ) { $ok = true; break; }
			}
			if ( ! $ok ) {
				continue;
			}
			$anthropic_name = self::to_anthropic_tool_name( $name );
			$desc           = method_exists( $ability, 'get_description' ) ? $ability->get_description() : '';
			if ( '' === trim( (string) $desc ) && method_exists( $ability, 'get_label' ) ) {
				$desc = $ability->get_label();
			}
			$schema = method_exists( $ability, 'get_input_schema' ) ? (array) $ability->get_input_schema() : array();
			if ( empty( $schema ) || ! isset( $schema['type'] ) ) {
				
				
				
				$schema = array(
					'type'       => 'object',
					'properties' => new stdClass(),
					'required'   => array(),
				);
			} elseif ( 'object' === $schema['type'] && ( empty( $schema['properties'] ) || ! is_array( $schema['properties'] ) || 0 === count( $schema['properties'] ) ) ) {
				$schema['properties'] = new stdClass();
			}
			$tools[] = array(
				'name'         => $anthropic_name,
				'description'  => (string) $desc,
				'input_schema' => $schema,
			);
		}
		return $tools;
	}

	
	private static function to_anthropic_tool_name( $ability_name ) {
		return str_replace( array( '/', '-' ), array( '_', '_' ), $ability_name );
	}

	
	private static function from_anthropic_tool_name( $anthropic_name ) {
		$registry = function_exists( 'wp_get_abilities' ) ? wp_get_abilities() : array();
		foreach ( array_keys( $registry ) as $name ) {
			if ( self::to_anthropic_tool_name( $name ) === $anthropic_name ) {
				return $name;
			}
		}
		return null;
	}

	
	private static function run_tool_use_loop( $messages, $system_prompt, $max_iterations = self::MAX_ITERATIONS ) {
		$tools   = self::build_tool_list();
		$api_key = self::get_anthropic_api_key();
		if ( ! $api_key ) {
			return new WP_Error(
				'no_api_key',
				__( 'Rooster Content Connector token not configured. Paste it in the Pro Panel to enable Insights Chat.', 'ai-eshop-optimizer' )
			);
		}
		$model = self::get_model();
		$messages_url = self::get_rooster_messages_url();

		for ( $i = 0; $i < $max_iterations; $i++ ) {
			$response = wp_remote_post(
				$messages_url,
				array(
					'headers' => array(
						'X-OM-API-Key' => $api_key,
						'content-type' => 'application/json',
					),
					'body'    => wp_json_encode(
						array(
							'model'      => $model,
							'max_tokens' => 4096,
							'system'     => $system_prompt,
							'tools'      => $tools,
							'messages'   => $messages,
						)
					),
					'timeout' => 90,
				)
			);

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

			$code = wp_remote_retrieve_response_code( $response );
			$body = json_decode( wp_remote_retrieve_body( $response ), true );

			if ( 200 !== (int) $code ) {
				$err = isset( $body['error']['message'] ) ? $body['error']['message'] : wp_remote_retrieve_body( $response );
				return new WP_Error(
					'anthropic_http_' . $code,
					sprintf(
						
						__( 'Anthropic API returned HTTP %1$d: %2$s', 'ai-eshop-optimizer' ),
						(int) $code,
						$err
					)
				);
			}

			if ( ! is_array( $body ) || empty( $body['content'] ) ) {
				return new WP_Error( 'anthropic_bad_body', __( 'Anthropic returned an empty/invalid response.', 'ai-eshop-optimizer' ) );
			}

			
			
			
			
			$assistant_content = self::normalise_assistant_content( $body['content'] );
			$messages[] = array(
				'role'    => 'assistant',
				'content' => $assistant_content,
			);

			$stop_reason = isset( $body['stop_reason'] ) ? $body['stop_reason'] : '';

			if ( 'tool_use' === $stop_reason ) {
				$tool_results = array();
				foreach ( $body['content'] as $block ) {
					if ( ! is_array( $block ) || empty( $block['type'] ) || 'tool_use' !== $block['type'] ) {
						continue;
					}
					$anthropic_name = isset( $block['name'] ) ? (string) $block['name'] : '';
					$ability_name   = self::from_anthropic_tool_name( $anthropic_name );
					$tool_input     = isset( $block['input'] ) ? (array) $block['input'] : array();

					if ( ! $ability_name || ! function_exists( 'wp_get_ability' ) ) {
						$result_str = 'Error: ability ' . $anthropic_name . ' not registered.';
					} else {
						$ability = wp_get_ability( $ability_name );
						if ( ! $ability ) {
							$result_str = 'Error: ability ' . $ability_name . ' not found in registry.';
						} else {
							$result = $ability->execute( $tool_input );
							if ( is_wp_error( $result ) ) {
								$result_str = 'Error: ' . $result->get_error_message();
							} else {
								$result_str = is_string( $result ) ? $result : wp_json_encode( $result );
							}
						}
					}

					$tool_results[] = array(
						'type'        => 'tool_result',
						'tool_use_id' => isset( $block['id'] ) ? $block['id'] : '',
						'content'     => (string) $result_str,
					);
				}

				
				
				
				if ( empty( $tool_results ) ) {
					$text = self::extract_text( $body['content'] );
					return array(
						'text'     => $text !== '' ? $text : __( '(no response)', 'ai-eshop-optimizer' ),
						'messages' => $messages,
					);
				}

				$messages[] = array(
					'role'    => 'user',
					'content' => $tool_results,
				);
				continue;
			}

			if ( 'end_turn' === $stop_reason || 'stop_sequence' === $stop_reason ) {
				return array(
					'text'     => self::extract_text( $body['content'] ),
					'messages' => $messages,
				);
			}

			
			
			$text = self::extract_text( $body['content'] );
			return array(
				'text'     => $text !== '' ? $text : sprintf(
					
					__( '(response stopped: %s)', 'ai-eshop-optimizer' ),
					$stop_reason
				),
				'messages' => $messages,
			);
		}

		return new WP_Error(
			'tool_loop_exhausted',
			sprintf(
				
				__( 'Tool-use loop exceeded %d iterations.', 'ai-eshop-optimizer' ),
				$max_iterations
			)
		);
	}

	
	private static function normalise_assistant_content( $content ) {
		if ( ! is_array( $content ) ) {
			return array();
		}
		$out = array();
		foreach ( $content as $block ) {
			if ( ! is_array( $block ) ) {
				continue;
			}
			if ( isset( $block['caller'] ) ) {
				unset( $block['caller'] );
			}
			if ( isset( $block['type'] ) && 'tool_use' === $block['type'] ) {
				$input = $block['input'] ?? array();
				if ( is_array( $input ) && empty( $input ) ) {
					
					
					$block['input'] = new stdClass();
				}
			}
			$out[] = $block;
		}
		return $out;
	}

	
	private static function extract_text( $content ) {
		$text = '';
		if ( ! is_array( $content ) ) {
			return $text;
		}
		foreach ( $content as $block ) {
			if ( is_array( $block ) && isset( $block['type'] ) && 'text' === $block['type'] && isset( $block['text'] ) ) {
				$text .= $block['text'];
			}
		}
		return $text;
	}

	
	private static function count_tool_uses( $messages ) {
		$n = 0;
		foreach ( $messages as $msg ) {
			if ( ! is_array( $msg ) || 'assistant' !== ( $msg['role'] ?? '' ) ) {
				continue;
			}
			$content = $msg['content'] ?? array();
			if ( ! is_array( $content ) ) {
				continue;
			}
			foreach ( $content as $block ) {
				if ( is_array( $block ) && isset( $block['type'] ) && 'tool_use' === $block['type'] ) {
					$n++;
				}
			}
		}
		return $n;
	}

	
	private static function get_anthropic_api_key() {
		return (string) get_option( 'aieo_rooster_api_key', '' );
	}

	
	private static function get_rooster_messages_url() {
		$base = (string) get_option( 'aieo_rooster_hub_url', 'https://rooster.systems' );
		if ( '' === $base ) {
			$base = 'https://rooster.systems';
		}
		return rtrim( $base, '/' ) . '/wp-json/om-portal/v1/anthropic/messages';
	}

	
	private static function get_model() {
		$model = (string) get_option( 'aieo_chat_model', '' );
		if ( '' !== $model ) {
			return $model;
		}
		return self::DEFAULT_MODEL;
	}

	
	public static function render_markdown( $markdown ) {
		$markdown = (string) $markdown;
		if ( '' === trim( $markdown ) ) {
			return '';
		}

		
		$markdown = str_replace( array( "\r\n", "\r" ), "\n", $markdown );

		
		$placeholders = array();
		$markdown     = preg_replace_callback(
			'/```([a-zA-Z0-9]*)\n([\s\S]*?)```/',
			static function ( $m ) use ( &$placeholders ) {
				$lang = $m[1] !== '' ? ' class="language-' . preg_replace( '/[^a-zA-Z0-9_-]/', '', $m[1] ) . '"' : '';
				$html = '<pre><code' . $lang . '>' . esc_html( $m[2] ) . '</code></pre>';
				$key  = '%%AIEO_MCHAT_CODE_' . count( $placeholders ) . '%%';
				$placeholders[ $key ] = $html;
				return $key;
			},
			$markdown
		);

		
		$blocks = preg_split( "/\n{2,}/", $markdown );
		$out    = array();
		foreach ( $blocks as $block ) {
			$block = rtrim( $block );
			if ( '' === $block ) {
				continue;
			}

			
			if ( preg_match( '/^(#{1,6})\s+(.+)$/m', $block, $hm ) && substr_count( $block, "\n" ) === 0 ) {
				$level = strlen( $hm[1] );
				$out[] = '<h' . $level . '>' . self::inline_md( $hm[2] ) . '</h' . $level . '>';
				continue;
			}

			
			if ( preg_match( '/^\s*\|.+\|\s*\n\s*\|[\s:|-]+\|\s*\n/', $block ) ) {
				$out[] = self::render_md_table( $block );
				continue;
			}

			
			if ( preg_match( '/^\s*[-*+]\s+/m', $block ) && self::all_lines_match( $block, '/^\s*[-*+]\s+/' ) ) {
				$items = array();
				foreach ( explode( "\n", $block ) as $line ) {
					if ( preg_match( '/^\s*[-*+]\s+(.+)$/', $line, $lm ) ) {
						$items[] = '<li>' . self::inline_md( $lm[1] ) . '</li>';
					}
				}
				$out[] = '<ul>' . implode( '', $items ) . '</ul>';
				continue;
			}

			
			if ( preg_match( '/^\s*\d+\.\s+/m', $block ) && self::all_lines_match( $block, '/^\s*\d+\.\s+/' ) ) {
				$items = array();
				foreach ( explode( "\n", $block ) as $line ) {
					if ( preg_match( '/^\s*\d+\.\s+(.+)$/', $line, $lm ) ) {
						$items[] = '<li>' . self::inline_md( $lm[1] ) . '</li>';
					}
				}
				$out[] = '<ol>' . implode( '', $items ) . '</ol>';
				continue;
			}

			
			$paragraph = self::inline_md( $block );
			$paragraph = nl2br( $paragraph );
			$out[]     = '<p>' . $paragraph . '</p>';
		}

		$html = implode( "\n", $out );

		
		foreach ( $placeholders as $key => $code_html ) {
			$html = str_replace( $key, $code_html, $html );
		}

		return wp_kses_post( $html );
	}

	
	private static function all_lines_match( $block, $regex ) {
		foreach ( explode( "\n", $block ) as $line ) {
			if ( '' === trim( $line ) ) {
				continue;
			}
			if ( ! preg_match( $regex, $line ) ) {
				return false;
			}
		}
		return true;
	}

	
	private static function render_md_table( $block ) {
		$lines = array_values( array_filter( array_map( 'trim', explode( "\n", $block ) ), 'strlen' ) );
		if ( count( $lines ) < 2 ) {
			return '<p>' . self::inline_md( $block ) . '</p>';
		}
		$split_row = static function ( $row ) {
			$row = trim( $row, " \t|" );
			return array_map( 'trim', explode( '|', $row ) );
		};

		$headers = $split_row( $lines[0] );
		$body    = array_slice( $lines, 2 ); 

		$html  = '<table class="aieo-mchat-table"><thead><tr>';
		foreach ( $headers as $h ) {
			$html .= '<th>' . self::inline_md( $h ) . '</th>';
		}
		$html .= '</tr></thead><tbody>';
		foreach ( $body as $row ) {
			$cells = $split_row( $row );
			$html .= '<tr>';
			foreach ( $cells as $c ) {
				$html .= '<td>' . self::inline_md( $c ) . '</td>';
			}
			$html .= '</tr>';
		}
		$html .= '</tbody></table>';
		return $html;
	}

	
	private static function inline_md( $text ) {
		$text = esc_html( $text );

		
		$text = preg_replace( '/`([^`]+)`/', '<code>$1</code>', $text );

		
		$text = preg_replace( '/\*\*([^*]+)\*\*/', '<strong>$1</strong>', $text );
		$text = preg_replace( '/__([^_]+)__/', '<strong>$1</strong>', $text );

		
		$text = preg_replace( '/(?<![\w*])\*([^*\n]+)\*(?![\w*])/', '<em>$1</em>', $text );
		$text = preg_replace( '/(?<![\w_])_([^_\n]+)_(?![\w_])/', '<em>$1</em>', $text );

		
		$text = preg_replace_callback(
			'/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/',
			static function ( $m ) {
				return '<a href="' . esc_url( $m[2] ) . '" target="_blank" rel="noopener">' . $m[1] . '</a>';
			},
			$text
		);

		return $text;
	}

	
	private static function inline_css() {
		return '
			.aieo-merchant-chat-wrap { max-width: 1200px; }
			.aieo-mchat-header { margin: 12px 0 20px; padding: 16px 20px; background: linear-gradient(135deg,#7b3fa5 0%,#3a5fcd 100%); color: #fff; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,.08); }
			.aieo-mchat-header h1 { color: #fff; margin: 0 0 6px; font-size: 22px; display: flex; align-items: center; gap: 10px; }
			.aieo-mchat-header h1 .aieo-mchat-bot-avatar { font-size: 26px; }
			.aieo-mchat-tagline { margin: 0; opacity: .9; font-size: 13px; }

			.aieo-mchat-layout { display: grid; grid-template-columns: minmax(0,1fr) 280px; gap: 20px; }
			@media (max-width: 980px) { .aieo-mchat-layout { grid-template-columns: 1fr; } }
			.aieo-mchat-main { background: #fff; border: 1px solid #dcdcde; border-radius: 8px; display: flex; flex-direction: column; min-height: 600px; }
			.aieo-mchat-sidebar { background: #fff; border: 1px solid #dcdcde; border-radius: 8px; padding: 16px; align-self: start; }
			.aieo-mchat-sidebar h3 { margin: 0 0 8px; font-size: 13px; text-transform: uppercase; letter-spacing: .04em; color: #50575e; }
			.aieo-mchat-sidebar h3 + h3 { margin-top: 18px; }
			.aieo-mchat-suggestions, .aieo-mchat-notes { list-style: none; margin: 0 0 8px; padding: 0; }
			.aieo-mchat-suggestions li { margin-bottom: 6px; }
			.aieo-mchat-chip { background: #f0f0f1; border: 1px solid #dcdcde; border-radius: 16px; padding: 6px 12px; cursor: pointer; font-size: 12px; text-align: left; width: 100%; }
			.aieo-mchat-chip:hover { background: #e8e8ea; }
			/* Accordion of suggestion groups — native details/summary,
			 * no JS. Closed by default so the sidebar shows just the
			 * category labels; clicking opens that groups chips. */
			.aieo-mchat-suggestion-groups { margin: 0 0 14px; }
			.aieo-mchat-suggestion-group { border: 1px solid #dcdcde; border-radius: 6px; margin-bottom: 6px; background: #fafafa; }
			.aieo-mchat-suggestion-group > summary { padding: 8px 12px; font-size: 13px; font-weight: 600; cursor: pointer; list-style: none; display: flex; align-items: center; justify-content: space-between; color: #1d2327; user-select: none; }
			.aieo-mchat-suggestion-group > summary::-webkit-details-marker { display: none; }
			.aieo-mchat-suggestion-group > summary::before { content: "▸"; display: inline-block; margin-right: 8px; transition: transform .15s ease; color: #7b3fa5; font-size: 11px; }
			.aieo-mchat-suggestion-group[open] > summary::before { transform: rotate(90deg); }
			.aieo-mchat-suggestion-group > summary:hover { background: #f0f0f1; }
			.aieo-mchat-suggestion-group .aieo-mchat-group-label { flex: 1; }
			.aieo-mchat-suggestion-group .aieo-mchat-group-count { font-size: 11px; color: #7b3fa5; font-weight: 500; }
			.aieo-mchat-suggestion-group .aieo-mchat-suggestions { padding: 6px 10px 10px; margin: 0; }
			.aieo-mchat-suggestion-group .aieo-mchat-chip { font-size: 11px; padding: 5px 10px; }
			.aieo-mchat-notes li { font-size: 12px; color: #50575e; margin-bottom: 6px; padding-left: 14px; position: relative; }
			.aieo-mchat-notes li::before { content: "•"; position: absolute; left: 0; color: #7b3fa5; }

			.aieo-mchat-transcript { flex: 1; padding: 18px; overflow-y: auto; max-height: 60vh; }
			.aieo-mchat-empty { color: #50575e; text-align: center; padding: 40px 20px; font-style: italic; }

			.aieo-mchat-bubble { display: flex; gap: 12px; margin-bottom: 16px; align-items: flex-start; }
			.aieo-mchat-bubble--user { flex-direction: row-reverse; }
			.aieo-mchat-avatar { width: 32px; height: 32px; flex: 0 0 32px; border-radius: 50%; background: #f0f0f1; display: flex; align-items: center; justify-content: center; font-size: 18px; }
			.aieo-mchat-bubble--user .aieo-mchat-avatar { background: #dbeafe; }
			.aieo-mchat-bubble--bot .aieo-mchat-avatar { background: #f3e8ff; }
			.aieo-mchat-body { background: #f6f7f7; border-radius: 10px; padding: 12px 16px; max-width: 80%; line-height: 1.55; }
			.aieo-mchat-bubble--user .aieo-mchat-body { background: #2271b1; color: #fff; }
			.aieo-mchat-bubble--user .aieo-mchat-body a { color: #fff; text-decoration: underline; }
			.aieo-mchat-body h1, .aieo-mchat-body h2, .aieo-mchat-body h3, .aieo-mchat-body h4 { margin: 12px 0 6px; line-height: 1.3; }
			.aieo-mchat-body h1 { font-size: 18px; }
			.aieo-mchat-body h2 { font-size: 16px; }
			.aieo-mchat-body h3 { font-size: 14px; }
			.aieo-mchat-body p { margin: 6px 0; }
			.aieo-mchat-body ul, .aieo-mchat-body ol { margin: 6px 0 6px 22px; }
			.aieo-mchat-body li { margin: 2px 0; }
			.aieo-mchat-body code { background: rgba(0,0,0,.06); padding: 1px 5px; border-radius: 3px; font-size: 90%; }
			.aieo-mchat-body pre { background: #0f172a; color: #f1f5f9; padding: 10px 12px; border-radius: 6px; overflow-x: auto; font-size: 12px; }
			.aieo-mchat-body pre code { background: transparent; color: inherit; padding: 0; }
			.aieo-mchat-table { width: 100%; border-collapse: collapse; margin: 8px 0; font-size: 13px; }
			.aieo-mchat-table th, .aieo-mchat-table td { border: 1px solid #dcdcde; padding: 6px 9px; text-align: left; }
			.aieo-mchat-table th { background: #f0f0f1; }

			.aieo-mchat-form { border-top: 1px solid #dcdcde; padding: 14px 18px; }
			.aieo-mchat-input { width: 100%; resize: vertical; font-size: 14px; padding: 10px; border: 1px solid #c3c4c7; border-radius: 6px; }
			.aieo-mchat-input:focus { border-color: #7b3fa5; box-shadow: 0 0 0 1px #7b3fa5; outline: 0; }
			.aieo-mchat-toolbar { display: flex; align-items: center; gap: 10px; margin-top: 10px; flex-wrap: wrap; }
			.aieo-mchat-footer-note { margin-left: auto; color: #646970; font-size: 12px; }

			.aieo-mchat-thinking { opacity: .8; font-style: italic; }
			.aieo-mchat-error { color: #b32d2e; }
		';
	}

	
	private static function inline_js() {
		return <<<'JS'
(function ($) {
	'use strict';
	$(function () {
		var $form = $('#aieo-mchat-form');
		var $input = $('#aieo-mchat-input');
		var $transcript = $('#aieo-mchat-transcript');
		var $send = $('.aieo-mchat-send');
		var $clear = $('#aieo-mchat-clear');

		function escapeHtml(s) {
			return String(s).replace(/[&<>"']/g, function (c) {
				return ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' })[c];
			});
		}

		function appendUserBubble(text) {
			$transcript.find('.aieo-mchat-empty').remove();
			var html =
				'<div class="aieo-mchat-bubble aieo-mchat-bubble--user">' +
					'<div class="aieo-mchat-avatar" aria-hidden="true">👤</div>' +
					'<div class="aieo-mchat-body">' + escapeHtml(text) + '</div>' +
				'</div>';
			$transcript.append(html);
			scrollBottom();
		}

		function appendBotBubble(html, extraClass) {
			$transcript.find('.aieo-mchat-empty').remove();
			var cls = 'aieo-mchat-bubble aieo-mchat-bubble--bot' + (extraClass ? ' ' + extraClass : '');
			var node = $(
				'<div class="' + cls + '">' +
					'<div class="aieo-mchat-avatar" aria-hidden="true">🛍</div>' +
					'<div class="aieo-mchat-body"></div>' +
				'</div>'
			);
			node.find('.aieo-mchat-body').html(html);
			$transcript.append(node);
			scrollBottom();
			return node;
		}

		function scrollBottom() {
			$transcript.scrollTop($transcript[0].scrollHeight);
		}

		$form.on('submit', function (e) {
			e.preventDefault();
			var msg = $.trim($input.val());
			if (!msg) return;

			appendUserBubble(msg);
			$input.val('');
			$send.prop('disabled', true);

			var $thinking = appendBotBubble(
				'<span class="aieo-mchat-thinking">' + escapeHtml(AIEOMerchantChat.thinking) + '</span>',
				'aieo-mchat-pending'
			);

			$.post(AIEOMerchantChat.ajaxUrl, {
				action: 'aieo_merchant_chat_send',
				nonce: AIEOMerchantChat.nonce,
				message: msg
			})
				.done(function (resp) {
					$thinking.remove();
					if (resp && resp.success && resp.data && resp.data.html) {
						appendBotBubble(resp.data.html);
					} else {
						var err = (resp && resp.data && resp.data.message) || AIEOMerchantChat.unknownError;
						appendBotBubble('<span class="aieo-mchat-error">' + escapeHtml(AIEOMerchantChat.error + ' ' + err) + '</span>');
					}
				})
				.fail(function (xhr) {
					$thinking.remove();
					var err = (xhr.responseJSON && xhr.responseJSON.data && xhr.responseJSON.data.message) || xhr.statusText || AIEOMerchantChat.networkError;
					appendBotBubble('<span class="aieo-mchat-error">' + escapeHtml(AIEOMerchantChat.error + ' ' + err) + '</span>');
				})
				.always(function () {
					$send.prop('disabled', false);
					$input.focus();
				});
		});

		$clear.on('click', function () {
			if (!window.confirm(AIEOMerchantChat.clearConf)) return;
			$.post(AIEOMerchantChat.ajaxUrl, {
				action: 'aieo_merchant_chat_clear',
				nonce: AIEOMerchantChat.nonce
			}).always(function () {
				$transcript.html(
					'<div class="aieo-mchat-empty"><p>' +
					escapeHtml(AIEOMerchantChat.emptyPrompt) +
					'</p></div>'
				);
			});
		});

		$('.aieo-mchat-chip').on('click', function () {
			$input.val($(this).data('prompt')).focus();
		});
	});
})(jQuery);
JS;
	}
}
