import React, { useCallback } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { v4 as uuidv4 } from 'uuid';
import { GetContextMenuItems, MenuItemDef } from 'ag-grid-community';
import { MediaPlanMode, WithConfirmPayload } from 'media-plan/types';
import { removeToasts } from 'common/actions';
import { IS_MAC_OS } from 'common/constants';
import {
  getColorsFromClipboard,
  getInitialCellData,
  getValuesFromClipboard,
  pasteDataIntoSingleCell,
  isLastPhase,
  isMultipleColumnsSelected,
  isMultipleRowsSelected,
  isReadOnlyMode,
  mergeClipboardData,
  sendToClipboard,
  updateCellsColor,
} from '../transducers';
import { MediaPlanColumnId, SpreadsheetContext, SpreadsheetRowData, UndoHook } from '../types';
import { useCreatePhase } from './use-create-phase';
import { useDeletePhase } from './use-delete-phase';
import { useCreateCampaign } from './use-create-campaign';
import { useDeleteCampaign } from './use-delete-campaign';
import { useMediaPlanAccessMode } from 'media-plan/hooks';
import { mediaPlanModeSelector, spreadsheetValidationModelSelector } from 'media-plan/selectors';
import { CELL_BACKGROUND_COLORS } from '../constants';
import { renderToString } from 'react-dom/server';
import { ContextMenuColor } from '../components/context-menu-color';

