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

trait Niwa_AI_Chat_Context {

    private function build_workspace_context_block( $mode = 'main' ) {
        if ( ! $this->workspace || ! method_exists( $this->workspace, 'build_prompt_context' ) ) {
            return '';
        }

        $limit = 'subagent' === $mode ? 2600 : 6200;

        return trim( (string) $this->workspace->build_prompt_context( $mode, $limit ) );
    }

    private function build_skills_context_block( $user_message = '', $mode = 'main' ) {
        if ( ! $this->skills_manager || ! method_exists( $this->skills_manager, 'build_skills_context' ) ) {
            return '';
        }

        return trim( (string) $this->skills_manager->build_skills_context( $user_message, $mode ) );
    }

    private function format_history( $history ) {
        if ( empty( $history ) || ! is_array( $history ) ) {
            return 'No prior founder chat history.';
        }

        $lines = array();
        $windowed = method_exists( $this, 'limit_runtime_history_window' )
            ? $this->limit_runtime_history_window( $history, 'main' )
            : array_slice( (array) $history, -160 );

        foreach ( $windowed as $item ) {
            if ( ! is_array( $item ) ) {
                continue;
            }

            $role = isset( $item['role'] ) ? strtolower( (string) $item['role'] ) : 'system';
            $text = isset( $item['text'] ) ? trim( (string) $item['text'] ) : '';
            if ( '' === $text ) {
                continue;
            }

            if ( 'bot' === $role ) {
                $role = 'assistant';
            } elseif ( 'user' !== $role ) {
                $role = 'system';
            }

            $lines[] = strtoupper( $role ) . ': ' . $text;
        }

        return empty( $lines ) ? 'No prior founder chat history.' : implode( "\n", $lines );
    }

    private function get_site_local_time_context() {
        try {
            $timezone = wp_timezone();
            $now      = new DateTimeImmutable( 'now', $timezone );
            return $now->format( 'Y-m-d H:i:s T' );
        } catch ( Throwable $e ) {
            return '';
        }
    }

    private function maybe_refresh_memory_summary( $session_id, $history ) {
        unset( $history );

        return $this->build_session_memory_summary( $session_id );
    }

    private function get_founder_notes_payload( $session_id ) {
        $workspace_payload = $this->get_workspace_founder_notes_payload();
        $session_summary   = $this->build_session_memory_summary( $session_id );
        $summary_parts     = array();

        if ( ! empty( $workspace_payload['summary'] ) ) {
            $summary_parts[] = (string) $workspace_payload['summary'];
        }

        if ( '' !== $session_summary ) {
            $summary_parts[] = "Current session continuity:\n" . $session_summary;
        }

        $sources = isset( $workspace_payload['sources'] ) && is_array( $workspace_payload['sources'] ) ? $workspace_payload['sources'] : array();
        if ( '' !== $session_summary ) {
            $sources[] = 'session_manifest';
        }

        return array(
            'seed_notes'        => $this->get_seed_notes(),
            'summary'           => $this->truncate_block( implode( "\n\n", array_filter( $summary_parts ) ), 1800 ),
            'workspace_summary' => isset( $workspace_payload['summary'] ) ? (string) $workspace_payload['summary'] : '',
            'session_summary'   => $session_summary,
            'sources'           => array_values( array_unique( array_filter( array_map( 'strval', $sources ) ) ) ),
        );
    }

    private function get_workspace_founder_notes_payload() {
        $sections = array();
        $sources  = array();
        if ( $this->workspace && method_exists( $this->workspace, 'get_companion_prompt_summary' ) ) {
            $excerpt = trim( (string) $this->workspace->get_companion_prompt_summary( 800 ) );
            if ( '' !== $excerpt ) {
                $sections[] = "Founder and relationship:\n" . $excerpt;
                $sources[]  = 'memory/companion';
            }
        }

        if ( $this->workspace && method_exists( $this->workspace, 'get_operational_prompt_summary' ) ) {
            $excerpt = trim( (string) $this->workspace->get_operational_prompt_summary( 900 ) );
            if ( '' !== $excerpt ) {
                $sections[] = "Operational memory:\n" . $excerpt;
                $sources[]  = 'memory/operational';
            }
        }

        return array(
            'summary' => $this->truncate_block( implode( "\n\n", $sections ), 1400 ),
            'sources' => $sources,
        );
    }

