/* global LimbChatbot */
import {__} from '@wordpress/i18n';
import {useState, useEffect, useRef, useCallback} from '@wordpress/element';

const LAYOUT_VERTICAL = 'vertical';

const getLayout = () => {
    return LAYOUT_VERTICAL;
};

const getItems = (items) => {
    if (!Array.isArray(items)) {
        return [];
    }

    return items;
};

// WordPress native image sizes (excluding custom sizes like liquid-*)
const WP_NATIVE_SIZES = ['thumbnail', 'medium', 'medium_large', 'large', '1536x1536', '2048x2048', 'full'];

const normalizeImage = (image, fallbackTitle, thumbnailUrls = null) => {
    if (!image && !thumbnailUrls) {
        return null;
    }

    // If we have thumbnail_urls, use them to build srcset
    if (thumbnailUrls && typeof thumbnailUrls === 'object' && !Array.isArray(thumbnailUrls)) {
        // Filter to only WP native sizes
        const nativeSizes = {};
        for (const sizeName of WP_NATIVE_SIZES) {
            if (thumbnailUrls[sizeName] && thumbnailUrls[sizeName].url) {
                nativeSizes[sizeName] = thumbnailUrls[sizeName];
            }
        }

        // If no native sizes found, return null
        if (Object.keys(nativeSizes).length === 0) {
            return null;
        }

        // Build srcset from native sizes, avoiding duplicates
        // Deduplicate by URL (same URL should only appear once with its maximum width)
        const urlMap = new Map(); // URL -> {width, sizeName}
        let defaultSrc = null;

        // Order sizes from smallest to largest
        const sizeOrder = ['thumbnail', 'medium', 'medium_large', 'large', '1536x1536', '2048x2048', 'full'];
        for (const sizeName of sizeOrder) {
            if (nativeSizes[sizeName]) {
                const sizeData = nativeSizes[sizeName];
                const url = sizeData.url;
                const width = sizeData.width;

                // If URL already exists, update to keep the maximum width
                if (urlMap.has(url)) {
                    const existing = urlMap.get(url);
                    if (width > existing.width) {
                        urlMap.set(url, {width, sizeName});
                    }
                } else {
                    urlMap.set(url, {width, sizeName});
                }

                // Use medium_large or large as default, fallback to first available
                if (!defaultSrc) {
                    if (sizeName === 'medium_large' || sizeName === 'large') {
                        defaultSrc = url;
                    }
                }
            }
        }

        // Build srcset array from deduplicated URLs, sorted by width
        // Also ensure width descriptors are unique
        const widthMap = new Map(); // width -> url (to ensure unique widths)
        const srcsetParts = [];

        for (const [url, {width}] of Array.from(urlMap.entries()).sort((a, b) => a[1].width - b[1].width)) {
            // If this width is already used, skip it (prefer first URL for each width)
            if (!widthMap.has(width)) {
                widthMap.set(width, url);
                srcsetParts.push(`${url} ${width}w`);
            }
        }

        // If no default src set, use the largest available (last in sorted order)
        if (!defaultSrc && srcsetParts.length > 0) {
            // Extract URL from the last (largest) srcset entry
            const lastEntry = srcsetParts[srcsetParts.length - 1];
            defaultSrc = lastEntry.split(' ')[0];
        }

        return {
            src: defaultSrc,
            srcset: srcsetParts.join(', '),
            sizes: '(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw',
            alt: image?.alt || image?.name || fallbackTitle || '',
        };
    }

    // Fallback to old structure
    const src = image?.src || image?.thumbnail || image?.url;

    if (!src) {
        return null;
    }

    return {
        src,
        alt: image.alt || image.name || fallbackTitle || '',
    };
};

