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

import React from 'react';
import type { ReactNode } from 'react';
import TitleBar from '@indutny/frameless-titlebar';
import type { MenuItem } from '@indutny/frameless-titlebar';
import type { MenuItemConstructorOptions } from 'electron';

import { createTemplate } from '../../app/menu';
import { ThemeType } from '../types/Util';
import type { LocaleMessagesType } from '../types/I18N';
import type { MenuOptionsType, MenuActionType } from '../types/menu';

export type MenuPropsType = Readonly<{
  hasMenu: true;
  localeMessages: LocaleMessagesType;
  menuOptions: MenuOptionsType;
  executeMenuAction: (action: MenuActionType) => void;
}>;

export type ExecuteMenuRoleType = (
  role: MenuItemConstructorOptions['role']
) => void;

export type PropsType = Readonly<{
  theme: ThemeType;
  isMaximized?: boolean;
  isFullScreen?: boolean;
  platform: string;
  executeMenuRole: ExecuteMenuRoleType;
  titleBarDoubleClick?: () => void;
  children: ReactNode;
}> &
  (MenuPropsType | { hasMenu?: false });

// Windows only
const ROLE_TO_ACCELERATOR = new Map<
  MenuItemConstructorOptions['role'],
  string
>();
ROLE_TO_ACCELERATOR.set('undo', 'CmdOrCtrl+Z');
ROLE_TO_ACCELERATOR.set('redo', 'CmdOrCtrl+Y');
ROLE_TO_ACCELERATOR.set('cut', 'CmdOrCtrl+X');
ROLE_TO_ACCELERATOR.set('copy', 'CmdOrCtrl+C');
ROLE_TO_ACCELERATOR.set('paste', 'CmdOrCtrl+V');
ROLE_TO_ACCELERATOR.set('pasteAndMatchStyle', 'CmdOrCtrl+Shift+V');
ROLE_TO_ACCELERATOR.set('selectAll', 'CmdOrCtrl+A');
ROLE_TO_ACCELERATOR.set('resetZoom', 'CmdOrCtrl+0');
ROLE_TO_ACCELERATOR.set('zoomIn', 'CmdOrCtrl+=');
ROLE_TO_ACCELERATOR.set('zoomOut', 'CmdOrCtrl+-');
ROLE_TO_ACCELERATOR.set('togglefullscreen', 'F11');
ROLE_TO_ACCELERATOR.set('toggleDevTools', 'CmdOrCtrl+Shift+I');
ROLE_TO_ACCELERATOR.set('minimize', 'CmdOrCtrl+M');

function convertMenu(
  menuList: ReadonlyArray<MenuItemConstructorOptions>,
  executeMenuRole: (role: MenuItemConstructorOptions['role']) => void
): Array<MenuItem> {
  return menuList.map(item => {
    const {
      type,
      label,
      accelerator: originalAccelerator,
      click: originalClick,
      submenu: originalSubmenu,
      role,
    } = item;
    let submenu: Array<MenuItem> | undefined;

    if (Array.isArray(originalSubmenu)) {
      submenu = convertMenu(originalSubmenu, executeMenuRole);
    } else if (originalSubmenu) {
      throw new Error('Non-array submenu is not supported');
    }

    let click: (() => unknown) | undefined;
    if (originalClick) {
      if (role) {
        throw new Error(`Menu item: ${label} has both click and role`);
      }

      // We don't use arguments in app/menu.ts
      click = originalClick as () => unknown;
    } else if (role) {
      click = () => executeMenuRole(role);
    }

    let accelerator: string | undefined;
    if (originalAccelerator) {
      accelerator = originalAccelerator.toString();
    } else if (role) {
      accelerator = ROLE_TO_ACCELERATOR.get(role);
    }

    return {
      type,
      label,
      accelerator,
      click,
      submenu,
    };
  });
}

export const TitleBarContainer = (props: PropsType): JSX.Element => {
  const {
    theme,
    isMaximized,
    isFullScreen,
    executeMenuRole,
    titleBarDoubleClick,
    children,
    platform,
    hasMenu,
  } = props;

  if (platform !== 'win32' || isFullScreen) {
    return <>{children}</>;
  }

  let maybeMenu: Array<MenuItem> | undefined;
  if (hasMenu) {
    const { localeMessages, menuOptions, executeMenuAction } = props;

    const menuTemplate = createTemplate(
      {
        ...menuOptions,

        // actions
        forceUpdate: () => executeMenuAction('forceUpdate'),
        openContactUs: () => executeMenuAction('openContactUs'),
        openForums: () => executeMenuAction('openForums'),
        openJoinTheBeta: () => executeMenuAction('openJoinTheBeta'),
        openReleaseNotes: () => executeMenuAction('openReleaseNotes'),
        openSupportPage: () => executeMenuAction('openSupportPage'),
        setupAsNewDevice: () => executeMenuAction('setupAsNewDevice'),
        setupAsStandalone: () => executeMenuAction('setupAsStandalone'),
        showAbout: () => executeMenuAction('showAbout'),
        showDebugLog: () => executeMenuAction('showDebugLog'),
        showKeyboardShortcuts: () => executeMenuAction('showKeyboardShortcuts'),
        showSettings: () => executeMenuAction('showSettings'),
        showStickerCreator: () => executeMenuAction('showStickerCreator'),
        showWindow: () => executeMenuAction('showWindow'),
      },
      localeMessages
    );

    maybeMenu = convertMenu(menuTemplate, executeMenuRole);
  }

  const titleBarTheme = {
    bar: {
      palette:
        theme === ThemeType.light ? ('light' as const) : ('dark' as const),
    },

    // Hide overlay
    menu: {
      overlay: {
        opacity: 0,
      },
    },
  };

  return (
    <div className="TitleBarContainer">
      <TitleBar
        className="TitleBarContainer__title"
        platform={platform}
        iconSrc="images/icon_32.png"
        theme={titleBarTheme}
        maximized={isMaximized}
        menu={maybeMenu}
        onDoubleClick={titleBarDoubleClick}
        hideControls
      />

      <div className="TitleBarContainer__content">{children}</div>
    </div>
  );
};
