import React, { useMemo, useEffect, useState } from 'react';
import { useWindowVirtualizer } from '@tanstack/react-virtual';
import styled from 'styled-components';

import { CatalogItem, ResolvedFilter } from '@redocly/theme/core/types';
import { useElementSize } from '@redocly/theme/core/hooks';
import { CatalogClassicCard } from '@redocly/theme/components/CatalogClassic/CatalogClassicCard';
import { CounterTag } from '@redocly/theme/components/Tags/CounterTag';
import { SpinnerLoader } from '@redocly/theme/components/Loaders/SpinnerLoader';

const GAP_SIZE = 32;
const ESTIMATED_HEADER_HEIGHT = 43;
const ESTIMATED_CARD_HEIGHT = 194 + GAP_SIZE;
const CARD_MIN_WIDTH_VAR = '--catalog-classic-card-min-width';
const VIRTUALIZATION_THRESHOLD = 20; // Don't virtualize below this number of items

export type Group = {
  title: string;
  items: CatalogItem[];
};

export type VirtualRowData =
  | { type: 'header'; groupTitle: string; groupCount: number; key: string }
  | { type: 'cardRow'; groupTitle: string; items: CatalogItem[]; key: string };

export type CatalogClassicVirtualizedGroupsProps = {
  groups: Group[];
  filters: (ResolvedFilter & { isFilterUsed?: boolean })[];
  filterTerm: string;
};

