import { MenuItem, MenuGroup, Popover, NavigableMenu } from '@wordpress/components';
import { useMemo, useState, useEffect } from '@wordpress/element';
import {
  hideLoadingMessage,
  showLoadingMessage,
  showNotice,
  ai,
  removeWrappingQuotes,
  showGrammarCheckModal,
  useGrammarCheckModal,
  resetGrammarCheckModal,
  showChoiceModal,
} from '@/utils';
import { showGenerateContentModal } from '@/utils/generateContentModal';
import { streamIntoBlock, regenerateBlock } from './streamIntoBlock';
import { useGenerationParams } from '@/utils/generateContentModal/paramsStore';
import { BlockEditProps } from '@/types';
import { useSettings } from '@/settings';
import { insert, toHTMLString, slice, create } from '@wordpress/rich-text';
import { useSelect, select, dispatch } from '@wordpress/data';
import { ToolbarButton } from '@/components/toolbarButton';
import { __, sprintf } from '@wordpress/i18n';
import { usePrompts } from '@/utils/ai/prompts/usePrompts';
import { useService } from '@/utils/ai/services/useService';

type RegenerateExtraProps = {
  text: string;
  feature: string;
  service?: {
    slug: string;
  };
  prompt: string;
};

type Regenerate = (options: string[], extraProps: RegenerateExtraProps) => Promise<void>;

const tones = [
  {
    key: 'professional',
    label: __('Professional', 'filter-ai'),
  },
  {
    key: 'informal',
    label: __('Informal', 'filter-ai'),
  },
  { key: 'humorous', label: __('Humorous', 'filter-ai') },
  { key: 'helpful', label: __('Helpful', 'filter-ai') },
];

type PromptKey =
  | 'customise_text_rewrite_prompt'
  | 'customise_text_expand_prompt'
  | 'customise_text_condense_prompt'
  | 'customise_text_summarise_prompt'
  | 'customise_text_check_grammar_prompt'
  | 'customise_text_change_tone_prompt';

type OnClick = (promptKey: string, params?: Record<string, string>) => Promise<void>;