const formatRecommendationItem = (item) => {
    if (!item) {
        return null;
    }

    // Extract title - handle WordPress REST API structure (title.rendered) or direct title
    const title = [
        item.title?.rendered,
        item.title,
        item.name
    ].find(val => typeof val === 'string' && val.trim()) || '';

    // Extract excerpt/description - handle WordPress REST API structure (excerpt.rendered) or direct excerpt
    const excerpt = [
        item.excerpt?.rendered,
        item.excerpt,
        item.description,
        item.short_description
    ].find(val => typeof val === 'string' && val.trim()) || '';

    // Extract link - handle permalink or link
    const link = item.permalink || item.link || '';

    // Handle images - check thumbnail_url first (direct URL), then thumbnail_urls, then featured_media, images array, image object, or thumbnail
    let image = null;

    // Check if thumbnail_url exists and is a valid image URL
    const hasValidThumbnailUrl = item.thumbnail_url &&
        typeof item.thumbnail_url === 'string' &&
        item.thumbnail_url.trim() !== '' &&
        (item.thumbnail_url.match(/\.(jpg|jpeg|png|gif|webp|svg)(\?|$)/i) || item.thumbnail_url.startsWith('http'));

    if (hasValidThumbnailUrl) {
        // thumbnail_url is a direct URL string
        image = normalizeImage({src: item.thumbnail_url}, title);
    } else if (item.thumbnail_urls && (typeof item.thumbnail_urls === 'object' && !Array.isArray(item.thumbnail_urls)) && Object.keys(item.thumbnail_urls).length > 0) {
        // Use thumbnail_urls structure with srcset
        image = normalizeImage(null, title, item.thumbnail_urls);
    } else if (item.featured_media && item.featured_media !== 0) {
        // If featured_media is an object with source_url
        if (typeof item.featured_media === 'object' && item.featured_media.source_url) {
            image = normalizeImage({src: item.featured_media.source_url}, title);
        }
        // If featured_media is an ID, we'd need to fetch it, but for now we'll skip
    } else if (Array.isArray(item.images) && item.images.length > 0) {
        image = normalizeImage(item.images[0], title);
    } else if (item.image) {
        image = normalizeImage(item.image, title);
    } else if (item.thumbnail) {
        image = normalizeImage(item.thumbnail, title);
    } else if (item.featured_image) {
        image = normalizeImage(item.featured_image, title);
    }

    return {
        id: item.id,
        title,
        description: excerpt,
        image,
        link,
        type: item.type || item.post_type || 'post', // Preserve post type for selection
    };
};

const getRecommendationItems = (parameter, config) => {
    if (!parameter) {
        return [];
    }

    const results = parameter?.search_results?.results;

    if (!Array.isArray(results) || results.length === 0) {
        return [];
    }

    return results.map(formatRecommendationItem).filter(Boolean);
};

const getLink = (item) => {
    if (!item?.link) {
        return null;
    }

    if (typeof item.link === 'string') {
        return {
            href: item.link,
            title: item.link,
        };
    }

    return {
        href: item.link.href || item.link.url,
        title: item.link.title || item.link.label || item.title || '',
    };
};

const getButtonLabel = (item, fallback) => {
    if (item?.button?.label) {
        return item.button.label;
    }

    if (typeof item?.button === 'string') {
        return item.button;
    }

    return fallback;
};

const emitHook = (hookName, payload) => {
    if (!hookName) {
        return;
    }

    if (typeof LimbChatbot !== 'undefined' && LimbChatbot?.Hooks?.doAction) {
        LimbChatbot.Hooks.doAction(hookName, payload);
    }
};

const handleFilterClick = (config) => {
    if (!config) {
        return;
    }

    if (typeof config.onFilterClick === 'function') {
        config.onFilterClick(config);
        return;
    }

    emitHook(config.filterAction, config);
};

const handleExtendedSelection = (config, item) => {
    if (!config) {
        return false;
    }

    if (typeof config.onSelect === 'function') {
        config.onSelect(item, config);
        return true;
    }

    if (config.selectAction) {
        emitHook(config.selectAction, {item, config});
        return true;
    }

    return false;
};

