<?php
namespace EasyChat\Model\Cms;

use EasyChat\Library\Catalog\CatalogSearchUnavailableException;

class CmsModelWordPress extends CmsModelBase {

     
    private $currency_cache = null;

    


    public function getProducts(): array {
        $p      = $this->prefix;
        $result = $this->db->query(
            "SELECT p.ID AS id, p.post_title AS name
             FROM `{$p}posts` p
             WHERE p.post_type = 'product' AND p.post_status = 'publish'
               AND " . $this->productSearchVisibilitySql('p') . "
             ORDER BY p.ID ASC"
        );
        return $result->rows ?? [];
    }

    public function getProductsBatch(int $after_id, int $limit): array {
        $after_id = max(0, $after_id);
        $limit = max(1, min(100, $limit));
        $p = $this->prefix;
        $result = $this->db->query(
            "SELECT p.ID AS id, p.post_title AS name FROM `{$p}posts` p
             WHERE p.post_type = 'product' AND p.post_status = 'publish' AND p.ID > {$after_id}
               AND " . $this->productSearchVisibilitySql('p') . "
             ORDER BY p.ID ASC LIMIT {$limit}"
        );
        return $result->rows ?? array();
    }

    


    public function getProductText(int $product_id): string {
        $parts = [];
        $p     = $this->prefix;

        $result  = $this->db->query(
            "SELECT p.post_title AS name, p.post_content AS description,
                    p.post_excerpt AS summary,
                    COALESCE(sku.meta_value, '') AS sku
             FROM `{$p}posts` p
             LEFT JOIN `{$p}postmeta` sku
                 ON sku.post_id = p.ID AND sku.meta_key = '_sku'
             WHERE p.ID = $product_id
               AND p.post_type = 'product' AND p.post_status = 'publish'
               AND " . $this->productSearchVisibilitySql('p') . "
             LIMIT 1"
        );
        $product = $result->row ?? [];

        if (empty($product)) {
            return '';
        }

        if ($this->settings['chat_embedding_use_name'] ?? false) {
            if (!empty($product['name'])) $parts[] = $product['name'];
        }

        if ($this->settings['chat_embedding_use_desc'] ?? false) {
            $text = !empty($product['description']) ? $product['description'] : ($product['summary'] ?? '');
            if (!empty($text)) $parts[] = strip_tags($text);
        }

         

        if ($this->settings['chat_embedding_use_sku'] ?? false) {
            if (!empty($product['sku'])) $parts[] = 'Артикул: ' . $product['sku'];
        }

        if ($this->settings['chat_embedding_use_attrs'] ?? false) {
            $attrs = $this->getAttributes($product_id, $p);
            if (!empty($attrs)) $parts[] = 'Характеристики: ' . implode(', ', $attrs);
        }

        if ($this->settings['chat_embedding_use_options'] ?? false) {
            $options = $this->getVariationOptions($product_id, $p);
            if (!empty($options)) $parts[] = 'Варианты: ' . implode('; ', $options);
        }

        return implode('. ', array_filter(array_map('trim', $parts)));
    }

    


    public function getPages(): array {
        $p      = $this->prefix;
        $result = $this->db->query(
            "SELECT ID AS id, post_title AS name
             FROM `{$p}posts`
             WHERE post_type = 'page' AND post_status = 'publish'
             ORDER BY ID ASC"
        );
        return $result->rows ?? [];
    }

    public function getPagesBatch(int $after_id, int $limit): array {
        $after_id = max(0, $after_id);
        $limit = max(1, min(100, $limit));
        $result = $this->db->query(
            "SELECT ID AS id, post_title AS name FROM `{$this->prefix}posts`
             WHERE post_type = 'page' AND post_status = 'publish' AND ID > {$after_id}
             ORDER BY ID ASC LIMIT {$limit}"
        );
        return $result->rows ?? array();
    }

    