    private function is_workspace_note_placeholder( $filename, $content ) {
        $filename   = strtoupper( trim( (string) $filename ) );
        $normalized = strtolower( trim( preg_replace( '/\s+/', ' ', (string) $content ) ) );
        $defaults   = array(
            'USER.MD'   => array(
                'founder preferences and operating notes can live here.',
            ),
            'MEMORY.MD' => array(
                'long-lived project memory, durable discoveries, and site-specific instructions.',
            ),
        );

        if ( empty( $defaults[ $filename ] ) ) {
            return false;
        }

        return in_array( $normalized, $defaults[ $filename ], true );
    }

    private function build_session_memory_summary( $session_id ) {
        $session_id = trim( (string) $session_id );
        if ( '' === $session_id || ! $this->session_state || ! method_exists( $this->session_state, 'get_session_snapshot' ) ) {
            return '';
        }

        $snapshot = (array) $this->session_state->get_session_snapshot( $session_id );
        if ( empty( $snapshot ) ) {
            return '';
        }

        $parts   = array();
        $worklog = trim( (string) ( isset( $snapshot['worklog_summary'] ) ? $snapshot['worklog_summary'] : '' ) );
        if ( '' !== $worklog ) {
            $parts[] = 'Worklog: ' . $worklog;
        }

        $note_lines = array();
        $notes      = isset( $snapshot['sticky_notes'] ) && is_array( $snapshot['sticky_notes'] ) ? array_slice( $snapshot['sticky_notes'], -4 ) : array();
        foreach ( $notes as $note ) {
            if ( ! is_array( $note ) || empty( $note['text'] ) ) {
                continue;
            }

            $note_lines[] = '- ' . $this->truncate_block( (string) $note['text'], 180 );
        }

        if ( ! empty( $note_lines ) ) {
            $parts[] = "Sticky notes:\n" . implode( "\n", $note_lines );
        }

        return $this->truncate_block( implode( "\n\n", $parts ), 900 );
    }

    private function build_memory_detail_text( $row ) {
        if ( empty( $row ) || ! is_array( $row ) ) {
            return '';
        }

        $parts     = array();
        $post_type = isset( $row['post_type'] ) ? (string) $row['post_type'] : '';
        $is_product = ( 'product' === $post_type );

        if ( ! empty( $row['title'] ) ) {
            $parts[] = 'Title: ' . (string) $row['title'];
        }
        if ( '' !== $post_type ) {
            $parts[] = 'Type: ' . $post_type;
        }
        if ( isset( $row['price'] ) && '' !== (string) $row['price'] ) {
            $parts[] = 'Price: ' . (string) $row['price'];
        }
        if ( isset( $row['sale_price'] ) && '' !== (string) $row['sale_price'] ) {
            $parts[] = 'Sale price: ' . (string) $row['sale_price'];
        }
        if ( $is_product && ! empty( $row['brand'] ) ) {
            $parts[] = 'Brand: ' . (string) $row['brand'];
        }
        if ( ! empty( $row['stock_status'] ) ) {
            $parts[] = 'Stock: ' . (string) $row['stock_status'];
        }
        if ( ! empty( $row['categories_text'] ) ) {
            $parts[] = 'Categories: ' . $this->truncate_block( (string) $row['categories_text'], 250 );
        }
        if ( ! empty( $row['attributes_text'] ) ) {
            $parts[] = 'Attributes: ' . $this->truncate_block( (string) $row['attributes_text'], 350 );
        }
        if ( ! empty( $row['short_desc'] ) ) {
            $parts[] = 'Short description: ' . $this->truncate_block( (string) $row['short_desc'], 500 );
        }
        if ( ! empty( $row['content_text'] ) ) {
            $parts[] = 'Content: ' . $this->truncate_block( (string) $row['content_text'], 1800 );
        }
        if ( ! empty( $row['url'] ) ) {
            $parts[] = 'URL: ' . (string) $row['url'];
        }

        return implode( "\n", $parts );
    }

