import { firstValueFrom } from "rxjs";

import { LockService, LogoutService } from "@bitwarden/auth/common";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import {
  VaultTimeoutAction,
  VaultTimeoutService,
  VaultTimeoutSettingsService,
  VaultTimeoutStringType,
} from "@bitwarden/common/key-management/vault-timeout";
import { ServerNotificationsService } from "@bitwarden/common/platform/server-notifications";
import { UserId } from "@bitwarden/user-core";

const IdleInterval = 60 * 5; // 5 minutes

export default class IdleBackground {
  private idle: typeof chrome.idle | typeof browser.idle | null;
  private idleTimer: null | number | NodeJS.Timeout = null;
  private idleState = "active";

  constructor(
    private vaultTimeoutService: VaultTimeoutService,
    private serverNotificationsService: ServerNotificationsService,
    private accountService: AccountService,
    private vaultTimeoutSettingsService: VaultTimeoutSettingsService,
    private lockService: LockService,
    private logoutService: LogoutService,
  ) {
    this.idle = chrome.idle || (browser != null ? browser.idle : null);
  }

  init() {
    if (!this.idle) {
      return;
    }

    const idleHandler = (newState: string) => {
      if (newState === "active") {
        this.serverNotificationsService.reconnectFromActivity();
      } else {
        this.serverNotificationsService.disconnectFromInactivity();
      }
    };
    if (this.idle.onStateChanged && this.idle.setDetectionInterval) {
      this.idle.setDetectionInterval(IdleInterval);
      this.idle.onStateChanged.addListener(idleHandler);
    } else {
      this.pollIdle(idleHandler);
    }

    if (this.idle.onStateChanged) {
      this.idle.onStateChanged.addListener(
        async (newState: `${chrome.idle.IdleState}` | browser.idle.IdleState) => {
          if (newState === "locked") {
            // Need to check if any of the current users have their timeout set to `onLocked`
            const allUsers = await firstValueFrom(this.accountService.accounts$);
            for (const userId in allUsers) {
              // Skip if vault timeout is suppressed by shared unlock
              if (
                await this.vaultTimeoutSettingsService.isVaultTimeoutSuppressed(userId as UserId)
              ) {
                continue;
              }

              // If the screen is locked or the screensaver activates
              const timeout = await firstValueFrom(
                this.vaultTimeoutSettingsService.getVaultTimeoutByUserId$(userId),
              );
              if (timeout === VaultTimeoutStringType.OnLocked) {
                // On System Lock vault timeout option
                const action = await firstValueFrom(
                  this.vaultTimeoutSettingsService.getVaultTimeoutActionByUserId$(userId),
                );
                if (action === VaultTimeoutAction.LogOut) {
                  await this.logoutService.logout(userId as UserId, "vaultTimeout");
                } else {
                  await this.lockService.lock(userId as UserId);
                }
              }
            }
          }
        },
      );
    }
  }

  private pollIdle(handler: (newState: string) => void) {
    if (this.idleTimer != null) {
      globalThis.clearTimeout(this.idleTimer);
      this.idleTimer = null;
    }

    void this.idle?.queryState(IdleInterval, (state: string) => {
      if (state !== this.idleState) {
        this.idleState = state;
        handler(state);
      }
      this.idleTimer = globalThis.setTimeout(() => this.pollIdle(handler), 5000);
    });
  }
}
