// Copyright 2017 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only

import lodash from 'lodash';

import type { LocalizerType } from '../ts/types/I18N.std.ts';
import type {
  MenuListType,
  MenuOptionsType,
  MenuActionsType,
} from '../ts/types/menu.std.ts';
import { strictAssert } from '../ts/util/assert.std.ts';

const { isString } = lodash;

export type CreateTemplateOptionsType = MenuOptionsType & MenuActionsType;

export const createTemplate = (
  options: CreateTemplateOptionsType,
  i18n: LocalizerType
): MenuListType => {
  if (!isString(options.platform)) {
    throw new TypeError('`options.platform` must be a string');
  }

  const {
    isProduction,
    devTools,
    includeSetup,
    openContactUs,
    openForums,
    openJoinTheBeta,
    openReleaseNotes,
    openSupportPage,
    platform,
    setupAsNewDevice,
    setupAsStandalone,
    stageLocalBackupForImport,
    forceUpdate,
    showAbout,
    showDebugLog,
    showKeyboardShortcuts,
    showSettings,
    openArtCreator,
    zoomIn,
    zoomOut,
    zoomReset,
  } = options;

  const template: MenuListType = [
    {
      label: i18n('icu:mainMenuFile'),
      submenu: [
        {
          label: i18n('icu:mainMenuCreateStickers'),
          click: openArtCreator,
        },
        {
          label: i18n('icu:mainMenuSettings'),
          accelerator: 'CommandOrControl+,',
          click: showSettings,
        },
        {
          type: 'separator',
        },
        {
          role: 'quit',
          label: i18n('icu:appMenuQuit'),
        },
      ],
    },
    {
      label: i18n('icu:mainMenuEdit'),
      submenu: [
        {
          role: 'undo',
          label: i18n('icu:editMenuUndo'),
        },
        {
          role: 'redo',
          label: i18n('icu:editMenuRedo'),
        },
        {
          type: 'separator',
        },
        {
          role: 'cut',
          label: i18n('icu:editMenuCut'),
        },
        {
          role: 'copy',
          label: i18n('icu:editMenuCopy'),
        },
        {
          role: 'paste',
          label: i18n('icu:editMenuPaste'),
        },
        {
          role: 'pasteAndMatchStyle',
          label: i18n('icu:editMenuPasteAndMatchStyle'),
        },
        {
          role: 'delete',
          label: i18n('icu:editMenuDelete'),
        },
        {
          role: 'selectAll',
          label: i18n('icu:editMenuSelectAll'),
        },
      ],
    },
    {
      label: i18n('icu:mainMenuView'),
      submenu: [
        {
          accelerator: 'CmdOrCtrl+0',
          label: i18n('icu:viewMenuResetZoom'),
          click: zoomReset,
        },
        {
          accelerator: 'CmdOrCtrl+=',
          label: i18n('icu:viewMenuZoomIn'),
          click: zoomIn,
        },
        {
          accelerator: 'CmdOrCtrl+-',
          label: i18n('icu:viewMenuZoomOut'),
          click: zoomOut,
        },
        {
          type: 'separator',
        },
        {
          role: 'togglefullscreen',
          label: i18n('icu:viewMenuToggleFullScreen'),
        },
        {
          type: 'separator',
        },
        {
          label: i18n('icu:debugLog'),
          click: showDebugLog,
        },
        ...(devTools
          ? [
              {
                type: 'separator' as const,
              },
              {
                role: 'toggleDevTools' as const,
                label: i18n('icu:viewMenuToggleDevTools'),
              },
            ]
          : []),
        ...(devTools && platform !== 'linux' && !process.mas
          ? [
              {
                label: i18n('icu:forceUpdate'),
                click: forceUpdate,
              },
            ]
          : []),
      ],
    },
    {
      label: i18n('icu:mainMenuWindow'),
      role: 'window',
      submenu: [
        {
          role: 'minimize',
          label: i18n('icu:windowMenuMinimize'),
        },
      ],
    },
    {
      label: i18n('icu:mainMenuHelp'),
      role: 'help',
      submenu: [
        {
          label: i18n('icu:helpMenuShowKeyboardShortcuts'),
          accelerator: 'CmdOrCtrl+/',
          click: showKeyboardShortcuts,
        },
        {
          type: 'separator',
        },
        {
          label: i18n('icu:contactUs'),
          click: openContactUs,
        },
        {
          label: i18n('icu:goToReleaseNotes'),
          click: openReleaseNotes,
        },
        {
          label: i18n('icu:goToForums'),
          click: openForums,
        },
        {
          label: i18n('icu:goToSupportPage'),
          click: openSupportPage,
        },
        ...(isProduction
          ? [
              {
                label: i18n('icu:joinTheBeta'),
                click: openJoinTheBeta,
              },
            ]
          : []),
        {
          type: 'separator',
        },
        {
          label: i18n('icu:aboutSignalDesktop'),
          click: showAbout,
        },
      ],
    },
  ];

  if (includeSetup) {
    const fileMenu = template[0];
    strictAssert(fileMenu, 'Missing fileMenu');

    if (Array.isArray(fileMenu.submenu)) {
      // These are in reverse order, since we're prepending them one at a time
      if (options.isNightly) {
        fileMenu.submenu.unshift({
          label: i18n('icu:menuStageLocalBackupForImport'),
          click: stageLocalBackupForImport,
        });
      }

      if (options.development) {
        fileMenu.submenu.unshift({
          label: i18n('icu:menuSetupAsStandalone'),
          click: setupAsStandalone,
        });
      }

      fileMenu.submenu.unshift({
        type: 'separator',
      });
      fileMenu.submenu.unshift({
        label: i18n('icu:menuSetupAsNewDevice'),
        click: setupAsNewDevice,
      });
    } else {
      throw new Error('createTemplate: fileMenu.submenu was not an array!');
    }
  }

  if (platform === 'darwin') {
    return updateForMac(template, i18n, options);
  }

  return template;
};