    private function build_woo_product_lookup_payload( $query, $include_trash, $limit, $context = array(), $lookup_options = array() ) {
        if ( ! $this->admin_tools || ! method_exists( $this->admin_tools, 'lookup_woo_products' ) ) {
            return array(
                'available' => false,
                'error'     => 'Woo product lookup nije ucitan.',
            );
        }

        if ( method_exists( $this->admin_tools, 'set_runtime_context' ) ) {
            $this->admin_tools->set_runtime_context(
                method_exists( $this, 'build_admin_tool_runtime_context' )
                    ? $this->build_admin_tool_runtime_context( $context )
                    : array()
            );
        }

        $statuses = $include_trash
            ? array( 'publish', 'private', 'draft', 'pending', 'future', 'trash' )
            : array( 'publish', 'private', 'draft', 'pending', 'future' );
        $products = $this->admin_tools->lookup_woo_products( $query, $statuses, $limit, $lookup_options );

        if ( method_exists( $this->admin_tools, 'clear_runtime_context' ) ) {
            $this->admin_tools->clear_runtime_context();
        }

        $page   = isset( $lookup_options['page'] ) ? max( 1, intval( $lookup_options['page'] ) ) : 1;
        $offset = array_key_exists( 'offset', $lookup_options ) ? max( 0, intval( $lookup_options['offset'] ) ) : max( 0, ( $page - 1 ) * max( 1, intval( $limit ) ) );

        return array(
            'available'  => true,
            'scope'      => isset( $lookup_options['scope'] ) && 'all' === (string) $lookup_options['scope'] ? 'all' : 'match',
            'query'      => (string) $query,
            'limit'      => max( 1, intval( $limit ) ),
            'page'       => $page,
            'offset'     => $offset,
            'returned'   => count( (array) $products ),
            'maybe_more' => count( (array) $products ) >= max( 1, intval( $limit ) ),
            'filters'    => array(
                'onsale_only'           => ! empty( $lookup_options['onsale_only'] ),
                'exclude_category_id'   => isset( $lookup_options['exclude_category_id'] ) ? intval( $lookup_options['exclude_category_id'] ) : 0,
                'exclude_category_slug' => isset( $lookup_options['exclude_category_slug'] ) ? sanitize_title( (string) $lookup_options['exclude_category_slug'] ) : '',
            ),
            'products'   => array_values( (array) $products ),
        );
    }

    private function build_woo_variation_lookup_payload( $product_query, $variation_attributes, $include_trash, $limit, $context = array() ) {
        if ( ! $this->admin_tools || ! method_exists( $this->admin_tools, 'lookup_woo_variations' ) ) {
            return array(
                'available' => false,
                'error'     => 'Woo variation lookup nije ucitan.',
            );
        }

        if ( method_exists( $this->admin_tools, 'set_runtime_context' ) ) {
            $this->admin_tools->set_runtime_context(
                method_exists( $this, 'build_admin_tool_runtime_context' )
                    ? $this->build_admin_tool_runtime_context( $context )
                    : array()
            );
        }

        $statuses   = $include_trash
            ? array( 'publish', 'private', 'draft', 'pending', 'future', 'trash' )
            : array( 'publish', 'private', 'draft', 'pending', 'future' );
        $variations = $this->admin_tools->lookup_woo_variations( $product_query, (array) $variation_attributes, $statuses, $limit );

        if ( method_exists( $this->admin_tools, 'clear_runtime_context' ) ) {
            $this->admin_tools->clear_runtime_context();
        }

        return array(
            'available'            => true,
            'product_query'        => (string) $product_query,
            'variation_attributes' => (array) $variation_attributes,
            'variations'           => array_values( (array) $variations ),
        );
    }