export const useContextMenu = (
  projectId: number,
  mediaPlanId: number,
  withConfirm: (payload: WithConfirmPayload) => void,
  resetCopiedData: () => void,
  undo: UndoHook,
  context: SpreadsheetContext
) => {
  const dispatch = useDispatch();

  const createPhase = useCreatePhase(projectId, mediaPlanId);
  const deletePhase = useDeletePhase(projectId, mediaPlanId);
  const createCampaign = useCreateCampaign();
  const deleteCampaign = useDeleteCampaign(undo.sanitizeUndoRedoStacks);
  const spreadsheetValidationModel = useSelector(spreadsheetValidationModelSelector);
  const mediaPlanMode = useSelector(mediaPlanModeSelector);
  const accessMode = useMediaPlanAccessMode(projectId, mediaPlanMode);

  const getContextMenuItems = useCallback<GetContextMenuItems<SpreadsheetRowData>>(
    ({ node, column, api, columnApi }) => {
      if (!node && !column) return [];

      const isFooter = node?.footer;
      const isPhase = !!node?.group && !isFooter;
      const isAccessModeReadOnly = isReadOnlyMode(accessMode);
      const isMultipleRows = isMultipleRowsSelected(api);
      const focusedCell = api.getFocusedCell();
      const focusedRow = focusedCell && api.getDisplayedRowAtIndex(focusedCell.rowIndex);
      const isMultipleColumns = isMultipleColumnsSelected(api);
      const isApproverMode = mediaPlanMode === MediaPlanMode.APPROVAL;

      const isCellActionGroupVisible = !isPhase && !isFooter && !isAccessModeReadOnly && !isMultipleRows;
      const isRowActionsGroupVisible =
        !isFooter && !isAccessModeReadOnly && !isApproverMode && !(!isPhase && isMultipleRows);
      const isPhaseActionsGroupVisible = !isFooter && !isAccessModeReadOnly && !isApproverMode && !isMultipleRows;

      const isCellEditable = !!node && !!focusedCell?.column.isCellEditable(node);
      const isStatusCopyPasteHidden = isApproverMode && column?.getColId() === MediaPlanColumnId.STATUS;

      const addOption = (option: string | MenuItemDef, isVisible: boolean) => (isVisible ? option : '');

      if (column?.getColId() === MediaPlanColumnId.ACTIONS) {
        api.clearRangeSelection();
        api.addCellRange({
          rowStartIndex: node && node.rowIndex,
          rowEndIndex: node && node.rowIndex,
          columnStart: MediaPlanColumnId.STATUS,
          columnEnd: MediaPlanColumnId.NAMING_CONVENTION,
        });
      }

      dispatch(removeToasts());

      if (isFooter && column?.getColId() === MediaPlanColumnId.ACTIONS) return [];

      return [
        addOption(
          {
            name: 'Undo',
            shortcut: IS_MAC_OS ? '⌘ Z' : 'Ctrl Z',
            action: () => undo.applyUndo(api),
          },
          !isAccessModeReadOnly && !undo.isUndoStackEmpty
        ),
        addOption(
          {
            name: 'Redo',
            shortcut: IS_MAC_OS ? '⌘ shift Z' : 'Ctrl Shift Z',
            action: () => undo.applyRedo(api),
          },
          !isAccessModeReadOnly && !undo.isRedoStackEmpty
        ),
        addOption(
          {
            name: 'Cell Background Color',
            subMenu: CELL_BACKGROUND_COLORS.map(color => ({
              name: renderToString(<ContextMenuColor color={color} />),
              action: () => updateCellsColor(api, undo, color === 'transparent' ? null : color),
              cssClasses: ['color-menu-option'],
            })),
          },
          !isAccessModeReadOnly && !isPhase && !isFooter
        ),
        addOption(
          { name: 'Cell Actions', cssClasses: ['menu-options-group', 'menu-options-group-no-offset'], disabled: true },
          !isFooter && !isPhase && !isStatusCopyPasteHidden
        ),
        addOption(
          {
            name: 'Copy',
            cssClasses: ['data-selector-context-menu-copy'],
            shortcut: IS_MAC_OS ? '⌘ C' : 'Ctrl C',
            action: () => {
              if (isFooter && focusedRow) {
                const valueToCopy = focusedRow.aggData[focusedCell.column.getColId()] ?? getInitialCellData(undefined);
                sendToClipboard(new Map([[uuidv4('footer'), [valueToCopy]]]), resetCopiedData)();
              } else {
                api.copySelectedRangeToClipboard();
              }
            },
          },
          !isPhase && !isStatusCopyPasteHidden
        ),
        addOption(
          {
            name: 'Paste',
            cssClasses: ['data-selector-context-menu-paste'],
            shortcut: IS_MAC_OS ? '⌘ V' : 'Ctrl V',
            action: () => {
              if (column?.getColId() === MediaPlanColumnId.ACTIONS && node?.rowIndex != null) {
                api.clearFocusedCell();
                api.setFocusedCell(node.rowIndex, MediaPlanColumnId.STATUS);
              }

              navigator.clipboard.read().then(async ([clipboardItem]) => {
                if (spreadsheetValidationModel && clipboardItem) {
                  const [valuesResult, colorsResult] = await Promise.allSettled([
                    clipboardItem.getType('text/plain'),
                    clipboardItem.getType('text/html'),
                  ]);

                  try {
                    const valuesString = valuesResult.status === 'fulfilled' ? await valuesResult.value.text() : '';
                    const colorsHTMLString = colorsResult.status === 'fulfilled' ? await colorsResult.value.text() : '';

                    const clipboardData = mergeClipboardData(
                      getValuesFromClipboard(valuesString),
                      getColorsFromClipboard(colorsHTMLString)
                    );

                    pasteDataIntoSingleCell(api, columnApi, spreadsheetValidationModel, undo, context)(clipboardData);
                  } catch {
                    throw 'Cannot parse data in buffer';
                  }
                }
              });
            },
          },
          isCellActionGroupVisible && !isMultipleColumns && isCellEditable && !isStatusCopyPasteHidden
        ),
        addOption(
          {
            name: 'Row Actions',
            cssClasses: ['menu-options-group', !isCellActionGroupVisible ? 'menu-options-group-no-offset' : ''],
            disabled: true,
          },
          isRowActionsGroupVisible
        ),
        addOption(
          {
            name: 'Insert New Row Above',
            cssClasses: ['data-selector-context-menu-insert-new-row-above'],
            action: () => {
              createCampaign(api, columnApi, node, true);
            },
          },
          isRowActionsGroupVisible && node?.rowIndex !== 0
        ),
        addOption(
          {
            name: 'Insert New Row Below',
            cssClasses: ['data-selector-context-menu-insert-new-row-below'],
            shortcut: IS_MAC_OS ? '⌘ ⌥ =' : 'Ctrl Alt =',
            action: () => {
              createCampaign(api, columnApi, node);
            },
          },
          isRowActionsGroupVisible
        ),
        addOption(
          {
            name: 'Delete Row',
            cssClasses: ['data-selector-context-menu-delete-row'],
            shortcut: IS_MAC_OS ? '⌘ ⌥ -' : 'Ctrl Alt -',
            action: () =>
              withConfirm({
                text: 'Deleting campaign cannot be undone',
                action: () => {
                  deleteCampaign(api, columnApi, node);
                },
              }),
          },
          isRowActionsGroupVisible && !isPhase
        ),
        addOption(
          { name: 'Phase Actions', cssClasses: ['menu-options-group'], disabled: true },
          isPhaseActionsGroupVisible
        ),
        addOption(
          {
            name: 'Insert New Phase Above',
            cssClasses: ['data-selector-context-menu-insert-new-phase-above'],
            action: () => {
              createPhase(api, node, true);
            },
          },
          isPhaseActionsGroupVisible
        ),
        addOption(
          {
            name: 'Insert New Phase Below',
            cssClasses: ['data-selector-context-menu-insert-new-phase-below'],
            action: () => {
              createPhase(api, node);
            },
          },
          isPhaseActionsGroupVisible
        ),
        addOption(
          {
            name: 'Delete Phase',
            cssClasses: ['data-selector-context-menu-delete-phase'],
            shortcut: IS_MAC_OS ? '⌘ ⌥ -' : 'Ctrl Alt -',
            action: () =>
              withConfirm({
                text: 'Deleting the phase will also delete all of its campaigns.',
                action: () => {
                  deletePhase(api, node);
                },
              }),
          },
          isPhase && isPhaseActionsGroupVisible && !isLastPhase(api)
        ),
      ];
    },
    [
      accessMode,
      context,
      createCampaign,
      createPhase,
      deleteCampaign,
      deletePhase,
      dispatch,
      mediaPlanMode,
      resetCopiedData,
      spreadsheetValidationModel,
      undo,
      withConfirm,
    ]
  );

  return { getContextMenuItems };
};