function updateForMac(
  template: MenuListType,
  i18n: LocalizerType,
  options: CreateTemplateOptionsType
): MenuListType {
  const { showAbout, showSettings, showWindow } = options;

  // Remove About item and separator from Help menu, since they're in the app menu
  const aboutMenu = template[4];
  strictAssert(aboutMenu, 'Missing aboutMenu');
  if (Array.isArray(aboutMenu.submenu)) {
    aboutMenu.submenu.pop();
    aboutMenu.submenu.pop();
  } else {
    throw new Error('updateForMac: help.submenu was not an array!');
  }

  // Remove preferences, separator, and quit from the File menu, since they're
  // in the app menu
  const fileMenu = template[0];
  strictAssert(fileMenu, 'Missing fileMenu');
  if (Array.isArray(fileMenu.submenu)) {
    fileMenu.submenu.pop();
    fileMenu.submenu.pop();
    fileMenu.submenu.pop();
    // And insert "close".
    fileMenu.submenu.push(
      {
        type: 'separator',
      },
      {
        label: i18n('icu:windowMenuClose'),
        accelerator: 'CmdOrCtrl+W',
        role: 'close',
      }
    );
  } else {
    throw new Error('updateForMac: fileMenu.submenu was not an array!');
  }

  // Add the OSX-specific Signal Desktop menu at the far left
  template.unshift({
    label: i18n('icu:signalDesktop'),
    submenu: [
      {
        label: i18n('icu:aboutSignalDesktop'),
        click: showAbout,
      },
      {
        type: 'separator',
      },
      {
        label: i18n('icu:mainMenuSettings'),
        accelerator: 'CommandOrControl+,',
        click: showSettings,
      },
      {
        type: 'separator',
      },
      {
        label: i18n('icu:appMenuServices'),
        role: 'services',
      },
      {
        type: 'separator',
      },
      {
        label: i18n('icu:appMenuHide'),
        role: 'hide',
      },
      {
        label: i18n('icu:appMenuHideOthers'),
        role: 'hideOthers',
      },
      {
        label: i18n('icu:appMenuUnhide'),
        role: 'unhide',
      },
      {
        type: 'separator',
      },
      {
        label: i18n('icu:appMenuQuit'),
        role: 'quit',
      },
    ],
  });

  const editMenu = template[2];
  strictAssert(editMenu, 'Missing editMenu');
  if (Array.isArray(editMenu.submenu)) {
    editMenu.submenu.push(
      {
        type: 'separator',
      },
      {
        label: i18n('icu:speech'),
        submenu: [
          {
            role: 'startSpeaking',
            label: i18n('icu:editMenuStartSpeaking'),
          },
          {
            role: 'stopSpeaking',
            label: i18n('icu:editMenuStopSpeaking'),
          },
        ],
      }
    );
  } else {
    throw new Error('updateForMac: edit.submenu was not an array!');
  }

  const windowMenu = template[4];
  strictAssert(windowMenu, 'Missing windowMenu');
  // Replace Window menu

  windowMenu.submenu = [
    {
      label: i18n('icu:windowMenuMinimize'),
      accelerator: 'CmdOrCtrl+M',
      role: 'minimize',
    },
    {
      label: i18n('icu:windowMenuZoom'),
      role: 'zoom',
    },
    {
      label: i18n('icu:show'),
      accelerator: 'CmdOrCtrl+Shift+0',
      click: showWindow,
    },
    {
      type: 'separator',
    },
    {
      role: 'front',
      label: i18n('icu:windowMenuBringAllToFront'),
    },
  ];

  return template;
}