    private function build_turn_input_content( $prompt, $context = array() ) {
        $prompt              = trim( (string) $prompt );
        $context             = is_array( $context ) ? $context : array();
        $image_urls          = $this->extract_context_image_urls( $context );
        $preview_urls        = $this->extract_incoming_media_preview_urls( $context );
        $attachment_note     = $this->build_current_attachment_prompt_note( $context );
        $recent_note         = $this->build_recent_attachment_prompt_note( $context );
        $incoming_media_note = $this->build_incoming_media_prompt_note( $context );

        if ( empty( $image_urls ) && empty( $preview_urls ) ) {
            // Append image error to prompt so the AI can inform the founder.
            if ( ! empty( $context['image_error'] ) ) {
                return $prompt . "\n\n[IMAGE ERROR: " . (string) $context['image_error'] . ']';
            }
            $extras = trim( trim( $attachment_note ) . "\n\n" . trim( $recent_note ) . "\n\n" . trim( $incoming_media_note ) );
            if ( '' !== $extras ) {
                return trim( $prompt . "\n\n" . $extras );
            }
            return $prompt;
        }

        if ( '' !== $prompt ) {
            $text_prompt = $prompt;
        } elseif ( ! empty( $image_urls ) ) {
            $text_prompt = 'Analyze the attached image and answer the founder naturally.';
        } else {
            $text_prompt = 'Analyze the attached video / GIF / sticker preview frame(s) and answer the founder naturally.';
        }
        if ( '' !== $attachment_note ) {
            $text_prompt = trim( $text_prompt . "\n\n" . $attachment_note );
        }
        if ( '' !== $recent_note ) {
            $text_prompt = trim( $text_prompt . "\n\n" . $recent_note );
        }
        if ( '' !== $incoming_media_note ) {
            $text_prompt = trim( $text_prompt . "\n\n" . $incoming_media_note );
        }

        $parts = array();
        $parts[] = array(
            'type' => 'text',
            'text' => $text_prompt,
        );

        foreach ( $image_urls as $image_url ) {
            $parts[] = array(
                'type'      => 'image_url',
                'image_url' => array(
                    'url'    => (string) $image_url,
                    'detail' => 'auto',
                ),
            );
        }

        // Preview frames extracted from videos / GIFs / video stickers, plus
        // sideloaded static-sticker images. They live alongside the regular
        // attachment_image_urls in the multimodal payload so the vision model
        // sees them, but they are NOT pushed into the
        // [CURRENT TELEGRAM IMAGE ATTACHMENTS] block — that block drives
        // manage_product_gallery and we don't want a video frame to land as
        // a product gallery image. The [CURRENT TELEGRAM INCOMING MEDIA]
        // block tells the agent which previews these are and what kind of
        // medium each one came from.
        foreach ( $preview_urls as $preview_url ) {
            $parts[] = array(
                'type'      => 'image_url',
                'image_url' => array(
                    'url'    => (string) $preview_url,
                    'detail' => 'auto',
                ),
            );
        }

        return $parts;
    }

