import React, { Fragment, useRef, useState, useEffect } from 'react';
import styled from 'styled-components';

import type { MouseEvent } from 'react';
import type { SearchFacetCount, SearchItemData } from '@redocly/theme/core/types';

import { SearchInput } from '@redocly/theme/components/Search/SearchInput';
import { SearchShortcut } from '@redocly/theme/components/Search/SearchShortcut';
import { Button } from '@redocly/theme/components/Button/Button';
import { breakpoints, concatClassNames } from '@redocly/theme/core/utils';
import { SearchItem } from '@redocly/theme/components/Search/SearchItem';
import { SearchRecent } from '@redocly/theme/components/Search/SearchRecent';
import { SearchSuggestedPages } from '@redocly/theme/components/Search/SearchSuggestedPages';
import { useThemeHooks, useDialogHotKeys, useSearchFilter } from '@redocly/theme/core/hooks';
import { Tag } from '@redocly/theme/components/Tag/Tag';
import { CloseIcon } from '@redocly/theme/icons/CloseIcon/CloseIcon';
import { SearchFilter } from '@redocly/theme/components/Search/SearchFilter';
import { SearchGroups } from '@redocly/theme/components/Search/SearchGroups';
import { Typography } from '@redocly/theme/components/Typography/Typography';
import { SpinnerLoader } from '@redocly/theme/components/Loaders/SpinnerLoader';
import { SearchAiDialog } from '@redocly/theme/components/Search/SearchAiDialog';
import { SettingsIcon } from '@redocly/theme/icons/SettingsIcon/SettingsIcon';
import { AiStarsIcon } from '@redocly/theme/icons/AiStarsIcon/AiStarsIcon';
import { ReturnKeyIcon } from '@redocly/theme/icons/ReturnKeyIcon/ReturnKeyIcon';
import { ChevronLeftIcon } from '@redocly/theme/icons/ChevronLeftIcon/ChevronLeftIcon';
import { EditIcon } from '@redocly/theme/icons/EditIcon/EditIcon';

export type SearchDialogProps = {
  onClose: () => void;
  className?: string;
};