const RecommendationCardHeader = ({item, link}) => {
    if (!item?.title && !link) {
        return null;
    }

    return (
        <a className='lbaic-recommendation-card__header'
           href={link.href}
           target='_blank'
           rel='noopener noreferrer'
           title={link.title}
        >
            {item?.title && (
                <h3 className='lbaic-recommendation-card__title' dangerouslySetInnerHTML={{__html: item.title}}/>
            )}
            {link?.href && (
                <div
                    className='lbaic-recommendation-card__external'
                >
                    <svg className='lbaic-recommendation-card__external-i' viewBox='0 0 24 24'
                         xmlns='http://www.w3.org/2000/svg'>
                        <use className='lbaic-arrow' href='#lbaic-card-external-link-arrow'/>
                        <use href='#lbaic-card-external-link-box'/>
                    </svg>
                </div>
            )}
        </a>
    );
};

const RecommendationHeroMedia = ({item}) => {
    if (!item?.image?.src && !item?.media?.src && !item?.thumbnail?.src) {
        return null;
    }

    const media = item.image || item.media || item.thumbnail;

    return (
        <div className='lbaic-recommendation-card__media'>
            <img
                className='lbaic-recommendation-card__media-img'
                src={media.src}
                srcSet={media.srcset || undefined}
                sizes={media.sizes || undefined}
                alt={media.alt || item?.title || ''}
                loading='lazy'
            />
        </div>
    );
};


const RecommendationSelectButton = ({onClick, label, disabled}) => (
    <button
        type='button'
        className='lbaic-recommendation-card__select'
        onClick={onClick}
        disabled={disabled}
    >
        {label}
    </button>
);

const RecommendationViewButton = ({href, label, disabled}) => {
    if (!href) {
        return null;
    }

    return (
        <a
            href={href}
            target='_blank'
            rel='noopener noreferrer'
            className='lbaic-recommendation-card__select'
            onClick={(e) => {
                if (disabled) {
                    e.preventDefault();
                }
            }}
        >
            <span className='lbaic-recommendation-card__select-label'>{label}</span>
            <svg className='lbaic-recommendation-card__select-i' viewBox='0 0 24 24'
                 xmlns='http://www.w3.org/2000/svg'>
                <use href='#lbaic-card-external-link'/>
            </svg>
        </a>
    );
};

const RecommendationEmptyState = ({config}) => {
    const title = config?.emptyState?.title || config?.empty_state?.title || __('No results yet', 'limb-chatbot');
    const description = config?.emptyState?.description || config?.empty_state?.description || __('Try updating your search or refine filters.', 'limb-chatbot');

    return (
        <div className='lbaic-recommendation__empty'>
            <p className='lbaic-recommendation__empty-title'>{title}</p>
            {description && <p className='lbaic-recommendation__empty-description'>{description}</p>}
        </div>
    );
};

const RecommendationVerticalCard = ({item, index, config, onSelect, selectLabel, disabled, hasCallback}) => {
    const link = getLink(item);
    const showViewButton = hasCallback === false;
    const viewLabel = __('View', 'limb-chatbot');

    return (
        <div className='lbaic-recommendation-card lbaic-recommendation-card--vertical'
             key={`recommendation-card-${index}`}>
            <RecommendationHeroMedia item={item}/>
            <div className='lbaic-recommendation-card__body'>
                <RecommendationCardHeader item={item} link={link}/>
                <div className="lbaic-recommendation-card__body-in">
                    {item?.description && (
                        <div
                            className='lbaic-recommendation-card__description'
                            dangerouslySetInnerHTML={{__html: item.description}}
                        />
                    )}
                </div>
                <div className="lbaic-recommendation-card__footer">
                    <div className="lbaic-intro-label-body lbaic-intro-label-body-scroll">
                        {showViewButton ? (
                            <RecommendationViewButton
                                href={link?.href}
                                label={viewLabel}
                                disabled={disabled || item?.disabled}
                            />
                        ) : (
                            <RecommendationSelectButton
                                onClick={() => onSelect(item, config)}
                                label={getButtonLabel(item, selectLabel)}
                                disabled={disabled || item?.disabled}
                            />
                        )}
                    </div>
                </div>
            </div>
        </div>
    );
};


