/**
 * Highlight Component
 *
 * Renders highlighted search result text with matching terms emphasized
 */
import React, { useMemo } from 'react';
import { useSearchContext } from './SearchProvider';
import { getHighlightedValue, parseHighlightedParts, parseQueryHighlightParts, } from '@seekora-ai/ui-sdk-core';
export const Highlight = ({ hit, attribute, className, style, theme: customTheme, tagName: Tag = 'span', renderHighlighted, renderNonHighlighted, query, }) => {
    const { theme } = useSearchContext();
    const highlightTheme = customTheme || {};
    // Get highlighted value from hit
    const highlightedValue = useMemo(() => {
        return getHighlightedValue({
            hit,
            attribute,
            preTag: '<mark>',
            postTag: '</mark>',
        });
    }, [hit, attribute]);
    // Parse into parts
    const parts = useMemo(() => {
        // First try server-provided highlights
        const serverParts = parseHighlightedParts(highlightedValue, '<mark>', '</mark>');
        // If no highlights found and we have a query, do client-side highlighting
        if (serverParts.every((p) => !p.isHighlighted) && query) {
            const plainValue = hit[attribute] || highlightedValue;
            return parseQueryHighlightParts(String(plainValue), query);
        }
        return serverParts;
    }, [highlightedValue, query, hit, attribute]);
    // Default highlight style
    const defaultHighlightStyle = {
        backgroundColor: theme.colors.warning || '#fff59d',
        fontWeight: theme.typography.fontWeight?.semibold || 600,
        padding: '0 2px',
        borderRadius: '2px',
    };
    // Render parts
    const renderedParts = parts.map((part, index) => {
        if (part.isHighlighted) {
            if (renderHighlighted) {
                return React.createElement(React.Fragment, { key: index }, renderHighlighted(part, index));
            }
            return (React.createElement("mark", { key: index, className: highlightTheme.highlighted, style: defaultHighlightStyle }, part.value));
        }
        if (renderNonHighlighted) {
            return React.createElement(React.Fragment, { key: index }, renderNonHighlighted(part, index));
        }
        return (React.createElement("span", { key: index, className: highlightTheme.nonHighlighted }, part.value));
    });
    return (React.createElement(Tag, { className: className || highlightTheme.root, style: style }, renderedParts));
};
export const Snippet = ({ hit, attribute, maxLength = 100, ellipsis = '...', className, style, theme: customTheme, tagName: Tag = 'span', renderHighlighted, renderNonHighlighted, query, }) => {
    const { theme } = useSearchContext();
    const snippetTheme = customTheme || {};
    // Get snippet or truncated highlighted value
    const snippetValue = useMemo(() => {
        // Check for server-provided snippet
        const snippetResult = hit._snippetResult?.[attribute]?.value ||
            hit.snippet_result?.[attribute]?.value ||
            hit._snippet?.[attribute] ||
            hit.snippet?.[attribute];
        if (snippetResult) {
            return snippetResult
                .replace(/<em>/g, '<mark>')
                .replace(/<\/em>/g, '</mark>');
        }
        // Fall back to highlighted value, truncated
        let text = getHighlightedValue({
            hit,
            attribute,
            preTag: '<mark>',
            postTag: '</mark>',
        });
        // Remove tags for length calculation
        const plainText = text.replace(/<\/?mark>/g, '');
        if (plainText.length > maxLength) {
            // Need to truncate while preserving tags
            const truncated = truncateWithTags(text, maxLength);
            return truncated + ellipsis;
        }
        return text;
    }, [hit, attribute, maxLength, ellipsis]);
    // Parse into parts
    const parts = useMemo(() => {
        const serverParts = parseHighlightedParts(snippetValue, '<mark>', '</mark>');
        if (serverParts.every((p) => !p.isHighlighted) && query) {
            const plainValue = hit[attribute] || snippetValue;
            let text = String(plainValue);
            if (text.length > maxLength) {
                text = text.substring(0, maxLength) + ellipsis;
            }
            return parseQueryHighlightParts(text, query);
        }
        return serverParts;
    }, [snippetValue, query, hit, attribute, maxLength, ellipsis]);
    // Default highlight style
    const defaultHighlightStyle = {
        backgroundColor: theme.colors.warning || '#fff59d',
        fontWeight: theme.typography.fontWeight?.semibold || 600,
        padding: '0 2px',
        borderRadius: '2px',
    };
    // Render parts
    const renderedParts = parts.map((part, index) => {
        if (part.isHighlighted) {
            if (renderHighlighted) {
                return React.createElement(React.Fragment, { key: index }, renderHighlighted(part, index));
            }
            return (React.createElement("mark", { key: index, className: snippetTheme.highlighted, style: defaultHighlightStyle }, part.value));
        }
        if (renderNonHighlighted) {
            return React.createElement(React.Fragment, { key: index }, renderNonHighlighted(part, index));
        }
        return (React.createElement("span", { key: index, className: snippetTheme.nonHighlighted }, part.value));
    });
    return (React.createElement(Tag, { className: className || snippetTheme.root, style: style }, renderedParts));
};
/**
 * Truncate text while preserving HTML tags
 */
function truncateWithTags(text, maxLength) {
    let visibleLength = 0;
    let result = '';
    let inTag = false;
    for (let i = 0; i < text.length; i++) {
        const char = text[i];
        if (char === '<') {
            inTag = true;
            result += char;
        }
        else if (char === '>') {
            inTag = false;
            result += char;
        }
        else if (inTag) {
            result += char;
        }
        else {
            if (visibleLength >= maxLength) {
                break;
            }
            result += char;
            visibleLength++;
        }
    }
    // Close any unclosed tags
    const openTags = result.match(/<mark>/g)?.length || 0;
    const closeTags = result.match(/<\/mark>/g)?.length || 0;
    for (let i = 0; i < openTags - closeTags; i++) {
        result += '</mark>';
    }
    return result;
}