    public function getPageText(int $page_id): string {
        $p      = $this->prefix;
        $result = $this->db->query(
            "SELECT post_title AS name, post_content AS description
             FROM `{$p}posts`
             WHERE ID = $page_id
             LIMIT 1"
        );
        $page = $result->row ?? [];

        $parts = [];
        if (!empty($page['name']))        $parts[] = $page['name'];
        if (!empty($page['description'])) $parts[] = strip_tags($page['description']);

        return implode('. ', array_filter(array_map('trim', $parts)));
    }

    



    public function searchProducts(string $query, float $max_price = 0.0, array $options = array()): array {
        $p     = $this->prefix;
        $words = $this->splitWords($query);
        $limit = max(1, min(200, (int)($options['candidate_limit'] ?? $this->getSearchLimit()) * 2));

        $likes     = [];
        $relevance = [];
        foreach ($words as $word) {
            $esc         = $this->escapeLikeWord($word);
            $likes[] = "(LOWER(p.post_title) LIKE '%{$esc}%'
                        OR LOWER(COALESCE(sku.meta_value, '')) LIKE '%{$esc}%'
                        OR LOWER(COALESCE(vsku.meta_value, '')) LIKE '%{$esc}%')";
            $relevance[] = "(CASE
                WHEN LOWER(COALESCE(sku.meta_value, '')) = '{$esc}' OR LOWER(COALESCE(vsku.meta_value, '')) = '{$esc}' THEN 12
                WHEN LOWER(p.post_title) LIKE '%{$esc}%' THEN 4
                WHEN LOWER(COALESCE(sku.meta_value, '')) LIKE '%{$esc}%' OR LOWER(COALESCE(vsku.meta_value, '')) LIKE '%{$esc}%' THEN 6
                ELSE 0 END)";
        }

        $rel_expr = implode(' + ', $relevance);
        $compact = mb_substr($this->normalizeSearchIdentifier($query), 0, 120, 'UTF-8');
        $compact_e = $this->db->escape($compact);
        $compact_parent_sku = $this->compactSearchIdentifierSql('sku.meta_value');
        $compact_variation_sku = $this->compactSearchIdentifierSql('vsku.meta_value');
        $compact_name = $this->compactSearchIdentifierSql('p.post_title');
        $identifier_cond = $compact_e !== ''
            ? "({$compact_parent_sku} = '{$compact_e}' OR {$compact_variation_sku} = '{$compact_e}' OR {$compact_name} LIKE '%{$compact_e}%')"
            : '0';

        $make_sql = function (string $op) use ($p, $likes, $rel_expr, $limit, $max_price, $identifier_cond): string {
            $cond = implode(" {$op} ", $likes);
            $price_cond = !$max_price
                ? '1'
                : "CAST(COALESCE(NULLIF(vprice.meta_value, ''), price.meta_value) AS DECIMAL(20,4)) <= " . (float)$max_price;
            return "SELECT DISTINCT p.ID AS product_id, p.post_title AS name,
                        price.meta_value AS price,
                        sku.meta_value AS sku,
                        stock_status.meta_value AS stock_status,
                        stock.meta_value AS stock_quantity,
                        backorders.meta_value AS backorders,
                        v.ID AS variation_id,
                        vsku.meta_value AS variation_sku,
                        vstock_status.meta_value AS variation_stock_status,
                        vstock.meta_value AS variation_stock_quantity,
                        vbackorders.meta_value AS variation_backorders,
                        vprice.meta_value AS variation_price,
                        ({$rel_expr}) AS relevance
                    FROM `{$p}posts` p
                    LEFT JOIN `{$p}postmeta` price ON price.post_id = p.ID AND price.meta_key = '_price'
                    LEFT JOIN `{$p}postmeta` sku ON sku.post_id = p.ID AND sku.meta_key = '_sku'
                    LEFT JOIN `{$p}postmeta` stock_status ON stock_status.post_id = p.ID AND stock_status.meta_key = '_stock_status'
                    LEFT JOIN `{$p}postmeta` stock ON stock.post_id = p.ID AND stock.meta_key = '_stock'
                    LEFT JOIN `{$p}postmeta` backorders ON backorders.post_id = p.ID AND backorders.meta_key = '_backorders'
                    LEFT JOIN `{$p}posts` v
                        ON v.post_parent = p.ID AND v.post_type = 'product_variation' AND v.post_status = 'publish'
                    LEFT JOIN `{$p}postmeta` vsku ON vsku.post_id = v.ID AND vsku.meta_key = '_sku'
                    LEFT JOIN `{$p}postmeta` vstock_status ON vstock_status.post_id = v.ID AND vstock_status.meta_key = '_stock_status'
                    LEFT JOIN `{$p}postmeta` vstock ON vstock.post_id = v.ID AND vstock.meta_key = '_stock'
                    LEFT JOIN `{$p}postmeta` vbackorders ON vbackorders.post_id = v.ID AND vbackorders.meta_key = '_backorders'
                    LEFT JOIN `{$p}postmeta` vprice ON vprice.post_id = v.ID AND vprice.meta_key = '_price'
                    WHERE p.post_type = 'product' AND p.post_status = 'publish'
                      AND (({$cond}) OR {$identifier_cond})
                      AND {$price_cond}
                      AND " . $this->productSearchVisibilitySql('p') . "
                    ORDER BY relevance DESC, p.ID DESC
                    LIMIT {$limit}";
        };

        $multi = count($words) > 1;
        $sql   = $make_sql($multi ? 'AND' : 'OR');
        $started_at = microtime(true);

        try {
            $result = $this->db->query($sql);

            if ($multi && empty($result->rows)) {
                $result = $this->db->query($make_sql('OR'));
            }

            $grouped = [];
            $base_url = $this->getBaseUrl();
            $currency = $this->getCurrencyCode();
            $query_identifier = $this->normalizeCatalogIdentifier($query);
            foreach ($result->rows as $row) {
                $product_id = (int)$row['product_id'];
                if (!isset($grouped[$product_id])) {
                    $parent_state = $this->wooAvailability(
                        (string)($row['stock_status'] ?? ''),
                        (string)($row['backorders'] ?? '')
                    );
                    $price_value = (float)($row['price'] ?? 0);
                    $grouped[$product_id] = [
                        'id' => (string)$product_id,
                        'name' => (string)$row['name'],
                        'model' => '',
                        'sku' => (string)($row['sku'] ?? ''),
                        'price' => $this->formatCatalogPrice($price_value, $currency),
                        'price_value' => $price_value,
                        'currency' => $currency,
                        'link' => $base_url . '/?p=' . $product_id,
                        'availability' => $parent_state,
                        'availability_label' => $this->wooAvailabilityLabel($parent_state),
                        'can_order' => in_array($parent_state, array('in_stock', 'preorder'), true),
                        'quantity' => is_numeric($row['stock_quantity'] ?? null) ? (float)$row['stock_quantity'] : null,
                        'relevance' => (int)($row['relevance'] ?? 0),
                        '_has_variation' => false,
                        '_exact_variation' => false,
                    ];
                }

                if (!empty($row['variation_id'])) {
                    $variation_state = $this->wooAvailability(
                        (string)($row['variation_stock_status'] ?? ''),
                        (string)($row['variation_backorders'] ?? '')
                    );
                    if ($variation_state === 'unknown') {
                        $variation_state = $grouped[$product_id]['availability'];
                    }
                    $variation_sku = trim((string)($row['variation_sku'] ?? ''));
                    $variation_identifier = $this->normalizeCatalogIdentifier($variation_sku);
                    $exact_variation = $variation_identifier !== ''
                        && mb_strlen($variation_identifier, 'UTF-8') >= 3
                        && strpos($query_identifier, $variation_identifier) !== false;
                    if ($exact_variation) {
                        $grouped[$product_id]['availability'] = $variation_state;
                        $grouped[$product_id]['availability_label'] = $this->wooAvailabilityLabel($variation_state);
                        $grouped[$product_id]['can_order'] = in_array($variation_state, array('in_stock', 'preorder'), true);
                        $grouped[$product_id]['quantity'] = is_numeric($row['variation_stock_quantity'] ?? null)
                            ? (float)$row['variation_stock_quantity']
                            : null;
                        $grouped[$product_id]['sku'] = $variation_sku;
                        $variation_price = is_numeric($row['variation_price'] ?? null)
                            ? (float)$row['variation_price']
                            : 0.0;
                        if ($variation_price > 0) {
                            $grouped[$product_id]['price_value'] = $variation_price;
                            $grouped[$product_id]['price'] = $this->formatCatalogPrice($variation_price, $currency);
                        }
                        $grouped[$product_id]['_has_variation'] = true;
                        $grouped[$product_id]['_exact_variation'] = true;
                        continue;
                    }
                    if ($grouped[$product_id]['_exact_variation']) {
                        continue;
                    }
                    if (!$grouped[$product_id]['_has_variation']) {
                        $grouped[$product_id]['availability'] = $variation_state;
                        $grouped[$product_id]['availability_label'] = $this->wooAvailabilityLabel($variation_state);
                        $grouped[$product_id]['can_order'] = in_array($variation_state, array('in_stock', 'preorder'), true);
                        $grouped[$product_id]['quantity'] = null;
                        $grouped[$product_id]['_has_variation'] = true;
                    } elseif ($this->availabilityPriority($variation_state) > $this->availabilityPriority($grouped[$product_id]['availability'])) {
                        $grouped[$product_id]['availability'] = $variation_state;
                        $grouped[$product_id]['availability_label'] = $this->wooAvailabilityLabel($variation_state);
                        $grouped[$product_id]['can_order'] = in_array($variation_state, array('in_stock', 'preorder'), true);
                    }
                    if ($variation_sku !== '' && strpos($query_identifier, $this->normalizeCatalogIdentifier($variation_sku)) !== false) {
                        $grouped[$product_id]['sku'] = $variation_sku;
                    }
                    if (is_numeric($row['variation_stock_quantity'] ?? null)) {
                        $current = is_numeric($grouped[$product_id]['quantity']) ? (float)$grouped[$product_id]['quantity'] : 0.0;
                        $grouped[$product_id]['quantity'] = $current + max(0.0, (float)$row['variation_stock_quantity']);
                    }
                }
            }
            $products = array_values($grouped);
            foreach ($products as &$product) {
                unset($product['_has_variation'], $product['_exact_variation']);
            }
            unset($product);
            $duration_ms = (int)round((microtime(true) - $started_at) * 1000);
            $this->log('[CmsSearch] WooCommerce products mode=' . ($multi ? 'and' : 'or') . ' results=' . count($products) . ' duration_ms=' . $duration_ms);
            return $products;
        } catch (\Throwable $e) {
            $this->log('[CmsSearch] WooCommerce products failed code=query_error');
            throw new CatalogSearchUnavailableException('wordpress', $e);
        }
    }

    



    public function searchPages(string $query): array {
        $p     = $this->prefix;
        $words = $this->splitWords($query);
        $limit = $this->getSearchLimit();

        $likes     = [];
        $relevance = [];
        foreach ($words as $word) {
            $esc         = $this->escapeLikeWord($word);
            $likes[]     = "(LOWER(p.post_title) LIKE '%{$esc}%' OR LOWER(p.post_content) LIKE '%{$esc}%')";
            $relevance[] = "(CASE WHEN LOWER(p.post_title) LIKE '%{$esc}%' THEN 2 WHEN LOWER(p.post_content) LIKE '%{$esc}%' THEN 1 ELSE 0 END)";
        }

        $rel_expr = implode(' + ', $relevance);
        $make_sql = function (string $op) use ($p, $likes, $rel_expr, $limit): string {
            $cond = implode(" {$op} ", $likes);
            return "SELECT DISTINCT p.ID, p.post_title AS name, p.post_content AS description,
                        ({$rel_expr}) AS relevance
                    FROM `{$p}posts` p
                    WHERE p.post_type = 'page' AND p.post_status = 'publish'
                      AND ({$cond})
                    ORDER BY relevance DESC, p.ID DESC
                    LIMIT {$limit}";
        };

        $multi = count($words) > 1;
        $sql   = $make_sql($multi ? 'AND' : 'OR');
        $used_mode = $multi ? 'and' : 'or';

        try {
            $result = $this->db->query($sql);

            if ($multi && empty($result->rows)) {
                $sql = $make_sql('OR');
                $used_mode = 'or_fallback';
                $result = $this->db->query($sql);
            }

            $pages    = [];
            $base_url = $this->getBaseUrl();
            foreach ($result->rows as $row) {
                $excerpt = mb_substr(trim(strip_tags((string)$row['description'])), 0, 200, 'UTF-8');
                $pages[] = [
                    'id'      => (string)$row['ID'],
                    'name'    => $row['name'],
                    'excerpt' => $excerpt,
                    'link'    => $base_url . '/?page_id=' . $row['ID'],
                ];
            }
            $this->log('[CmsSearch] WordPress pages mode=' . $used_mode . ' results=' . count($pages));
            return $pages;
        } catch (\Throwable $e) {
            $this->logFailure('[CmsSearch] WordPress pages failed', $e);
            return [];
        }
    }

    




    public function resolvePublicUrls(array $items, string $resource_type): array {
        $items = parent::resolvePublicUrls($items, $resource_type);
        if (!$items) {
            return $items;
        }

        $ids = array();
        foreach ($items as $item) {
            $id = (int)($item['id'] ?? 0);
            if ($id > 0) {
                $ids[$id] = $id;
            }
        }
        if (!$ids) {
            return $items;
        }

        $permalink_structure = $this->wordpressOption('permalink_structure');
        if (trim($permalink_structure) === '') {
            return $items;
        }

        $resource_type = strtolower(trim($resource_type));
        $post_type = $resource_type === 'product' ? 'product' : 'page';
        $id_list = implode(',', array_values($ids));
        try {
            $result = $this->db->query(
                "SELECT ID, post_name, post_parent FROM `{$this->prefix}posts`
                 WHERE ID IN ({$id_list}) AND post_type = '" . $this->db->escape($post_type) . "'
                   AND post_status = 'publish'"
            );
        } catch (\Throwable $e) {
            return $items;
        }

        $slugs = array();
        $parents = array();
        foreach (($result->rows ?? array()) as $row) {
            $id = (int)($row['ID'] ?? 0);
            $slug = trim((string)($row['post_name'] ?? ''), '/');
            if ($id > 0 && $slug !== '') {
                $slugs[$id] = $slug;
                $parents[$id] = (int)($row['post_parent'] ?? 0);
            }
        }
        if (!$slugs) {
            return $items;
        }

        $base_url = $this->getBaseUrl();
        $product_base = $this->woocommerceProductBase();
        foreach ($items as &$item) {
            $id = (int)($item['id'] ?? 0);
            if (!isset($slugs[$id])) {
                continue;
            }
            if ($resource_type === 'product' && $product_base === '') {
                 
                 
                 
                continue;
            }
            if ($resource_type !== 'product' && !empty($parents[$id])) {
                 
                 
                continue;
            }
            $path = $resource_type === 'product'
                ? trim($product_base, '/') . '/' . $slugs[$id]
                : $slugs[$id];
            $url = rtrim($base_url, '/') . '/' . trim($path, '/') . '/';
            if (\EasyChat\Library\Catalog\CatalogUrlPolicy::isSafeLink($url)) {
                $item['link'] = $url;
                if (isset($item['url'])) {
                    $item['url'] = $url;
                }
            }
        }
        unset($item);
        return $items;
    }

    private function wordpressOption(string $name): string {
        try {
            $result = $this->db->query(
                "SELECT option_value FROM `{$this->prefix}options`
                 WHERE option_name = '" . $this->db->escape($name) . "' LIMIT 1"
            );
            return (string)($result->row['option_value'] ?? '');
        } catch (\Throwable $e) {
            return '';
        }
    }

    private function woocommerceProductBase(): string {
        $raw = $this->wordpressOption('woocommerce_permalinks');
        $options = @unserialize($raw, array('allowed_classes' => false));
        if (is_array($options)) {
            $base = trim((string)($options['product_base'] ?? ''), '/');
            if ($base !== '' && strpos($base, '%') === false && preg_match('#^[A-Za-z0-9_\-/]+$#', $base)) {
                return $base;
            }
            if ($base !== '') {
                return '';
            }
        }
        return 'product';
    }

    


    public function resolvePageContext(string $page_type, string $page_id): array {
        $page_type = strtolower(preg_replace('/[^a-z0-9_-]/i', '', trim($page_type)));
        $id = (int)$page_id;
        if ($id <= 0) {
            return [];
        }

        if ($page_type === 'product') {
            return $this->resolveProductPageContext($id);
        }
        if ($page_type === 'category') {
            return $this->resolveCategoryPageContext($id);
        }
        if ($page_type === 'information') {
            return $this->resolveInformationPageContext($id);
        }

        return [];
    }

     
     
     

    private function resolveProductPageContext(int $product_id): array {
        $p = $this->prefix;
        try {
            $result = $this->db->query(
                "SELECT p.ID, p.post_title AS name, p.post_content AS description,
                        p.post_excerpt AS summary,
                        COALESCE(price.meta_value, '') AS price,
                        COALESCE(sku.meta_value, '') AS sku
                 FROM `{$p}posts` p
                 LEFT JOIN `{$p}postmeta` price
                    ON price.post_id = p.ID AND price.meta_key = '_price'
                 LEFT JOIN `{$p}postmeta` sku
                    ON sku.post_id = p.ID AND sku.meta_key = '_sku'
                 WHERE p.ID = {$product_id}
                   AND p.post_type = 'product'
                   AND p.post_status NOT IN ('trash', 'auto-draft')
                 LIMIT 1"
            );
        } catch (\Throwable $e) {
            $this->logFailure('[CmsContext] WordPress product failed', $e);
            return [];
        }

        $row = $result->row ?? [];
        if (!$row) {
            return [];
        }

        $lines = ['Тип страницы: товар'];
        $this->addContextLine($lines, 'Название', (string)($row['name'] ?? ''));
        $this->addContextLine($lines, 'ID товара', (string)$product_id);
        $this->addContextLine($lines, 'Артикул', (string)($row['sku'] ?? ''));
        $this->addContextLine($lines, 'Цена', $this->formatContextPrice($row['price'] ?? ''));
        $text = !empty($row['description']) ? (string)$row['description'] : (string)($row['summary'] ?? '');
        $this->addContextLine($lines, 'Описание', $this->contextText($text, 620));

        return [
            'kind' => 'product',
            'page_type' => 'product',
            'page_id' => (string)$product_id,
            'title' => (string)($row['name'] ?? ''),
            'url' => $this->resolveSinglePublicUrl('product', $product_id, $this->getBaseUrl() . '/?p=' . $product_id),
            'facts' => [
                'type' => 'product',
                'product_id' => (string)$product_id,
                'name' => (string)($row['name'] ?? ''),
                'sku' => (string)($row['sku'] ?? ''),
                'price' => $this->formatContextPrice($row['price'] ?? ''),
                'description_excerpt' => $this->contextText($text, 620),
            ],
            'prompt_lines' => $lines,
        ];
    }

    private function resolveCategoryPageContext(int $term_id): array {
        $p = $this->prefix;
        try {
            $result = $this->db->query(
                "SELECT t.term_id, t.name, tt.description, tt.taxonomy
                 FROM `{$p}terms` t
                 JOIN `{$p}term_taxonomy` tt ON tt.term_id = t.term_id
                 WHERE t.term_id = {$term_id}
                   AND tt.taxonomy IN ('product_cat', 'category')
                 LIMIT 1"
            );
        } catch (\Throwable $e) {
            $this->logFailure('[CmsContext] WordPress category failed', $e);
            return [];
        }

        $row = $result->row ?? [];
        if (!$row) {
            return [];
        }

        $lines = ['Тип страницы: категория'];
        $this->addContextLine($lines, 'Название', (string)($row['name'] ?? ''));
        $this->addContextLine($lines, 'ID категории', (string)$term_id);
        $this->addContextLine($lines, 'Таксономия', (string)($row['taxonomy'] ?? ''));
        $this->addContextLine($lines, 'Описание', $this->contextText((string)($row['description'] ?? ''), 620));

        return [
            'kind' => 'category',
            'page_type' => 'category',
            'page_id' => (string)$term_id,
            'title' => (string)($row['name'] ?? ''),
            'url' => $this->getBaseUrl() . '/?cat=' . $term_id,
            'facts' => [
                'type' => 'category',
                'term_id' => (string)$term_id,
                'name' => (string)($row['name'] ?? ''),
                'taxonomy' => (string)($row['taxonomy'] ?? ''),
                'description_excerpt' => $this->contextText((string)($row['description'] ?? ''), 620),
            ],
            'prompt_lines' => $lines,
        ];
    }

    private function resolveInformationPageContext(int $page_id): array {
        $p = $this->prefix;
        try {
            $result = $this->db->query(
                "SELECT ID, post_title AS name, post_content AS description, post_type
                 FROM `{$p}posts`
                 WHERE ID = {$page_id}
                   AND post_type IN ('page', 'post')
                   AND post_status NOT IN ('trash', 'auto-draft')
                 LIMIT 1"
            );
        } catch (\Throwable $e) {
            $this->logFailure('[CmsContext] WordPress page failed', $e);
            return [];
        }

        $row = $result->row ?? [];
        if (!$row) {
            return [];
        }

        $lines = ['Тип страницы: информационная страница'];
        $this->addContextLine($lines, 'Название', (string)($row['name'] ?? ''));
        $this->addContextLine($lines, 'ID страницы', (string)$page_id);
        $this->addContextLine($lines, 'Тип записи WordPress', (string)($row['post_type'] ?? ''));
        $this->addContextLine($lines, 'Содержание', $this->contextText((string)($row['description'] ?? ''), 700));

        return [
            'kind' => 'information',
            'page_type' => 'information',
            'page_id' => (string)$page_id,
            'title' => (string)($row['name'] ?? ''),
            'url' => $this->resolveSinglePublicUrl('page', $page_id, $this->getBaseUrl() . '/?page_id=' . $page_id),
            'facts' => [
                'type' => 'information',
                'page_id' => (string)$page_id,
                'title' => (string)($row['name'] ?? ''),
                'post_type' => (string)($row['post_type'] ?? ''),
                'content_excerpt' => $this->contextText((string)($row['description'] ?? ''), 700),
            ],
            'prompt_lines' => $lines,
        ];
    }

    private function wooAvailability(string $stock_status, string $backorders): string {
        $stock_status = strtolower(trim($stock_status));
        $backorders = strtolower(trim($backorders));
        if ($stock_status === 'instock') {
            return 'in_stock';
        }
        if ($stock_status === 'onbackorder' || in_array($backorders, array('yes', 'notify'), true)) {
            return 'preorder';
        }
        if ($stock_status === 'outofstock') {
            return 'out_of_stock';
        }
        return 'unknown';
    }

    private function productSearchVisibilitySql(string $alias): string {
        $p = $this->prefix;
        return "NOT EXISTS (
            SELECT 1
            FROM `{$p}term_relationships` tr
            JOIN `{$p}term_taxonomy` tt ON tt.term_taxonomy_id = tr.term_taxonomy_id
            JOIN `{$p}terms` term ON term.term_id = tt.term_id
            WHERE tr.object_id = {$alias}.ID
              AND tt.taxonomy = 'product_visibility'
              AND term.slug = 'exclude-from-search'
        )";
    }

    private function wooAvailabilityLabel(string $availability): string {
        if ($availability === 'in_stock') {
            return 'В наличии';
        }
        if ($availability === 'preorder') {
            return 'Доступно под заказ';
        }
        if ($availability === 'out_of_stock') {
            return 'Нет в наличии';
        }
        return 'Наличие уточняется';
    }

    private function availabilityPriority(string $availability): int {
        if ($availability === 'in_stock') {
            return 3;
        }
        if ($availability === 'preorder') {
            return 2;
        }
        if ($availability === 'unknown') {
            return 1;
        }
        return 0;
    }

    private function getCurrencyCode(): string {
        if ($this->currency_cache !== null) {
            return $this->currency_cache;
        }
        $this->currency_cache = '';
        try {
            $result = $this->db->query(
                "SELECT option_value FROM `{$this->prefix}options`
                 WHERE option_name = 'woocommerce_currency' LIMIT 1"
            );
            $currency = strtoupper(trim((string)($result->row['option_value'] ?? '')));
            if (preg_match('/^[A-Z]{3}$/', $currency)) {
                $this->currency_cache = $currency;
            }
        } catch (\Throwable $e) {
        }
        return $this->currency_cache;
    }

    private function formatCatalogPrice(float $price, string $currency): string {
        if ($price <= 0) {
            return 'Цена по запросу';
        }
        $formatted = number_format($price, 2, '.', '');
        return $currency !== '' ? $formatted . ' ' . $currency : $formatted;
    }

    private function normalizeCatalogIdentifier(string $value): string {
        $value = mb_strtolower(trim($value), 'UTF-8');
        $value = str_replace('\\', '', $value);
        return (string)preg_replace('/[\s\-_.\/]+/u', '', $value);
    }

    private function addContextLine(array &$lines, string $label, string $value): void {
        $value = $this->contextText($value, 900);
        if ($value !== '') {
            $lines[] = $label . ': ' . $value;
        }
    }

    private function contextText(string $value, int $limit): string {
        $value = html_entity_decode(strip_tags($value), ENT_QUOTES | ENT_HTML5, 'UTF-8');
        $value = preg_replace('/\s+/u', ' ', trim($value));
        return mb_strlen($value, 'UTF-8') > $limit ? mb_substr($value, 0, $limit, 'UTF-8') : $value;
    }

    private function formatContextPrice($value): string {
        if ($value === null || $value === '') {
            return '';
        }
        $price = (float)$value;
        return $price > 0 ? number_format($price, 2, '.', '') : '';
    }

    private function getAttributes(int $product_id, string $p): array {
        $sql = "SELECT meta_key, meta_value
                FROM `{$p}postmeta`
                WHERE post_id = $product_id
                  AND meta_key LIKE 'attribute_%' AND meta_value != ''
                ORDER BY meta_key ASC";
        try {
            $result = $this->db->query($sql);
            $attrs  = [];
            foreach ($result->rows as $row) {
                $label   = preg_replace('/^attribute_(pa_)?/i', '', $row['meta_key']);
                $label   = str_replace(['-', '_'], ' ', $label);
                $attrs[] = $label . ': ' . $row['meta_value'];
            }
            return $attrs;
        } catch (\Throwable $e) {
            return [];
        }
    }

    private function getVariationOptions(int $product_id, string $p): array {
        $sql = "SELECT pm.meta_key,
                       GROUP_CONCAT(DISTINCT pm.meta_value ORDER BY pm.meta_value SEPARATOR ', ') AS vals
                FROM `{$p}posts` v
                JOIN `{$p}postmeta` pm ON pm.post_id = v.ID AND pm.meta_key LIKE 'attribute_%'
                WHERE v.post_parent = $product_id
                  AND v.post_type = 'product_variation' AND v.post_status = 'publish'
                  AND pm.meta_value != ''
                GROUP BY pm.meta_key
                ORDER BY pm.meta_key ASC";
        try {
            $result  = $this->db->query($sql);
            $options = [];
            foreach ($result->rows as $row) {
                $label     = preg_replace('/^attribute_(pa_)?/i', '', $row['meta_key']);
                $label     = str_replace(['-', '_'], ' ', $label);
                $options[] = $label . ': ' . $row['vals'];
            }
            return $options;
        } catch (\Throwable $e) {
            return [];
        }
    }
}
