/* eslint-disable no-console */
import { createHash } from "crypto";
import { existsSync, mkdirSync } from "fs";
import { homedir } from "os";
import { join as path_join } from "path";

import * as ipc from "node-ipc";

export function getIpcSocketRoot(): string | null {
  let socketRoot = null;

  switch (process.platform) {
    case "darwin": {
      const ipcSocketRootDir = path_join(homedir(), "tmp");
      if (!existsSync(ipcSocketRootDir)) {
        mkdirSync(ipcSocketRootDir);
      }
      socketRoot = ipcSocketRootDir + "/";
      break;
    }
    case "win32": {
      // Let node-ipc use a unique IPC pipe //./pipe/xxxxxxxxxxxxxxxxx.app.bitwarden per user.
      // Hashing prevents problems with reserved characters and file length limitations.
      socketRoot = createHash("sha1").update(homedir()).digest("hex") + ".";
    }
  }
  return socketRoot;
}

ipc.config.id = "proxy";
ipc.config.retry = 1500;
ipc.config.logger = console.warn; // Stdout is used for native messaging
const ipcSocketRoot = getIpcSocketRoot();
if (ipcSocketRoot != null) {
  ipc.config.socketRoot = ipcSocketRoot;
}

export default class IPC {
  onMessage: (message: object) => void;

  private connected = false;

  connect() {
    ipc.connectTo("bitwarden", () => {
      ipc.of.bitwarden.on("connect", () => {
        this.connected = true;
        console.error("## connected to bitwarden desktop ##");

        // Notify browser extension, connection is established to desktop application.
        this.onMessage({ command: "connected" });
      });

      ipc.of.bitwarden.on("disconnect", () => {
        this.connected = false;
        console.error("disconnected from world");

        // Notify browser extension, no connection to desktop application.
        this.onMessage({ command: "disconnected" });
      });

      ipc.of.bitwarden.on("message", (message: any) => {
        this.onMessage(message);
      });

      ipc.of.bitwarden.on("error", (err: any) => {
        console.error("error", err);
      });
    });
  }

  isConnected(): boolean {
    return this.connected;
  }

  send(json: object) {
    ipc.of.bitwarden.emit("message", json);
  }
}