    private function extract_incoming_media_preview_urls( $context ) {
        $context = is_array( $context ) ? $context : array();
        $entries = isset( $context['incoming_media'] ) && is_array( $context['incoming_media'] )
            ? $context['incoming_media']
            : array();

        $urls = array();
        foreach ( $entries as $entry ) {
            if ( ! is_array( $entry ) ) {
                continue;
            }
            if ( ! empty( $entry['preview_image_url'] ) ) {
                $urls[] = esc_url_raw( (string) $entry['preview_image_url'] );
            }
            if ( ! empty( $entry['extra_frame_urls'] ) && is_array( $entry['extra_frame_urls'] ) ) {
                foreach ( $entry['extra_frame_urls'] as $frame_url ) {
                    $urls[] = esc_url_raw( (string) $frame_url );
                }
            }
        }

        return array_values( array_unique( array_filter( array_map( 'trim', array_map( 'strval', $urls ) ) ) ) );
    }

    private function build_current_attachment_prompt_note( $context ) {
        $context = is_array( $context ) ? $context : array();

        $ids = isset( $context['attachment_image_attachment_ids'] ) && is_array( $context['attachment_image_attachment_ids'] )
            ? array_values( array_filter( array_map( 'intval', $context['attachment_image_attachment_ids'] ) ) )
            : array();
        if ( empty( $ids ) && ! empty( $context['attachment_image_attachment_id'] ) ) {
            $ids[] = intval( $context['attachment_image_attachment_id'] );
        }
        $ids = array_values( array_unique( array_filter( $ids ) ) );

        $urls = isset( $context['attachment_image_urls'] ) && is_array( $context['attachment_image_urls'] )
            ? array_values( array_filter( array_map( 'esc_url_raw', array_map( 'strval', $context['attachment_image_urls'] ) ) ) )
            : array();
        if ( empty( $urls ) && ! empty( $context['attachment_image_url'] ) ) {
            $urls[] = esc_url_raw( (string) $context['attachment_image_url'] );
        }
        $urls = array_values( array_unique( array_filter( $urls ) ) );

        if ( empty( $ids ) && empty( $urls ) ) {
            return '';
        }

        $lines = array( '[CURRENT TELEGRAM IMAGE ATTACHMENTS]' );
        $count = max( count( $ids ), count( $urls ) );
        $lines[] = 'count=' . $count;
        if ( ! empty( $context['is_album'] ) ) {
            $lines[] = 'album_size=' . intval( $context['album_size'] ?? $count );
        }
        if ( ! empty( $ids ) ) {
            $lines[] = 'attachment_ids=[' . implode( ',', array_map( 'intval', $ids ) ) . ']';
        }
        if ( ! empty( $urls ) ) {
            $lines[] = 'image_urls=[' . implode( ',', array_map( 'strval', $urls ) ) . ']';
        }
        $lines[] = 'For product galleries, call manage_product_gallery with attachment_ids when available; preserve this order.';
        $lines[] = '[/CURRENT TELEGRAM IMAGE ATTACHMENTS]';

        return implode( "\n", $lines );
    }