export function CatalogClassicVirtualizedGroups({
  groups,
  filters,
  filterTerm,
}: CatalogClassicVirtualizedGroupsProps) {
  const [isClient, setIsClient] = useState(false);
  const [size, parentRef] = useElementSize<HTMLDivElement>({ delay: 50, detectSizes: 'width' });

  useEffect(() => {
    setIsClient(true);
  }, []);

  const totalItemCount = useMemo(() => {
    return groups.reduce((total, group) => total + group.items.length, 0);
  }, [groups]);

  const shouldVirtualize = totalItemCount >= VIRTUALIZATION_THRESHOLD;

  const columnCount = useMemo(() => {
    if (!size.width) return 4;
    const cardMinWidth = parseInt(
      getComputedStyle(document.documentElement).getPropertyValue(CARD_MIN_WIDTH_VAR),
      10,
    );

    return Math.max(1, Math.floor((size.width + GAP_SIZE) / (cardMinWidth + GAP_SIZE)));
  }, [size.width]);

  const flatRows: VirtualRowData[] = useMemo(() => {
    if (!shouldVirtualize || !isClient) {
      return groups.flatMap((group) => [
        {
          type: 'header',
          groupTitle: group.title,
          groupCount: group.items.length,
          key: `header-${group.title}`,
        },
        {
          type: 'cardRow',
          groupTitle: group.title,
          items: group.items,
          key: `${group.title}-cards`,
        },
      ]);
    }

    const rows: VirtualRowData[] = [];
    groups.forEach((group) => {
      rows.push({
        type: 'header',
        groupTitle: group.title,
        groupCount: group.items.length,
        key: `header-${group.title}`,
      });
      const numRows = Math.ceil(group.items.length / columnCount);
      for (let rowIndex = 0; rowIndex < numRows; rowIndex++) {
        const startIndex = rowIndex * columnCount;
        const rowItems = group.items.slice(startIndex, startIndex + columnCount);
        rows.push({
          type: 'cardRow',
          groupTitle: group.title,
          items: rowItems,
          key: `${group.title}-row-${rowIndex}`,
        });
      }
    });
    return rows;
  }, [groups, columnCount, isClient, shouldVirtualize]);

  const virtualizer = useWindowVirtualizer({
    count: flatRows.length,
    estimateSize: (index: number) => {
      const row = flatRows[index];
      if (row.type === 'header') return ESTIMATED_HEADER_HEIGHT;
      return ESTIMATED_CARD_HEIGHT;
    },
    overscan: 5,
    enabled: shouldVirtualize,
  });

  useEffect(() => {
    if (!size.width || !shouldVirtualize) {
      return;
    }

    virtualizer.measure();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [filters, filterTerm, size.width, shouldVirtualize]);

  const renderRow = (rowData: VirtualRowData) => {
    if (rowData.type === 'header') {
      return (
        <SSRHeaderRow key={rowData.key}>
          <CatalogSeparatorLabel>{rowData.groupTitle}</CatalogSeparatorLabel>
          <CounterTag borderless>{rowData.groupCount}</CounterTag>
        </SSRHeaderRow>
      );
    }

    return (
      <SSRGridRow key={rowData.key}>
        {rowData.items.map((item) => (
          <CatalogClassicCard key={item.link} item={item} />
        ))}
      </SSRGridRow>
    );
  };

  if (!isClient) {
    return (
      <div ref={parentRef} data-component-name="CatalogClassic/CatalogClassicVirtualizedGroups">
        {flatRows.slice(0, 15).map((rowData) => renderRow(rowData))}
        <LoadingWrapper>
          <SpinnerLoader color="var(--catalog-classic-description-text-color)" size="20px" />
        </LoadingWrapper>
      </div>
    );
  }

  if (!shouldVirtualize) {
    return (
      <div ref={parentRef} data-component-name="CatalogClassic/CatalogClassicVirtualizedGroups">
        {flatRows.map((rowData) => renderRow(rowData))}
      </div>
    );
  }

  return (
    <div ref={parentRef} data-component-name="CatalogClassic/CatalogClassicVirtualizedGroups">
      <div
        style={{
          position: 'relative',
          height: `${virtualizer.getTotalSize()}px`,
        }}
      >
        {virtualizer.getVirtualItems().map((virtualRow) => {
          const rowData = flatRows[virtualRow.index];
          if (rowData.type === 'header') {
            return (
              <HeaderRow
                key={rowData.key}
                ref={virtualizer.measureElement}
                data-index={virtualRow.index}
                style={{ transform: `translateY(${virtualRow.start}px)` }}
              >
                <CatalogSeparatorLabel>{rowData.groupTitle}</CatalogSeparatorLabel>
                <CounterTag borderless>{rowData.groupCount}</CounterTag>
              </HeaderRow>
            );
          }

          return (
            <GridRow
              key={rowData.key}
              ref={virtualizer.measureElement}
              data-index={virtualRow.index}
              style={{ transform: `translateY(${virtualRow.start}px)` }}
            >
              {rowData.items.map((item) => (
                <CatalogClassicCard key={item.link} item={item} />
              ))}
            </GridRow>
          );
        })}
      </div>
    </div>
  );
}

const SSRHeaderRow = styled.div`
  width: 100%;
  display: flex;
  align-items: center;
  padding: var(--catalog-classic-separator-padding);
  border-top: 1px solid var(--catalog-classic-separator-border-color);
  padding-bottom: calc(4px * 4);
  color: var(--catalog-classic-separator-color);
  font-size: var(--catalog-classic-separator-font-size);
  font-weight: var(--catalog-classic-separator-font-weight);
`;

const SSRGridRow = styled.div`
  width: 100%;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--catalog-classic-card-min-width), 1fr));
  gap: var(--catalog-classic-cards-group-gap, 32px);
  padding-bottom: var(--catalog-classic-cards-group-gap, 32px);
`;

const HeaderRow = styled(SSRHeaderRow)`
  position: absolute;
  left: 0;
  will-change: transform;
`;

const GridRow = styled(SSRGridRow)`
  position: absolute;
  left: 0;
  will-change: transform;
`;

const CatalogSeparatorLabel = styled.div`
  margin: var(--catalog-classic-separator-label-margin);
`;

const LoadingWrapper = styled.div`
  display: flex;
  justify-content: center;
  align-items: center;
`;
