import * as React from 'react';

import {
  CodeBlock as CodeBlockComponent,
  type CodeBlockProps,
} from '@redocly/theme/components/CodeBlock/CodeBlock';
import { langToName } from '@redocly/theme/core/utils';
import { useActiveCodeSnippetId } from '@redocly/theme/core/contexts';

type SnippetData = {
  name: string;
  languageName: string;
  lang: string;
  props: CodeBlockProps;
  id: string;
};

export function CodeGroup(props: React.PropsWithChildren<{ mode?: 'tabs' | 'dropdown' }>) {
  const mode = props.mode || 'tabs';
  const isTabsMode = mode === 'tabs';

  const rawSnippets = React.useMemo(
    () => parseSnippetsFromChildren(props.children),
    [props.children],
  );

  const groupId = React.useMemo(() => generateGroupId(rawSnippets, mode), [rawSnippets, mode]);

  const [activeSnippetId, setActiveSnippetId] = useActiveCodeSnippetId(groupId, rawSnippets);

  const snippets = React.useMemo(() => {
    const items = createItemsFromSnippets(rawSnippets, isTabsMode);

    const itemsProps = {
      items,
      onChange: (id: string | string[]) => setActiveSnippetId(id as string),
      value: activeSnippetId,
    };

    return Object.fromEntries(
      rawSnippets.map((snippet: SnippetData) => {
        const snippetProps = {
          ...snippet.props,
          header: {
            ...snippet.props.header,
            title: isTabsMode ? undefined : snippet.name,
          },
          ...(isTabsMode ? { tabs: itemsProps } : { dropdown: itemsProps }),
        };

        return [snippet.id, snippetProps];
      }),
    );
  }, [rawSnippets, activeSnippetId, isTabsMode, setActiveSnippetId]);

  const activeSnippet = snippets[activeSnippetId];
  if (!activeSnippet) {
    return null;
  }

  return <CodeBlockComponent {...activeSnippet} />;
}

function generateContentHash(content: string): number {
  let hash = 0;
  for (let i = 0; i < content.length; i++) {
    hash = content.charCodeAt(i) + ((hash << 5) - hash);
  }
  return Math.abs(hash);
}

// Generate unique group ID for CodeGroup instance
// Examples: "dropdown-8901234", "tabs-1234567"
function generateGroupId(rawSnippets: SnippetData[], mode: string): string {
  const content = rawSnippets.map((s) => s.id + (s.props.source || '')).join('|') + `|${mode}`;
  const hash = generateContentHash(content);

  return `${mode}-${hash}`;
}

function getTabName(props: CodeBlockProps, idx: number): string {
  const fallbackName = `Tab ${idx + 1}`;
  return String(props.header?.title || props.file || langToName(props.lang || '') || fallbackName);
}

function parseSnippetsFromChildren(children: React.ReactNode): SnippetData[] {
  return React.Children.toArray(children).map((child, idx) => {
    const childProps = child as React.ReactElement<CodeBlockProps>;
    const props = childProps.props;

    return {
      name: getTabName(props, idx),
      languageName: String(langToName(props.lang || 'Default') || ''),
      lang: props.lang || '',
      props,
      id: `${props.lang || ''}-${idx}`,
    };
  });
}

function createItemsFromSnippets(snippets: SnippetData[], isTabsMode: boolean) {
  return snippets.map((snippet) => ({
    name: isTabsMode ? snippet.name : snippet.languageName || '',
    lang: snippet.lang,
    id: snippet.id,
  }));
}