    /**
     * Render `[CURRENT TELEGRAM INCOMING MEDIA]` for any video / GIF / video
     * note / sticker that arrived this turn. The agent can't view frames for
     * these but should still acknowledge the medium specifically and use the
     * metadata (duration, dimensions, mime, filename, sticker emoji/set) when
     * answering. Empty when the turn has no non-image media.
     */
    private function build_incoming_media_prompt_note( $context ) {
        $context = is_array( $context ) ? $context : array();
        $entries = isset( $context['incoming_media'] ) && is_array( $context['incoming_media'] )
            ? array_values( array_filter( $context['incoming_media'], 'is_array' ) )
            : array();
        if ( empty( $entries ) ) {
            return '';
        }

        $lines = array( '[CURRENT TELEGRAM INCOMING MEDIA]' );
        $lines[] = 'count=' . count( $entries );
        $lines[] = 'INTERNAL CONTEXT — use silently, do not read back to the founder. Preview frames listed below are already attached to this turn as image_url inputs. Respond naturally to the content; do not narrate the medium, do not recite metadata, do not explain stickers / GIFs. Previews are NOT product images: never pass preview_image_url / preview_attachment_id to manage_product_gallery / set_product_featured_image_from_url.';

        foreach ( $entries as $idx => $entry ) {
            $entry = is_array( $entry ) ? $entry : array();
            $kind  = isset( $entry['kind'] ) ? (string) $entry['kind'] : 'unknown';
            $bits  = array( 'kind=' . $kind );

            $file_id = isset( $entry['file_id'] ) ? trim( (string) $entry['file_id'] ) : '';
            if ( '' !== $file_id && in_array( $kind, array( 'sticker', 'animation' ), true ) ) {
                $bits[] = 'file_id=' . $file_id;
            }

            $duration = isset( $entry['duration_seconds'] ) ? intval( $entry['duration_seconds'] ) : 0;
            if ( $duration > 0 ) {
                $bits[] = 'duration_seconds=' . $duration;
            }
            $width  = isset( $entry['width'] ) ? intval( $entry['width'] ) : 0;
            $height = isset( $entry['height'] ) ? intval( $entry['height'] ) : 0;
            if ( $width > 0 && $height > 0 ) {
                $bits[] = 'dimensions=' . $width . 'x' . $height;
            }
            $mime = isset( $entry['mime_type'] ) ? trim( (string) $entry['mime_type'] ) : '';
            if ( '' !== $mime ) {
                $bits[] = 'mime_type=' . $mime;
            }
            $size = isset( $entry['file_size_bytes'] ) ? intval( $entry['file_size_bytes'] ) : 0;
            if ( $size > 0 ) {
                $bits[] = 'file_size_bytes=' . $size;
            }
            $file_name = isset( $entry['file_name'] ) ? trim( (string) $entry['file_name'] ) : '';
            if ( '' !== $file_name ) {
                $bits[] = 'file_name=' . $file_name;
            }

            if ( 'sticker' === $kind ) {
                $emoji = isset( $entry['sticker_emoji'] ) ? trim( (string) $entry['sticker_emoji'] ) : '';
                $set   = isset( $entry['sticker_set'] ) ? trim( (string) $entry['sticker_set'] ) : '';
                if ( '' !== $emoji ) {
                    $bits[] = 'sticker_emoji=' . $emoji;
                }
                if ( '' !== $set ) {
                    $bits[] = 'sticker_set=' . $set;
                }
                if ( ! empty( $entry['is_animated_sticker'] ) ) {
                    $bits[] = 'is_animated_sticker=true';
                }
                if ( ! empty( $entry['is_video_sticker'] ) ) {
                    $bits[] = 'is_video_sticker=true';
                }
            }

            if ( ! empty( $entry['preview_image_url'] ) ) {
                $bits[] = 'preview_image_url=' . (string) $entry['preview_image_url'];
            }
            if ( ! empty( $entry['preview_attachment_id'] ) ) {
                $bits[] = 'preview_attachment_id=' . intval( $entry['preview_attachment_id'] );
            }
            if ( ! empty( $entry['preview_source'] ) ) {
                $bits[] = 'preview_source=' . (string) $entry['preview_source'];
            }
            if ( ! empty( $entry['extra_frame_urls'] ) && is_array( $entry['extra_frame_urls'] ) ) {
                $bits[] = 'extra_frames_attached=' . count( $entry['extra_frame_urls'] );
            }
            if ( ! empty( $entry['preview_status'] ) ) {
                $bits[] = 'preview_status=' . (string) $entry['preview_status'];
            }
            if ( ! empty( $entry['preview_error'] ) ) {
                $bits[] = 'preview_error=' . (string) $entry['preview_error'];
            }
            if ( ! empty( $entry['visual_description'] ) ) {
                $bits[] = 'visual_description=' . str_replace( array( "\r\n", "\n", "\r" ), ' ', (string) $entry['visual_description'] );
            }

            $lines[] = '#' . ( intval( $idx ) + 1 ) . ' ' . implode( ', ', $bits );

            // Audio transcript (Whisper) on its own indented line so a long
            // dialog does not wreck the readability of the metadata bullet.
            if ( ! empty( $entry['audio_transcript'] ) ) {
                $lines[] = '    audio_transcript: ' . str_replace( array( "\r\n", "\n", "\r" ), ' ', (string) $entry['audio_transcript'] );
            }
        }

        $lines[] = '[/CURRENT TELEGRAM INCOMING MEDIA]';

        return implode( "\n", $lines );
    }

