import { MarginInline } from "../elements/MarginInline";
import { Page } from "../types/Page";
import { QRScanner, QRScannerType } from "../elements/QRScanner";
import { SealStatusType, getSealStatus } from "../api/sys/getSealStatus";
import { changePage, setErrorText, setPageContent } from "../pageUtils";
import { makeElement } from "../htmlUtils";
import { submitUnsealKey } from "../api/sys/submitUnsealKey";
import i18next from "i18next";

const UnsealInputModes = {
  FORM_INPUT: "FORM_INPUT",
  QR_INPUT: "QR_INPUT",
};

export class UnsealPage extends Page {
  constructor() {
    super();
    this.mode = UnsealInputModes.FORM_INPUT;
  }

  mode: string;
  refresher: number;
  qrScanner: QRScannerType;
  unsealProgress: HTMLProgressElement;
  unsealProgressText: HTMLParagraphElement;
  unsealInputContent: HTMLElement;
  unsealKeyForm: HTMLFormElement;

  async cleanup(): Promise<void> {
    this.deinitWebcam();
    clearInterval(this.refresher);
  }

  deinitWebcam(): void {
    try {
      this.qrScanner.deinit();
    } catch (_) {
      // Do Nothing
    }
  }

  makeRefresher(): void {
    const id = setInterval(() => {
      void (this as UnsealPage).doRefresh().then(() => {});
      return;
    }, 1000);
    this.refresher = id as unknown as number;
  }

  async doRefresh(): Promise<void> {
    const status = await getSealStatus();
    await this.updateSealProgress(status);
  }

  async render(): Promise<void> {
    this.unsealProgress = makeElement({
      tag: "progress",
      class: "uk-progress",
      attributes: { value: "0", max: "0" },
    }) as HTMLProgressElement;
    this.unsealProgressText = makeElement({
      tag: "p",
      text: i18next.t("unseal_keys_progress", {
        progress: "0",
        keys_needed: "0",
      }),
    }) as HTMLParagraphElement;
    this.unsealInputContent = makeElement({
      tag: "div",
    });
    setPageContent(
      makeElement({
        tag: "div",
        children: [
          this.unsealProgress,
          makeElement({
            tag: "p",
            id: "errorText",
            class: ["uk-text-danger", "uk-margin-top"],
          }),
          this.unsealProgressText,
          this.unsealInputContent,
        ],
      }),
    );
    this.switchInputMode(this.mode);
    await this.updateSealProgress(await getSealStatus());
    this.makeRefresher();
  }

  setButtons(method: string): void {
    const newMethod: string =
      method == UnsealInputModes.FORM_INPUT
        ? UnsealInputModes.QR_INPUT
        : UnsealInputModes.FORM_INPUT;
    const buttonText: string =
      newMethod == UnsealInputModes.FORM_INPUT
        ? i18next.t("unseal_input_btn")
        : i18next.t("unseal_qr_btn");
    this.unsealInputContent.appendChild(
      makeElement({
        tag: "button",
        class: ["uk-button", "uk-button-primary"],
        text: buttonText,
        onclick: () => {
          this.switchInputMode(newMethod);
        },
      }),
    );
  }

  switchInputMode(method: string): void {
    this.deinitWebcam();
    this.unsealInputContent.querySelectorAll("*").forEach((n) => n.remove());
    if (method == UnsealInputModes.FORM_INPUT) this.makeUnsealForm();
    if (method == UnsealInputModes.QR_INPUT) void this.makeQRInput();
    this.setButtons(method);
  }

  makeUnsealForm(): void {
    this.unsealKeyForm = makeElement({
      tag: "form",
      children: [
        MarginInline(
          makeElement({
            tag: "input",
            class: ["uk-input", "uk-form-width-medium"],
            attributes: {
              required: "true",
              type: "password",
              placeholder: i18next.t("key_input_placeholder"),
              name: "key",
            },
          }),
        ),
        MarginInline(
          makeElement({
            tag: "button",
            class: ["uk-button", "uk-button-primary"],
            text: i18next.t("submit_key_btn"),
          }),
        ),
      ],
    }) as HTMLFormElement;
    this.unsealInputContent.appendChild(this.unsealKeyForm);
    this.unsealKeyForm.addEventListener("submit", (e: Event) => {
      e.preventDefault();
      void this.handleKeySubmit();
    });
  }

  async makeQRInput(): Promise<void> {
    this.qrScanner = await QRScanner(async (code: string) => {
      await this.submitKey(code);
    });
    this.unsealInputContent.appendChild(this.qrScanner);
  }

  async updateSealProgress(data: SealStatusType): Promise<void> {
    const progress = data.progress;
    const keysNeeded = data.t;
    const text = this.unsealProgressText;
    text.innerText = i18next.t("unseal_keys_progress", {
      progress: String(progress),
      keys_needed: String(keysNeeded),
    });
    const progressBar = this.unsealProgress;
    progressBar.value = progress;
    progressBar.max = keysNeeded;
    if (!data.sealed) {
      progressBar.value = keysNeeded;
      await changePage("HOME");
    }
  }

  async submitKey(key: string): Promise<void> {
    try {
      await submitUnsealKey(key);
      await this.updateSealProgress(await getSealStatus());
    } catch (e: unknown) {
      const error = e as Error;
      setErrorText(error.message);
    }
  }

  async handleKeySubmit(): Promise<void> {
    const formData = new FormData(this.unsealKeyForm);

    await this.submitKey(formData.get("key") as string);
  }
  get name(): string {
    return i18next.t("unseal_vault_text");
  }
}