export function SearchDialog({ onClose, className }: SearchDialogProps): JSX.Element {
  const { useTranslate, useCurrentProduct, useSearch, useProducts, useAiSearch, useOtelTelemetry } =
    useThemeHooks();
  const otelTelemetry = useOtelTelemetry();
  const products = useProducts();
  const currentProduct = useCurrentProduct();
  const [product, setProduct] = useState(currentProduct);
  const [mode, setMode] = useState<'search' | 'ai-dialog'>('search');
  const autoSearchDisabled = mode !== 'search';
  const {
    query,
    setQuery,
    filter,
    setFilter,
    items,
    isSearchLoading,
    facets,
    setLoadMore,
    advancedSearch,
    askAi,
    groupField,
  } = useSearch(product?.name, autoSearchDisabled);
  const {
    isFilterOpen,
    onFilterToggle,
    onFilterChange,
    onFilterReset,
    onFacetReset,
    onQuickFilterReset,
  } = useSearchFilter(filter, setFilter);
  const aiSearch = useAiSearch({ filter });

  const searchInputRef = useRef<HTMLInputElement>(null);
  const modalRef = useRef<HTMLDivElement>(null);
  const aiQueryRef = useRef<HTMLDivElement>(null);
  const firstSearchResultRef = useRef<HTMLAnchorElement>(null);
  const searchKeysWithResults = items ? Object.keys(items).filter((key) => items[key]?.length) : [];

  const { translate } = useTranslate();

  useDialogHotKeys(modalRef, onClose);

  const focusSearchInput = () => {
    requestAnimationFrame(() => {
      searchInputRef.current?.focus();
    });
  };

  useEffect(() => {
    if (mode === 'ai-dialog' && aiSearch.isGeneratingResponse) {
      setQuery('');
    }
  }, [mode, aiSearch.isGeneratingResponse, setQuery]);

  useEffect(focusSearchInput, []);

  const handleOverlayClick = (event: MouseEvent<HTMLElement>) => {
    const target = event.target as HTMLElement;
    if (typeof target.className !== 'string') return;
    if (target.className?.includes(' overlay')) {
      onClose();
    }
  };

  const mapItem = (
    item: SearchItemData,
    index: number,
    results: SearchItemData[],
    innerRef?: React.Ref<HTMLAnchorElement>,
  ) => {
    let itemProduct;
    if (!product && item.document.product) {
      const folder = item.document.product?.folder;
      const resolvedProduct = products.find((product) =>
        product.slug.match(`/${folder.startsWith('./') ? folder.slice(2) : folder}/`),
      );
      itemProduct = resolvedProduct
        ? { name: resolvedProduct.name, icon: resolvedProduct.icon }
        : undefined;
    }

    return (
      <SearchItem
        key={`${index}-${item.document.id}`}
        item={item}
        product={itemProduct}
        innerRef={innerRef}
        onClick={() => {
          otelTelemetry.send('search.result.clicked', {
            query,
            url: item.document.url,
            total_results: results.length.toString(),
            index: index.toString(),
            search_engine: mode,
          });
        }}
      />
    );
  };

  const showLoadMore = (groupKey: string, currentCount: number = 0) => {
    const groupFacet = facets.find((facet) => facet.field === groupField);
    let needLoadMore = false;
    if (groupFacet) {
      const groupValue = groupFacet.values.find((value) => {
        if (typeof value === 'object') {
          return value.value === groupKey;
        } else return false;
      }) as SearchFacetCount;
      needLoadMore = groupValue ? groupValue.count > currentCount : false;
    }
    return needLoadMore;
  };

  const showResults = !!((filter && filter.length) || query);
  const showSearchFilterButton = advancedSearch && mode === 'search';
  const showAiSearchButton = askAi && mode === 'search';
  const showAiSearchItem = showAiSearchButton && query;
  const showHeaderButtons = showSearchFilterButton || showAiSearchButton;

  return (
    <SearchOverlay
      data-component-name="Search/SearchDialog"
      ref={modalRef}
      onClick={handleOverlayClick}
      className={concatClassNames('overlay', className)}
    >
      <SearchDialogWrapper className="scroll-lock" role="dialog">
        <SearchDialogHeader>
          {product && (
            <SearchProductTag color="product">
              {product.name}
              <CloseIcon onClick={() => setProduct(undefined)} color="--icon-color-additional" />
            </SearchProductTag>
          )}
          {mode === 'search' ? (
            <>
              <SearchInput
                value={query}
                onChange={setQuery}
                placeholder={translate('search.label', 'Search docs...')}
                isLoading={isSearchLoading}
                inputRef={searchInputRef}
                onSubmit={() => {
                  if (isSearchLoading) return;

                  if (showAiSearchButton && aiQueryRef.current) {
                    aiQueryRef.current.focus();
                  } else {
                    firstSearchResultRef.current?.focus();
                  }
                }}
                data-translation-key="search.label"
              />
              {showHeaderButtons && (
                <SearchHeaderButtons>
                  {showAiSearchButton ? (
                    <SearchAiButton
                      icon={<AiStarsIcon gradient />}
                      onClick={() => {
                        setMode('ai-dialog');
                        if (query.trim()) {
                          aiSearch.askQuestion(query);
                        }
                      }}
                    >
                      {translate('search.ai.button', 'Search with AI')}
                    </SearchAiButton>
                  ) : null}
                  {showSearchFilterButton && (
                    <SearchFilterToggleButton icon={<SettingsIcon />} onClick={onFilterToggle} />
                  )}
                </SearchHeaderButtons>
              )}
            </>
          ) : (
            <AiDialogHeaderWrapper>
              <Button
                variant="secondary"
                onClick={() => {
                  setMode('search');
                  aiSearch.clearConversation();
                  focusSearchInput();
                }}
                tabIndex={0}
                icon={<ChevronLeftIcon />}
              >
                {translate('search.ai.backToSearch', 'Back to search')}
              </Button>
              <Button
                variant="secondary"
                disabled={!aiSearch.conversation.length}
                onClick={() => aiSearch.clearConversation()}
                tabIndex={0}
                icon={<EditIcon />}
              >
                {translate('search.ai.newConversation', 'New conversation')}
              </Button>
            </AiDialogHeaderWrapper>
          )}
        </SearchDialogHeader>

        <SearchDialogBody>
          {mode === 'search' ? (
            <>
              {advancedSearch && isFilterOpen && (
                <SearchDialogBodyFilterView>
                  <SearchFilter
                    facets={facets}
                    filter={filter}
                    query={query}
                    quickFilterFields={[groupField]}
                    onFilterChange={onFilterChange}
                    onFilterReset={onFilterReset}
                    onFacetReset={onFacetReset}
                  />
                </SearchDialogBodyFilterView>
              )}
              <SearchDialogBodyMainView>
                <SearchGroups
                  facets={facets}
                  searchFilter={filter}
                  onFilterChange={onFilterChange}
                  onQuickFilterReset={onQuickFilterReset}
                  groupField={groupField}
                />

                {showAiSearchItem && (
                  <SearchWithAI
                    onClick={() => {
                      setMode('ai-dialog');
                      if (query.trim()) {
                        aiSearch.askQuestion(query);
                      }
                    }}
                    onKeyDown={(e) => {
                      if (e.key === 'Enter') {
                        setMode('ai-dialog');
                        if (query.trim()) {
                          aiSearch.askQuestion(query);
                        }
                      }
                    }}
                    ref={aiQueryRef}
                    tabIndex={0}
                    role="option"
                    aria-selected="true"
                  >
                    <AiStarsIcon
                      color="var(--search-ai-icon-color)"
                      size="36px"
                      background="var(--search-ai-icon-bg-color)"
                      margin="0 var(--spacing-md) 0 0"
                      borderRadius="var(--border-radius-lg)"
                    />
                    <Typography fontWeight="var(--font-weight-semibold)">{query}</Typography>
                    <Typography>- {translate('search.ai.label', 'Ask AI assistant')}</Typography>
                    <ReturnKeyIcon color="var(--search-item-text-color)" />
                  </SearchWithAI>
                )}
                {showResults ? (
                  searchKeysWithResults.length ? (
                    searchKeysWithResults.map((key, searchGroupKeyIdx) => {
                      const searchResultItems = items[key];

                      if (searchResultItems?.length) {
                        return (
                          <Fragment key={key}>
                            <SearchGroupTitle data-testid="search-group-title">
                              {key}
                            </SearchGroupTitle>
                            {searchResultItems.map((item, idx, resultsArr) =>
                              mapItem(
                                item,
                                idx,
                                resultsArr,
                                searchGroupKeyIdx === 0 ? firstSearchResultRef : undefined,
                              ),
                            )}
                            {showLoadMore(key, searchResultItems.length || 0) && (
                              <SearchGroupFooter
                                tabIndex={0}
                                data-translation-key="search.showMore"
                                onKeyDown={(e) => {
                                  if (e.key === 'Enter') {
                                    setLoadMore({
                                      groupKey: key,
                                      offset: searchResultItems.length || 0,
                                    });
                                  }
                                }}
                                onClick={() =>
                                  setLoadMore({
                                    groupKey: key,
                                    offset: searchResultItems.length || 0,
                                  })
                                }
                              >
                                {translate('search.showMore', 'Show more')}
                              </SearchGroupFooter>
                            )}
                          </Fragment>
                        );
                      }

                      return null;
                    })
                  ) : isSearchLoading ? (
                    <SearchMessage data-translation-key="search.loading">
                      <SpinnerLoader size="26px" color="var(--search-input-icon-color)" />
                      {translate('search.loading', 'Loading...')}
                    </SearchMessage>
                  ) : (
                    <SearchMessage data-translation-key="search.noResults">
                      <b>{translate('search.noResults.title', 'No results')}</b>
                    </SearchMessage>
                  )
                ) : (
                  <>
                    <SearchRecent
                      onSelect={(query) => {
                        setQuery(query);
                        focusSearchInput();
                      }}
                    />
                    <SearchSuggestedPages />
                  </>
                )}
              </SearchDialogBodyMainView>
            </>
          ) : (
            <SearchAiDialog
              initialMessage={query}
              response={aiSearch.response}
              isGeneratingResponse={aiSearch.isGeneratingResponse}
              error={aiSearch.error}
              resources={aiSearch.resources}
              conversation={aiSearch.conversation}
              setConversation={aiSearch.setConversation}
              onMessageSent={aiSearch.askQuestion}
            />
          )}
        </SearchDialogBody>
        <SearchDialogFooter>
          {mode === 'ai-dialog' ? (
            <AiDisclaimer>
              {translate(
                'search.ai.disclaimer',
                'AI search might provide incomplete or incorrect results. Verify important information.',
              )}
            </AiDisclaimer>
          ) : (
            <>
              <SearchShortcuts>
                <SearchShortcut
                  data-translation-key="search.keys.navigate"
                  combination="Tab"
                  text={translate('search.keys.navigate', 'to navigate')}
                />
                <SearchShortcut
                  data-translation-key="search.keys.select"
                  combination="⏎"
                  text={translate('search.keys.select', 'to select')}
                />
                <SearchShortcut
                  data-translation-key="search.keys.exit"
                  combination="Esc"
                  text={translate('search.keys.exit', 'to exit')}
                />
              </SearchShortcuts>
              {isSearchLoading && (
                <SearchLoading>
                  <SpinnerLoader size="16px" color="var(--search-input-icon-color)" />
                  {translate('search.loading', 'Loading...')}
                </SearchLoading>
              )}
              <SearchCancelButton
                data-translation-key="search.cancel"
                variant="secondary"
                size="small"
                onClick={onClose}
              >
                {translate('search.cancel', 'Cancel')}
              </SearchCancelButton>
            </>
          )}
        </SearchDialogFooter>
      </SearchDialogWrapper>
    </SearchOverlay>
  );
}