    /**
     * Surface every image the founder has sent in the last 30 minutes across
     * separate turns. The session_state media-context records persist
     * attachment IDs even after the originating turn finishes, so the agent
     * can resolve "ubaci ove dve slike u galeriju" in a follow-up turn that
     * carries no fresh attachment.
     *
     * Skipped when the current turn already has its own attachments — the
     * `[CURRENT TELEGRAM IMAGE ATTACHMENTS]` block covers that and we don't
     * want to confuse the model with two parallel lists.
     */
    private function build_recent_attachment_prompt_note( $context ) {
        $context = is_array( $context ) ? $context : array();

        // If the current turn already has attachments, the [CURRENT…] note
        // already lists them — don't duplicate.
        if ( ! empty( $context['attachment_image_attachment_id'] )
            || ! empty( $context['attachment_image_attachment_ids'] )
            || ! empty( $context['attachment_image_url'] )
            || ! empty( $context['attachment_image_urls'] )
        ) {
            return '';
        }

        $session_id = isset( $context['session_id'] ) ? (string) $context['session_id'] : '';
        if ( '' === $session_id || ! $this->session_state || ! method_exists( $this->session_state, 'get_recent_media_attachments' ) ) {
            return '';
        }

        $bag = (array) $this->session_state->get_recent_media_attachments( $session_id, 1800, 12 );
        $ids  = isset( $bag['attachment_ids'] ) && is_array( $bag['attachment_ids'] ) ? array_values( array_filter( array_map( 'intval', $bag['attachment_ids'] ) ) ) : array();
        $urls = isset( $bag['image_urls'] ) && is_array( $bag['image_urls'] ) ? array_values( array_filter( array_map( 'strval', $bag['image_urls'] ) ) ) : array();

        if ( empty( $ids ) && empty( $urls ) ) {
            return '';
        }

        $lines = array( '[RECENT TELEGRAM IMAGE ATTACHMENTS — last 30 min, across previous turns]' );
        $count = max( count( $ids ), count( $urls ) );
        $lines[] = 'count=' . $count;
        if ( ! empty( $ids ) ) {
            $lines[] = 'attachment_ids=[' . implode( ',', array_map( 'intval', $ids ) ) . ']  (newest first)';
        }
        if ( ! empty( $urls ) ) {
            $lines[] = 'image_urls=[' . implode( ',', array_map( 'strval', $urls ) ) . ']';
        }
        $lines[] = 'When the founder asks to use "te slike", "ove slike", "obe slike" without a fresh attachment, these IDs are what they mean.';
        $lines[] = 'For galleries: pass them as attachment_ids[] to manage_product_gallery (the runtime auto-injects if you omit them).';
        $lines[] = '[/RECENT TELEGRAM IMAGE ATTACHMENTS]';

        return implode( "\n", $lines );
    }

    private function get_seed_notes() {
        $settings = get_option( Niwa_AI_Constants::OPT_AUTONOMY, array() );
        if ( ! is_array( $settings ) ) {
            $settings = array();
        }

        return $this->truncate_block(
            trim( (string) ( isset( $settings['founder_notes_seed'] ) ? $settings['founder_notes_seed'] : '' ) ),
            1200
        );
    }

