/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";

const lazy = {};

ChromeUtils.defineESModuleGetters(lazy, {
  IPProtectionServerlist:
    "resource:///modules/ipprotection/IPProtectionServerlist.sys.mjs",
  IPProtectionService:
    "resource:///modules/ipprotection/IPProtectionService.sys.mjs",
  IPProtectionStates:
    "resource:///modules/ipprotection/IPProtectionService.sys.mjs",
});

const AUTOSTART_PREF = "browser.ipProtection.autoStartEnabled";

/**
 * This class monitors the auto-start pref and if it sees a READY state, it
 * calls `start()`. This is done only if the previous state was not a ACTIVE
 * because, in that case, more likely the VPN on/off state is an user decision.
 */
class IPPAutoStartSingleton {
  #shouldStartWhenReady = false;

  constructor() {
    XPCOMUtils.defineLazyPreferenceGetter(
      this,
      "autoStartPref",
      AUTOSTART_PREF,
      false,
      (_pref, _oldVal, featureEnabled) => {
        if (featureEnabled) {
          this.init();
        } else {
          this.uninit();
        }
      }
    );
  }

  init() {
    if (this.autoStart && !this.handleEvent) {
      this.handleEvent = this.#handleEvent.bind(this);
      this.#shouldStartWhenReady = true;

      lazy.IPProtectionService.addEventListener(
        "IPProtectionService:StateChanged",
        this.handleEvent
      );
    }
  }

  initOnStartupCompleted() {}

  uninit() {
    if (this.handleEvent) {
      lazy.IPProtectionService.removeEventListener(
        "IPProtectionService:StateChanged",
        this.handleEvent
      );

      delete this.handleEvent;
      this.#shouldStartWhenReady = false;
    }
  }

  get autoStart() {
    // We activate the auto-start feature only if the pref is true and we have
    // the serverlist already.
    return this.autoStartPref && lazy.IPProtectionServerlist.hasList;
  }

  #handleEvent(_event) {
    switch (lazy.IPProtectionService.state) {
      case lazy.IPProtectionStates.UNINITIALIZED:
      case lazy.IPProtectionStates.UNAVAILABLE:
      case lazy.IPProtectionStates.UNAUTHENTICATED:
      case lazy.IPProtectionStates.ERROR:
        this.#shouldStartWhenReady = true;
        break;

      case lazy.IPProtectionStates.READY:
        if (this.#shouldStartWhenReady) {
          this.#shouldStartWhenReady = false;
          lazy.IPProtectionService.start();
        }
        break;

      default:
        break;
    }
  }
}

const IPPAutoStart = new IPPAutoStartSingleton();

/**
 * This class monitors the startup phases and registers/unregisters the channel
 * filter to avoid data leak. The activation of the VPN is done by the
 * IPPAutoStart object above.
 */
class IPPEarlyStartupFilter {
  #autoStartAndAtStartup = false;

  constructor() {
    this.handleEvent = this.#handleEvent.bind(this);
    this.#autoStartAndAtStartup = IPPAutoStart.autoStart;
  }

  init() {
    if (this.#autoStartAndAtStartup) {
      lazy.IPProtectionService.proxyManager.createChannelFilter();

      lazy.IPProtectionService.addEventListener(
        "IPProtectionService:StateChanged",
        this.handleEvent
      );
    }
  }

  initOnStartupCompleted() {}

  uninit() {
    if (this.autoStartAndAtStartup) {
      this.#autoStartAndAtStartup = false;

      lazy.IPProtectionService.removeEventListener(
        "IPProtectionService:StateChanged",
        this.handleEvent
      );
    }
  }

  #cancelChannelFilter() {
    lazy.IPProtectionService.proxyManager.cancelChannelFilter();
  }

  #handleEvent(_event) {
    switch (lazy.IPProtectionService.state) {
      case lazy.IPProtectionStates.UNAVAILABLE:
      case lazy.IPProtectionStates.UNAUTHENTICATED:
      case lazy.IPProtectionStates.ERROR:
        // These states block the auto-start at startup.
        this.#cancelChannelFilter();
        this.uninit();
        break;

      case lazy.IPProtectionStates.ACTIVE:
        // We have completed our task.
        this.uninit();
        break;

      default:
        // Let's ignoring any other state.
        break;
    }
  }
}

const IPPAutoStartHelpers = [IPPAutoStart, new IPPEarlyStartupFilter()];

export { IPPAutoStartHelpers };