const SearchOverlay = styled.div`
  position: fixed;
  display: flex;
  align-items: center;
  justify-content: center;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background: var(--bg-color-modal-overlay);
  z-index: var(--z-index-overlay);

  @media screen and (max-width: ${breakpoints.small}) {
    align-items: start;
    position: fixed;
    overflow: hidden;
    overscroll-behavior: none;
  }
`;

const SearchDialogWrapper = styled.div`
  display: flex;
  flex-direction: column;
  overflow: auto;
  width: 100vw;
  height: 100vh;
  background: var(--search-modal-bg-color);
  box-shadow: var(--search-modal-box-shadow);
  border-radius: 0;

  @media screen and (max-width: ${breakpoints.small}) {
    min-height: -webkit-fill-available !important;
    min-height: 100dvh !important;
    height: auto !important;
    width: 100vw !important;
  }

  @media screen and (min-width: ${breakpoints.small}) {
    border-radius: var(--search-modal-border-radius);
    width: var(--search-modal-width);
    min-height: var(--search-modal-min-height);
    min-width: var(--search-modal-min-width);
    max-width: 95vw;
    max-height: 95vh;
    height: var(--search-modal-min-height);
    resize: both;
  }
`;

const SearchDialogHeader = styled.header`
  display: flex;
  align-items: center;
  border-bottom: var(--search-modal-border);
  background-color: var(--search-modal-header-bg-color);
  padding: var(--search-modal-header-padding);
`;