const RecommendationList = ({items, config, onSelect, selectLabel, disabled, hasCallback}) => {
    if (items.length === 0) {
        return <RecommendationEmptyState config={config}/>;
    }

    return (
        <div className='lbaic-recommendation__list lbaic-recommendation__list--vertical'>
            {items.map((item, index) => (
                <RecommendationVerticalCard
                    key={`recommendation-card-${index}`}
                    item={item}
                    index={index}
                    config={config}
                    onSelect={onSelect}
                    selectLabel={selectLabel}
                    disabled={disabled}
                    hasCallback={hasCallback}
                />
            ))}
        </div>
    );
};

export default function RecommendationInput({
                                                text,
                                                placeholder,
                                                config,
                                                parameter,
                                                onButtonClick,
                                                onCancelAction,
                                                onSkipAction,
                                                skipDisabled,
                                                skipLabel,
                                                send,
                                                chatbotUtility,
                                                isInProcess,
                                                isSavingPreviewSettings,
                                                sendLabel,
                                            }) {
    const [searchResults, setSearchResults] = useState(null);
    const [isSearching, setIsSearching] = useState(false);
    const debounceTimerRef = useRef(null);
    const initializedSearchQueryRef = useRef(null);
    const searchInputRef = useRef(null);
    const currentSearchKeywordRef = useRef(null);
    const abortControllerRef = useRef(null);

    // Local state for search input (separate from main textarea)
    const [searchText, setSearchText] = useState('');

    const layout = getLayout();
    const initialRecommendationItems = getRecommendationItems(parameter, config);
    const fallbackItems = getItems(config?.items);

    // Use search results if available, otherwise use initial items
    const recommendationItems = searchResults?.results ?
        searchResults.results.map(formatRecommendationItem).filter(Boolean) :
        initialRecommendationItems;
    const items = recommendationItems.length > 0 ? recommendationItems : fallbackItems;
    const searchPlaceholder = parameter?.label || placeholder || __('Search...', 'limb-chatbot');
    const cancelLabel = config?.cancel_label || __('Cancel', 'limb-chatbot');
    const filterLabel = config?.filter_label || __('Filter', 'limb-chatbot');
    const selectLabel = config?.select_label || __('Select', 'limb-chatbot');
    const showFilter = Boolean(config?.show_filter);
    const showSkip = typeof onSkipAction === 'function';
    const disableSelection = isInProcess || isSavingPreviewSettings || config?.disableSelection;
    const showSearch = config?.showSearch ?? config?.show_search ?? true;

    // Initialize search input with search_query when parameter appears (only once, as initial value)
    useEffect(() => {
        if (parameter?.search_results?.search_query) {
            const parameterId = parameter.id;
            const searchQuery = parameter.search_results.search_query.trim();

            // Only initialize if we haven't already initialized for this parameter and searchText is empty
            if (searchQuery && initializedSearchQueryRef.current !== parameterId && searchText === '') {
                setSearchText(searchQuery);
                initializedSearchQueryRef.current = parameterId;
            } else if (initializedSearchQueryRef.current !== parameterId) {
                // Mark as initialized even if we didn't set the text (to prevent future initialization)
                initializedSearchQueryRef.current = parameterId;
            }
        } else if (!parameter) {
            // Reset initialization tracking when parameter is removed
            initializedSearchQueryRef.current = null;
            setSearchText('');
        }
    }, [parameter, searchText]);

    // Search recommendations using chatbotUtility
    const searchRecommendations = useCallback(async (searchKeyword) => {
        if (!parameter?.id || !chatbotUtility) {
            return;
        }

        // Cancel any previous in-flight request
        if (abortControllerRef.current) {
            abortControllerRef.current.abort();
        }

        // Create a new AbortController for this request
        const abortController = new AbortController();
        abortControllerRef.current = abortController;

        // Mark this as the current search
        currentSearchKeywordRef.current = searchKeyword;
        setIsSearching(true);

        try {
            const data = await chatbotUtility.searchParameter(parameter.id, searchKeyword, abortController.signal);

            // Only update state if this is still the current search (not cancelled by new input)
            // and the request wasn't aborted
            if (currentSearchKeywordRef.current === searchKeyword && !abortController.signal.aborted) {
                if (data && data.status === 'success' && Array.isArray(data.results)) {
                    setSearchResults(data);
                } else {
                    setSearchResults(null);
                }
            }
        } catch (e) {
            // Ignore AbortError as it's expected when cancelling requests
            if (e.name === 'AbortError') {
                // If this search was aborted and is no longer current, reset isSearching
                if (currentSearchKeywordRef.current !== searchKeyword) {
                    setIsSearching(false);
                }
                return;
            }

            // Only log error if this is still the current search
            if (currentSearchKeywordRef.current === searchKeyword) {
                console.error(e);
                setSearchResults(null);
            }
        } finally {
            // Only update isSearching if this is still the current search and wasn't aborted
            if (currentSearchKeywordRef.current === searchKeyword && !abortController.signal.aborted) {
                setIsSearching(false);
                // Autofocus search input after search completes
                if (searchInputRef?.current) {
                    setTimeout(() => {
                        searchInputRef.current?.focus();
                    }, 0);
                }
            }
        }
    }, [parameter?.id, chatbotUtility]);

    // Debounced search handler
    const handleSearchChange = useCallback((searchValue) => {
        // Clear previous timer
        if (debounceTimerRef.current) {
            clearTimeout(debounceTimerRef.current);
        }

        // Cancel any in-flight request
        if (abortControllerRef.current) {
            abortControllerRef.current.abort();
            abortControllerRef.current = null;
        }

        // Cancel any in-flight request by marking it as stale
        // This ensures that if user types again while a request is in progress,
        // the old request's results will be ignored
        currentSearchKeywordRef.current = null;

        const trimmedValue = searchValue?.trim() || '';

        // Set new timer for debounced search
        debounceTimerRef.current = setTimeout(() => {
            searchRecommendations(trimmedValue);
        }, 500); // 500ms debounce
    }, [searchRecommendations]);

    // Handle search input change (separate from main textarea)
    const handleSearchInputChange = useCallback((e) => {
        const newValue = e.target.value;
        setSearchText(newValue);
        handleSearchChange(newValue);
    }, [handleSearchChange]);

    // Cleanup on unmount
    useEffect(() => {
        return () => {
            // Clear debounce timer
            if (debounceTimerRef.current) {
                clearTimeout(debounceTimerRef.current);
            }
            // Cancel any in-flight requests
            if (abortControllerRef.current) {
                abortControllerRef.current.abort();
                abortControllerRef.current = null;
            }
        };
    }, []);

    const handleSelection = (item) => {
        if (!item) {
            return;
        }

        const handled = handleExtendedSelection(config, item);

        if (handled) {
            return;
        }

        // Send structured message with post title and ID
        if (typeof send === 'function' && !isInProcess && item?.id && config?.cpt) {
            const postTitle = item?.title || __('No title', 'limb-chatbot');
            const postId = item.id;
            const postType = config.cpt;

            if (postTitle && postId) {
                send({
                    role: 'user',
                    content: [
                        {
                            type: 'text',
                            text: {
                                value: postTitle,
                            },
                        },
                        {
                            type: 'parameter_value',
                            parameter_value: {
                                value: postId,
                                post_type: postType,
                            },
                        },
                    ],
                });
                return;
            }
        }

        // Fallback to default behavior
        if (typeof onButtonClick === 'function') {
            const selectionValue = item?.actionValue ?? item?.action_value ?? item?.value ?? item?.title ?? '';
            onButtonClick(selectionValue);
        }
    };

    return (
        <div className='lbaic-recommendation lbaic-recommendation--vertical'>
            <div className='lbaic-recommendation__wrapper'>
                <div className='lbaic-recommendation__header lbaic-recommendation__header--sticky'>
                    <div className='lbaic-recommendation__header-row'>
                        {showSearch && (
                            <div className='lbaic-recommendation__search'>
                                <svg className='lbaic-recommendation__search-icon'
                                     xmlns='http://www.w3.org/2000/svg'>
                                    <use href='#lbaic-search'/>
                                </svg>
                                <input
                                    ref={searchInputRef}
                                    type='text'
                                    className='lbaic-recommendation__search-input'
                                    placeholder={searchPlaceholder}
                                    value={searchText}
                                    onChange={handleSearchInputChange}
                                    onKeyDown={(e) => {
                                        // Prevent Enter from submitting, just allow typing
                                        if (e.key === 'Enter') {
                                            e.preventDefault();
                                        }
                                    }}
                                    disabled={disableSelection}
                                    autoComplete="off"
                                    autoFocus
                                />
                                {isSearching && (
                                    <div className='lbaic-recommendation__search-loading'>
                                        <svg xmlns="http://www.w3.org/2000/svg">
                                            <use href="#lbaic-circle"></use>
                                        </svg>
                                    </div>
                                )}
                            </div>
                        )}
                        {showFilter && (
                            <button
                                type='button'
                                className='lbaic-button-recommendation lbaic-recommendation__filter'
                                onClick={() => handleFilterClick(config)}
                                disabled={disableSelection}
                                aria-label={filterLabel}
                                title={filterLabel}
                            >
                                <svg className='lbaic-recommendation-icon' viewBox='0 0 24 24'
                                     xmlns='http://www.w3.org/2000/svg'>
                                    <use href='#lbaic-filter'/>
                                </svg>
                            </button>
                        )}
                        {showSkip && (
                            <button
                                type='button'
                                className='lbaic-button-recommendation lbaic-recommendation__skip'
                                onClick={onSkipAction}
                                disabled={skipDisabled}
                            >
                                {skipLabel}
                            </button>
                        )}
                        {typeof onCancelAction === 'function' && (
                            <button
                                type='button'
                                className='lbaic-button-recommendation lbaic-recommendation__cancel'
                                onClick={onCancelAction}
                                disabled={isInProcess || isSavingPreviewSettings}
                            >
                                <svg className='lbaic-recommendation-icon' xmlns='http://www.w3.org/2000/svg'
                                     viewBox='0 0 24 24'>
                                    <use href='#lbaic-cancel'/>
                                </svg>
                                <span className='lbaic-recommendation-label'>{cancelLabel}</span>
                            </button>
                        )}
                    </div>
                    {config?.meta && (
                        <div className='lbaic-recommendation__meta'>
                            {Array.isArray(config.meta) ? config.meta.map((metaItem, index) => (
                                <span className='lbaic-recommendation__meta-item' key={`recommendation-meta-${index}`}>
                                    {metaItem}
                                </span>
                            )) : <span className='lbaic-recommendation__meta-item'>{config.meta}</span>}
                        </div>
                    )}
                </div>
                <div className='lbaic-recommendation__content lbaic-recommendation__content--vertical'>
                    <RecommendationList
                        items={items}
                        config={config}
                        onSelect={handleSelection}
                        selectLabel={selectLabel}
                        disabled={disableSelection}
                        hasCallback={parameter?.has_callback}
                    />
                </div>
                {config?.footer && (
                    <div className='lbaic-recommendation__footer'>
                        {config.footer}
                    </div>
                )}
                {config?.sendAction && (
                    <div className='lbaic-recommendation__footer'>
                        <button
                            type='button'
                            className='lbaic-recommendation__footer-send'
                            onClick={() => emitHook(config.sendAction, {config, text})}
                            disabled={disableSelection}
                        >
                            {sendLabel || __('Send', 'limb-chatbot')}
                        </button>
                    </div>
                )}
            </div>
        </div>
    );
}

export const validateRecommendationInput = () => true;