    private function build_site_context_blob() {
		$profile_context = method_exists( $this, 'build_site_operating_profile_context' )
			? trim( (string) $this->build_site_operating_profile_context() )
			: '';

        if ( $this->site_discovery && method_exists( $this->site_discovery, 'get_context_blob' ) ) {
			return trim( (string) $this->site_discovery->get_context_blob( 2200 ) . "\n" . $profile_context );
        }

        $cached = get_transient( 'niwa_ai_admin_autonomy_site_context_blob' );
        if ( is_string( $cached ) && '' !== $cached ) {
			return trim( $cached . "\n" . $profile_context );
        }

        $parts = array();
        $host  = wp_parse_url( home_url( '/' ), PHP_URL_HOST );
        if ( is_string( $host ) && '' !== $host ) {
            $parts[] = 'Host: ' . $host;
        }

        $site_name = trim( (string) get_bloginfo( 'name' ) );
        if ( '' !== $site_name ) {
            $parts[] = 'Site name: ' . $site_name;
        }

        if ( $this->content_store && method_exists( $this->content_store, 'get_store_overview' ) ) {
            $overview = $this->content_store->get_store_overview();
            if ( is_array( $overview ) ) {
                if ( isset( $overview['total_products'] ) && intval( $overview['total_products'] ) > 0 ) {
                    $parts[] = 'Total products: ' . intval( $overview['total_products'] );
                }

                if ( ! empty( $overview['categories'] ) && is_array( $overview['categories'] ) ) {
                    $categories = array();
                    foreach ( array_slice( $overview['categories'], 0, 5 ) as $category ) {
                        if ( empty( $category['name'] ) ) {
                            continue;
                        }
                        $categories[] = (string) $category['name'];
                    }
                    if ( ! empty( $categories ) ) {
                        $parts[] = 'Top categories: ' . implode( ', ', $categories );
                    }
                }
            }
        }

        if ( $this->content_store ) {
            $agent_profile = trim( (string) $this->content_store->get_agent_profile_summary( 450 ) );
            if ( '' !== $agent_profile ) {
                $parts[] = 'About site: ' . $agent_profile;
            }

            $home_page = trim( (string) $this->content_store->get_home_page_summary( 700 ) );
            if ( '' !== $home_page ) {
                $parts[] = 'Home page summary: ' . $home_page;
            }

            $navigation = trim( (string) $this->content_store->get_navigation_summary() );
            if ( '' !== $navigation ) {
                $parts[] = 'Navigation: ' . $this->truncate_block( $navigation, 450 );
            }
        }

        $blob = $this->truncate_block( implode( "\n", array_filter( $parts ) ), 2200 );
        if ( '' !== $blob ) {
            set_transient( 'niwa_ai_admin_autonomy_site_context_blob', $blob, 10 * MINUTE_IN_SECONDS );
        }

		return trim( $blob . "\n" . $profile_context );
    }

    public function build_site_context_blob_public() {
        $parts = array();
        $site_name = trim( (string) get_bloginfo( 'name' ) );
        if ( '' !== $site_name ) {
            $parts[] = 'Site name: ' . $site_name;
        }

        if ( $this->content_store && method_exists( $this->content_store, 'get_store_overview' ) ) {
            $overview = $this->content_store->get_store_overview();
            if ( is_array( $overview ) ) {
                if ( isset( $overview['total_products'] ) && intval( $overview['total_products'] ) > 0 ) {
                    $parts[] = 'Total products: ' . intval( $overview['total_products'] );
                }

                if ( ! empty( $overview['categories'] ) && is_array( $overview['categories'] ) ) {
                    $categories = array();
                    foreach ( array_slice( $overview['categories'], 0, 5 ) as $category ) {
                        if ( empty( $category['name'] ) ) {
                            continue;
                        }
                        $categories[] = (string) $category['name'];
                    }
                    if ( ! empty( $categories ) ) {
                        $parts[] = 'Top categories: ' . implode( ', ', $categories );
                    }
                }
            }
        }

        return $this->truncate_block( implode( "\n", array_filter( $parts ) ), 800 );
    }

}