const AiDialogHeaderWrapper = styled.div`
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
`;

const SearchDialogBody = styled.div`
  display: flex;
  flex-direction: row-reverse;
  flex: 1;
  min-height: 0;
  overflow: hidden;

  @media screen and (max-width: ${breakpoints.small}) {
    min-height: 0;
  }
`;

const SearchDialogBodyMainView = styled.div`
  flex: 2;
  flex-grow: 2;
  overflow-y: scroll;
  overscroll-behavior: contain;
  border-right: var(--search-modal-border);
`;

const SearchDialogBodyFilterView = styled.div`
  overflow: scroll;
`;

const SearchDialogFooter = styled.footer`
  display: flex;
  gap: var(--search-modal-footer-gap);
  padding: var(--search-modal-footer-padding);
  border-top: var(--search-modal-border);
`;

const SearchShortcuts = styled.div`
  display: none;
  justify-content: flex-start;
  align-items: center;
  gap: var(--search-shortcuts-gap);

  @media screen and (min-width: ${breakpoints.small}) {
    display: flex;
  }
`;

const SearchMessage = styled.div`
  display: flex;
  height: 40%;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  font-size: var(--search-message-font-size);
  font-weight: var(--search-message-font-weight);
  line-height: var(--search-message-line-height);
  color: var(--search-message-text-color);
  gap: var(--search-message-gap);
`;

