#!/usr/bin/env node
const ElectronApp = require('./ElectronApp');
const ElectronLogBridge = require('./LogBridge');
const ElectronObservability = require('./Observability');
const DotnetApp = require('./DotnetApp');

const path = require('path');
const { app } = require('electron');
const electronDev = require('electron-is-dev');
const fs = require('fs');
const dotenv = require('dotenv');
// const nodePortScanner = require('node-port-scanner');

// Globals
const hubEnv = {
  env: 'MAKER_HUB_APP_ENVIRONMENT', beEnv: 'MAKER_HUB_BACKEND_ENVIRONMENT',
  productName: 'MAKER_HUB_PROJECT',
  fePort: 'MAKER_HUB_FRONTEND_PORT',
  port:'MAKER_HUB_PORT', hostname: 'MAKER_HUB_HOSTNAME', scheme: 'MAKER_HUB_SCHEME',
};

// DesktopApp class
class DesktopApp {
  serverProc = null;

  constructor(launchPath = '/', resourcesPath = 'bin') {
    this.env = {};
    this.log = new ElectronLogBridge(this);
    this.observability = new ElectronObservability(this);
    this.appDev = electronDev ?? false;
    const appPath = app.getAppPath();
    const rootPath = path.normalize(path.join(appPath, '..'));
    const binPath = this.appDev ? path.join(rootPath, 'Server') : path.join(rootPath, resourcesPath);
    this.paths = {
      launch: launchPath,
      app: appPath,
      bin: binPath,
      root: rootPath,
      // When spawned as a dev app, all files are stored in the root of the source code.
      env: this.appDev ? rootPath : binPath,
      userData: this.appDev ? rootPath : app.getPath('userData'),
      documents: this.appDev ? rootPath : app.getPath('documents'),
      logs: this.appDev ? rootPath : app.getPath('logs'),
    };

    this.configureEnvironment();
    this.hostname = this.env[hubEnv.hostname];
    this.scheme = this.env[hubEnv.scheme];
    this.ports = {
      fe: this.appDev ? this.env[hubEnv.fePort] : undefined,
      be: this.env[hubEnv.port] ?? 8000,
    };
    this.urls = {
      fe: this.ports.fe ? this.getServerUrl(this.ports.fe) : undefined,
      be: this.getServerUrl(this.ports.be),
    };
    this.productName = this.env[hubEnv.productName];

    this.log.info('paths', this.paths);

    // Not emitted on Widows "if the app is closed due to shutdown/restart or user logout"
    app.on('before-quit', this.shutdown.bind(this));
    app.on('will-quit', this.shutdown.bind(this));
    app.on('quit', this.shutdown.bind(this));

    this.dotnet = new DotnetApp(this);
    this.electron = new ElectronApp(this);
  }

  // Set & check environment variables.
  configureEnvironment = () => {
    this.log.info(process.platform, '@', this.paths.bin);

    this.env = {
      ...this.loadEnv('hub.env'),
      ...this.loadEnv('maker.env'),
    };

    this.log.info('[DOTENV]', 'v', app.getVersion(), this.env);

    Object.values(hubEnv).forEach(ev => {
      if (!this.env[ev]) {
        this.log.error(ev, 'was not present in');
        process.exit(1);
      }
    });
  }

  loadEnv = (fn) => {
    const envFp = path.join(this.paths.env, fn);
    if (!fs.existsSync(envFp)) {
      this.log.error('Missing', envFp);
      return {};
    }
    const cfg = dotenv.config({ path: envFp });
    if (!cfg.parsed) {
      this.log.error('Invalid', envFp);
      return {};
    }
    return cfg.parsed;
  }

  shutdown = () => {
    this.dotnet.shutdown();
    // this.electron.shutdown();
  }

  getServerUrl = (port) => {
    const portNum = parseInt(port);
    let portStr = '';
    let schema = this.scheme;
    if (portNum === 443) {
      schema = 'https';
    } else if (portNum !== 80) {
      portStr = `:${port}`;
    }
    return `${schema}://${this.hostname}${portStr}`;
  }
}

module.exports = DesktopApp;