/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @format
 */

import path from 'path';
import {
  _NuxManagerContext,
  _createNuxManager,
  _setGlobalInteractionReporter,
  _LoggerContext,
} from 'flipper-plugin';
// eslint-disable-next-line no-restricted-imports,flipper/no-electron-remote-imports
import {
  ipcRenderer,
  remote,
  SaveDialogReturnValue,
  clipboard,
  shell,
} from 'electron';
import fs from 'fs';
import {setupMenuBar} from './setupMenuBar';
import {FlipperServer, FlipperServerConfig} from 'flipper-common';
import type {RenderHost} from 'flipper-ui-core';

export function initializeElectron(
  flipperServer: FlipperServer,
  flipperServerConfig: FlipperServerConfig,
) {
  const execPath = process.execPath || remote.process.execPath;
  const isProduction = !/node_modules[\\/]electron[\\/]/.test(execPath);

  function restart(update: boolean = false) {
    if (isProduction) {
      if (update) {
        const options = {
          args: process.argv
            .splice(0, 1)
            .filter((arg) => arg !== '--no-launcher' && arg !== '--no-updater'),
        };
        remote.app.relaunch(options);
      } else {
        remote.app.relaunch();
      }
      remote.app.exit();
    } else {
      // Relaunching the process with the standard way doesn't work in dev mode.
      // So instead we're sending a signal to dev server to kill the current instance of electron and launch new.
      fetch(`${flipperServerConfig.env.DEV_SERVER_URL}/_restartElectron`, {
        method: 'POST',
      });
    }
  }

  window.FlipperRenderHostInstance = {
    processId: remote.process.pid,
    isProduction,
    readTextFromClipboard() {
      return clipboard.readText();
    },
    writeTextToClipboard(text: string) {
      clipboard.writeText(text);
    },
    async showSaveDialog(options) {
      return (await remote.dialog.showSaveDialog(options))?.filePath;
    },
    async showOpenDialog({filter, defaultPath}) {
      const result = await remote.dialog.showOpenDialog({
        defaultPath,
        properties: ['openFile'],
        filters: filter ? [filter] : undefined,
      });
      return result.filePaths?.[0];
    },
    showSelectDirectoryDialog(defaultPath = path.resolve('/')) {
      return remote.dialog
        .showOpenDialog({
          properties: ['openDirectory'],
          defaultPath,
        })
        .then((result: SaveDialogReturnValue & {filePaths: string[]}) => {
          if (result.filePath) {
            return result.filePath.toString();
          }
          // Electron typings seem of here, just in case,
          // (can be tested with settings dialog)
          // handle both situations
          if (result.filePaths) {
            return result.filePaths[0];
          }
          return undefined;
        });
    },
    importFile: (async ({
      defaultPath,
      extensions,
      title,
      encoding = 'utf-8',
      multi,
    } = {}) => {
      let {filePaths} = await remote.dialog.showOpenDialog({
        defaultPath,
        properties: [
          'openFile',
          ...(multi ? (['multiSelections'] as const) : []),
        ],
        filters: extensions ? [{extensions, name: ''}] : undefined,
        title,
      });

      if (!filePaths.length) {
        return;
      }

      if (!multi) {
        filePaths = [filePaths[0]];
      }

      const descriptors = await Promise.all(
        filePaths.map(async (filePath) => {
          const fileName = path.basename(filePath);

          const data = await fs.promises.readFile(filePath, {encoding});
          return {
            data,
            name: fileName,
            path: filePath,
          };
        }),
      );

      return multi ? descriptors : descriptors[0];
    }) as RenderHost['importFile'],
    async exportFile(data, {defaultPath, encoding = 'utf-8'} = {}) {
      const {filePath} = await remote.dialog.showSaveDialog({
        defaultPath,
      });

      if (!filePath) {
        return;
      }

      await fs.promises.writeFile(filePath, data, {encoding});
      return filePath;
    },
    openLink(url: string) {
      shell.openExternal(url);
    },
    registerShortcut(shortcut, callback) {
      remote.globalShortcut.register(shortcut, callback);
      return () => remote.globalShortcut.unregister(shortcut);
    },
    hasFocus() {
      return remote.getCurrentWindow().isFocused();
    },
    onIpcEvent(event, callback) {
      ipcRenderer.on(event, (_ev, ...args: any[]) => {
        callback(...(args as any));
      });
    },
    sendIpcEvent(event, ...args: any[]) {
      ipcRenderer.send(event, ...args);
    },
    shouldUseDarkColors() {
      return remote.nativeTheme.shouldUseDarkColors;
    },
    restartFlipper() {
      restart();
    },
    loadDefaultPlugins: getDefaultPluginsIndex,
    serverConfig: flipperServerConfig,
    GK(gatekeeper) {
      return flipperServerConfig.gatekeepers[gatekeeper] ?? false;
    },
    flipperServer,
  } as RenderHost;

  setupMenuBar();
}

function getDefaultPluginsIndex() {
  // eslint-disable-next-line import/no-unresolved
  const index = require('../defaultPlugins');
  return index.default || index;
}