const SearchProductTag = styled(Tag)`
  --tag-border-radius: var(--border-radius);
  border: none;
  margin: var(--spacing-xs) var(--spacing-sm) !important;
`;

const SearchFilterToggleButton = styled(Button)`
  margin-left: 0;
`;

const SearchAiButton = styled(Button)`
  margin-left: 0;
`;

const SearchCancelButton = styled(Button)`
  width: 100%;

  @media screen and (min-width: ${breakpoints.small}) {
    display: none;
  }
`;

const SearchGroupTitle = styled.div`
  border-bottom: var(--search-modal-border);
  padding: var(--search-group-title-padding);
  background-color: var(--search-group-title-bg-color);
`;

const SearchGroupFooter = styled.div`
  display: flex;
  justify-content: center;
  padding: var(--search-group-footer-padding);
  color: var(--search-group-footer-text-color);
  cursor: pointer;
`;

const SearchLoading = styled.div`
  display: none;
  align-items: center;
  gap: var(--spacing-xs);

  @media screen and (min-width: ${breakpoints.small}) {
    display: flex;
  }
`;

const SearchHeaderButtons = styled.div`
  display: flex;
  gap: var(--search-header-buttons-gap);
  padding-left: var(--search-header-buttons-padding-left);
  border-left: var(--search-header-buttons-border-left);
`;

const AiDisclaimer = styled.div`
  font-size: var(--search-ai-disclaimer-font-size);
  line-height: var(--search-ai-disclaimer-line-height);
  color: var(--search-ai-disclaimer-text-color);
  margin: 0 auto;
`;

const SearchWithAI = styled.div`
  display: flex;
  justify-content: flex-start;
  align-items: center;
  cursor: pointer;
  gap: var(--spacing-unit);
  padding: var(--spacing-md);

  color: var(--search-item-text-color);
  background-color: var(--search-item-bg-color);
  text-decoration: none;
  white-space: normal;
  outline: none;
  border-top: 1px solid var(--search-item-border-color);
  border-bottom: 1px solid var(--search-item-border-color);

  transition: all 0.3s ease;

  ${ReturnKeyIcon} {
    opacity: 0;
  }

  &:focus,
  &:hover {
    color: var(--search-item-text-color-hover);
    background-color: var(--search-item-bg-color-hover);

    ${ReturnKeyIcon} {
      opacity: 1;
    }
  }

  &:focus {
    border-top: 1px solid var(--search-item-border-color-focused);
    border-bottom: 1px solid var(--search-item-border-color-focused);
  }

  & > :first-child {
    margin-right: var(--spacing-xs);
  }
`;