export const TextToolbar = ({ attributes, setAttributes, name, clientId }: BlockEditProps) => {
  const [changeToneAnchor, setChangeToneAnchor] = useState<HTMLButtonElement | null>(null);
  const [showChangeToneOptions, setShowChangeToneOptions] = useState(false);

  const { settings } = useSettings();

  const grammarModal = useGrammarCheckModal();

  const rewritePrompt = usePrompts('customise_text_rewrite_prompt');
  const rewritePromptService = useService('customise_text_rewrite_prompt_service');

  const expandPrompt = usePrompts('customise_text_expand_prompt');
  const expandPromptService = useService('customise_text_expand_prompt_service');

  const condensePrompt = usePrompts('customise_text_condense_prompt');
  const condensePromptService = useService('customise_text_condense_prompt_service');

  const summarisePrompt = usePrompts('customise_text_summarise_prompt');
  const summarisePromptService = useService('customise_text_summarise_prompt_service');

  const checkGrammarPrompt = usePrompts('customise_text_check_grammar_prompt');
  const checkGrammarPromptService = useService('customise_text_check_grammar_service');

  const changeTonePrompt = usePrompts('customise_text_change_tone_prompt');
  const changeTonePromptService = useService('customise_text_change_tone_prompt_service');

  const generateContentService = useService('generate_content_prompt_service');
  const regenerateParams = useGenerationParams(clientId);

  const promptConfigs = useMemo(
    () => ({
      customise_text_rewrite_prompt: {
        prompt: rewritePrompt,
        service: rewritePromptService,
      },
      customise_text_expand_prompt: {
        prompt: expandPrompt,
        service: expandPromptService,
      },
      customise_text_condense_prompt: {
        prompt: condensePrompt,
        service: condensePromptService,
      },
      customise_text_summarise_prompt: {
        prompt: summarisePrompt,
        service: summarisePromptService,
      },
      customise_text_check_grammar_prompt: {
        prompt: checkGrammarPrompt,
        service: checkGrammarPromptService,
      },
      customise_text_change_tone_prompt: {
        prompt: changeTonePrompt,
        service: changeTonePromptService,
      },
    }),
    [
      rewritePrompt,
      expandPrompt,
      condensePrompt,
      summarisePrompt,
      changeTonePrompt,
      rewritePromptService,
      expandPromptService,
      condensePromptService,
      summarisePromptService,
      checkGrammarPromptService,
      changeTonePromptService,
      settings,
    ]
  );

  const { selectionStart, selectionEnd, hasMultiSelection } = useSelect(
    (select) => {
      const { getSelectionStart, getSelectionEnd, hasMultiSelection } = select('core/block-editor');

      // @ts-expect-error Type 'never' has no call signatures
      const _selectionStart = getSelectionStart();

      // @ts-expect-error Type 'never' has no call signatures
      const _selectionEnd = getSelectionEnd();

      // @ts-expect-error Type 'never' has no call signatures
      const _hasMultiSelection = hasMultiSelection();

      return {
        selectionStart: _selectionStart,
        selectionEnd: _selectionEnd,
        hasMultiSelection: _hasMultiSelection,
      };
    },
    [attributes.content]
  );

  const hasSelection = useMemo(() => {
    return selectionStart.clientId === selectionEnd.clientId && selectionStart.offset !== selectionEnd.offset;
  }, [selectionStart, selectionEnd]);

  const label = useMemo(() => {
    switch (name) {
      case 'core/heading':
        return __('Heading', 'filter-ai');
      case 'core/list-item':
        return __('List Item', 'filter-ai');
      default:
        return __('Text', 'filter-ai');
    }
  }, [name]);

  const update = async (newText: string, promptKey: string) => {
    try {
      if (promptKey === 'customise_text_summarise_prompt') {
        await navigator.clipboard.writeText(newText);

        showNotice({ message: __('Summary has been copied to your clipboard', 'filter-ai') });
      } else {
        if (hasSelection) {
          const content =
            typeof attributes.content === 'string' ? create({ text: attributes.content }) : attributes.content;

          const newValue = insert(content, newText, selectionStart.offset, selectionEnd.offset);

          setAttributes({ content: toHTMLString({ value: newValue }) });

          setTimeout(() => document.getSelection()?.empty(), 0);
        } else {
          setAttributes({ content: newText });
        }

        let message: string = sprintf(__('Your %s has been updated', 'filter-ai'), label.toLowerCase());

        const { service } = promptConfigs[promptKey as PromptKey] || {};

        if (service?.metadata.name) {
          message = sprintf(
            __('Your %s has been updated using %s', 'filter-ai'),
            label.toLowerCase(),
            service.metadata.name
          );
        }

        showNotice({ message });
      }
    } catch (error) {
      console.error(error);

      // @ts-expect-error Property 'message' does not exist on type '{}'
      showNotice({ message: error?.message || error, type: 'error' });
    }
  };

  const regenerate: Regenerate = async (options: string[], { text, feature, prompt, service }) => {
    try {
      const oldOptions = [text, ...options];

      const response = await ai.customiseText(feature, text, oldOptions.join(', '), prompt, service?.slug);

      if (!response) {
        throw new Error(
          sprintf(__('Sorry, there has been an issue while generating your %s', 'filter-ai'), label.toLowerCase())
        );
      }

      const newOptions = JSON.parse(response);

      showChoiceModal({
        options: newOptions,
        choice: '',
      });
    } catch (error) {
      console.error(error);

      // @ts-expect-error Property 'message' does not exist on type '{}'
      showNotice({ message: error?.message || error, type: 'error' });
    }
  };

  const onClick: OnClick = async (promptKey, params) => {
    const isValidPromptKey = (key: string): key is PromptKey => {
      return key in promptConfigs;
    };

    if (!isValidPromptKey(promptKey)) {
      console.error(`Invalid prompt key: ${promptKey}`);
      return;
    }

    const { prompt, service } = promptConfigs[promptKey as PromptKey] || {};

    if (promptKey === 'customise_text_summarise_prompt') {
      showLoadingMessage(label, 'summarising');
    } else if (promptKey === 'customise_text_check_grammar_prompt') {
      showLoadingMessage(label, 'checking grammar');
    } else {
      showLoadingMessage(label, 'customising');
    }

    try {
      const feature = `filter-ai-${promptKey.replace(/_/g, '-')}`;

      const content =
        typeof attributes.content === 'string' ? create({ text: attributes.content }) : attributes.content;

      if (!content) {
        throw new Error(__('Please provide some text', 'filter-ai'));
      }

      const text = toHTMLString({
        value: hasSelection ? slice(content, selectionStart.offset, selectionEnd.offset) : content,
      });

      let finalPrompt = prompt;

      if (typeof prompt !== 'string') {
        throw new Error(
          __(
            "There was an error preparing your prompt. Please make sure that in plugin 'Settings' your prompt is a valid string.",
            'filter-ai'
          )
        );
      }

      if (params) {
        for (const key in params) {
          finalPrompt = finalPrompt.replace(new RegExp(`{{${key}}}`, 'g'), params[key]);
        }
      }

      if (promptKey === 'customise_text_check_grammar_prompt') {
        const correctedText = await ai.fixTextGrammar(text, finalPrompt, service?.slug);

        if (!correctedText) {
          throw new Error(sprintf(__('Sorry, there has been an issue while checking grammar', 'filter-ai')));
        }

        showGrammarCheckModal({
          originalText: text,
          correctedText: removeWrappingQuotes(correctedText),
          context: {
            content: attributes.content,
            hasSelection,
            selectionStart,
            selectionEnd,
            serviceName: service?.metadata.name,
          },
        });

        return;
      }

      const response = await ai.customiseText(feature, text, '', finalPrompt, service?.slug);

      if (!response) {
        throw new Error(
          sprintf(__('Sorry, there has been an issue while generating your %s', 'filter-ai'), label.toLowerCase())
        );
      }

      const options = JSON.parse(response);

      showChoiceModal({
        title: __('Select Your AI Generated Text', 'filter-ai'),
        description: __('Choose the version that works best for your content', 'filter-ai'),
        label: __('Choose from these AI generated options:', 'filter-ai'),
        update: (newValue) => update(newValue, promptKey),
        regenerate: (options) =>
          regenerate(options, {
            prompt: finalPrompt,
            text,
            service,
            feature,
          }),
        options,
      });
    } catch (error) {
      console.error(error);

      // @ts-expect-error Property 'message' does not exist on type '{}'
      showNotice({ message: error?.message || error, type: 'error' });
    } finally {
      hideLoadingMessage();
    }
  };

  useEffect(() => {
    const { choice, context } = grammarModal;

    if (choice && context) {
      const { content, hasSelection, selectionStart, selectionEnd, serviceName } = context;

      try {
        const blockEditor = select('core/block-editor');
        const blockDispatcher = dispatch('core/block-editor') as {
          updateBlockAttributes: (clientId: string, attributes: Record<string, any>) => void;
        };

        const startId = selectionStart?.clientId;
        const endId = selectionEnd?.clientId;
        const sameBlock = startId && endId && startId === endId;
        const targetBlockId = sameBlock ? startId : endId;
        const targetBlock = blockEditor.getBlock(targetBlockId);

        if (targetBlock) {
          if (Object.prototype.hasOwnProperty.call(targetBlock.attributes, 'content')) {
            if (hasSelection) {
              const richContent = typeof content === 'string' ? create({ text: content }) : content;
              const newValue = insert(richContent, choice, selectionStart.offset, selectionEnd.offset);

              blockDispatcher.updateBlockAttributes(targetBlockId, {
                content: newValue.text ? newValue.text : toHTMLString({ value: newValue }),
              });

              setTimeout(() => document.getSelection()?.empty(), 0);
            } else {
              blockDispatcher.updateBlockAttributes(targetBlockId, {
                content: choice,
              });
            }
          }
        }

        let message;

        if (serviceName) {
          message = sprintf(__('Grammar has been corrected using %s', 'filter-ai'), serviceName);
        } else {
          message = __('Grammar has been corrected', 'filter-ai');
        }

        showNotice({ message });
      } catch (error) {
        console.error('Error applying grammar correction:', error);
        showNotice({
          message: __('There was an issue applying the grammar correction.', 'filter-ai'),
          type: 'error',
        });
      } finally {
        resetGrammarCheckModal();
      }
    }
  }, [grammarModal.choice, grammarModal.context]);

  const onGenerateFromPrompt = () => {
    if (!clientId) return;
    showGenerateContentModal({
      blockName: name,
      onSubmit: ({ prompt, keywords, length, append }) => {
        streamIntoBlock({
          clientId,
          blockName: name,
          prompt,
          keywords,
          length,
          service: generateContentService?.slug,
          append,
        });
      },
    });
  };

  if (
    hasMultiSelection ||
    ![
      settings?.generate_content_enabled,
      settings?.customise_text_rewrite_enabled,
      settings?.customise_text_expand_enabled,
      settings?.customise_text_condense_enabled,
      settings?.customise_text_summarise_enabled,
      settings?.customise_text_check_grammar_prompt,
      settings?.customise_text_change_tone_enabled,
    ].some((setting) => !!setting)
  ) {
    return null;
  }

  return (
    <ToolbarButton>
      {({ onClose }) => (
        <>
          {settings?.generate_content_enabled && (
            <MenuGroup>
              <MenuItem
                onClick={() => {
                  onClose();
                  onGenerateFromPrompt();
                }}
              >
                {__('Generate From Prompt', 'filter-ai')}
              </MenuItem>
              {regenerateParams && (
                <MenuItem
                  onClick={() => {
                    onClose();
                    regenerateBlock(clientId);
                  }}
                >
                  {__('Regenerate', 'filter-ai')}
                </MenuItem>
              )}
            </MenuGroup>
          )}
          {settings?.customise_text_rewrite_enabled && (
            <MenuItem
              onClick={() => {
                onClose();
                onClick('customise_text_rewrite_prompt', { type: label.toLowerCase() });
              }}
            >
              {__('Rewrite', 'filter-ai')}
            </MenuItem>
          )}
          {settings?.customise_text_expand_enabled && (
            <MenuItem
              onClick={() => {
                onClose();
                onClick('customise_text_expand_prompt', { type: label.toLowerCase() });
              }}
            >
              {__('Expand', 'filter-ai')}
            </MenuItem>
          )}
          {settings?.customise_text_condense_enabled && (
            <MenuItem
              onClick={() => {
                onClose();
                onClick('customise_text_condense_prompt', { type: label.toLowerCase() });
              }}
            >
              {__('Condense', 'filter-ai')}
            </MenuItem>
          )}
          {settings?.customise_text_summarise_enabled && (
            <MenuItem
              onClick={() => {
                onClose();
                onClick('customise_text_summarise_prompt', { type: label.toLowerCase() });
              }}
            >
              {__('Summarise', 'filter-ai')}
            </MenuItem>
          )}
          {settings?.customise_text_check_grammar_enabled && (
            <MenuItem
              onClick={() => {
                onClose();
                onClick('customise_text_check_grammar_prompt', { type: label.toLowerCase() });
              }}
            >
              {__('Check Grammar', 'filter-ai')}
            </MenuItem>
          )}
          {settings?.customise_text_change_tone_enabled && (
            <>
              <MenuItem
                ref={setChangeToneAnchor}
                onClick={() => {
                  setShowChangeToneOptions(true);
                }}
                isSelected={showChangeToneOptions}
                // @ts-expect-error Type 'string' is not assignable to type 'ReactElement<any, any>'
                icon="arrow-right-alt2"
                iconPosition="right"
              >
                {__('Change Tone', 'filter-ai')}
              </MenuItem>
              {showChangeToneOptions && (
                <Popover
                  className="components-dropdown__content filter-ai-components-popover"
                  anchor={changeToneAnchor}
                  placement="right-start"
                  expandOnMobile
                  onClose={() => setShowChangeToneOptions(false)}
                >
                  <NavigableMenu role="menu">
                    {tones.map((tone) => (
                      <MenuItem
                        key={tone.key}
                        onClick={() => {
                          onClose();
                          onClick('customise_text_change_tone_prompt', {
                            tone: tone.label.toLowerCase(),
                            type: label.toLowerCase(),
                          });
                        }}
                      >
                        {tone.label}
                      </MenuItem>
                    ))}
                  </NavigableMenu>
                </Popover>
              )}
            </>
          )}
        </>
      )}
    </ToolbarButton>
  );
};
