<template>
  <QuickNav selected="Profile" />
  <!-- CONTENT -->
  <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
    <!-- Breadcrumb -->
    <div class="mb-8">
      <!-- Back -->
      <div class="text-lg text-center font-light relative px-7">
        <h1
          class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
          @click="$router.back()"
        >
          <fa icon="chevron-left" class="fa-fw" />
        </h1>
      </div>

      <!-- Heading -->
      <h1 id="ViewHeading" class="text-4xl text-center font-light pt-4">
        Your Contact Info
      </h1>
      <p
        v-if="!givenName"
        class="bg-amber-200 rounded-md overflow-hidden text-center px-4 py-3 mb-4"
      >
        <span class="text-red">Beware!</span>
        You aren't sharing your name, so quickly
        <br />
        <span
          @click="
            () => $refs.userNameDialog.open((name) => (this.givenName = name))
          "
          class="bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-1 rounded-md"
        >
          click here to set it for them.
        </span>
      </p>
    </div>
    <UserNameDialog ref="userNameDialog" />

    <div
      @click="onCopyUrlToClipboard()"
      v-if="activeDid && activeDid.startsWith(ETHR_DID_PREFIX)"
      class="text-center"
    >
      <!--
        Play with display options: https://qr-code-styling.com/
        See docs: https://www.npmjs.com/package/qr-code-generator-vue3
      -->
      <QRCodeVue3
        :value="this.qrValue"
        :cornersSquareOptions="{ type: 'extra-rounded' }"
        :dotsOptions="{ type: 'square' }"
        class="flex justify-center"
      />
      <span>
        Click the QR code to copy your contact info to your clipboard.
      </span>
    </div>
    <div v-else-if="activeDid" class="text-center">
      <!-- Not an ETHR DID so force them to paste it. (Passkey Peer DIDs are too big.) -->
      <span @click="onCopyDidToClipboard()" class="text-blue-500">
        Click here to copy your DID to your clipboard.
      </span>
      <span>
        Then give it to them so they can paste it in their list of People.
      </span>
    </div>
    <div class="text-center" v-else>
      You have no identitifiers yet, so
      <router-link
        :to="{ name: 'start' }"
        class="bg-blue-500 text-white px-1.5 py-1 rounded-md"
      >
        create your identifier.
      </router-link>
      <br />
      If you don't that first, these contacts won't see your activity.
    </div>

    <div class="text-center">
      <h1 class="text-4xl text-center font-light pt-6">Scan Contact Info</h1>
      <qrcode-stream @detect="onScanDetect" @error="onScanError" />
      <span>
        If you do not see a scanning camera window here, check your camera
        permissions.
      </span>
    </div>
  </section>
</template>

<script lang="ts">
import { AxiosError } from "axios";
import QRCodeVue3 from "qr-code-generator-vue3";
import { Component, Vue } from "vue-facing-decorator";
import { QrcodeStream } from "vue-qrcode-reader";
import { Router } from "vue-router";
import { useClipboard } from "@vueuse/core";

import QuickNav from "@/components/QuickNav.vue";
import UserNameDialog from "@/components/UserNameDialog.vue";
import { APP_SERVER, NotificationIface } from "@/constants/app";
import { db, retrieveSettingsForActiveAccount } from "@/db/index";
import { Contact } from "@/db/tables/contacts";
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
import { getContactPayloadFromJwtUrl } from "@/libs/crypto";
import {
  generateEndorserJwtUrlForAccount,
  isDid,
  register,
  setVisibilityUtil,
} from "@/libs/endorserServer";
import { ETHR_DID_PREFIX } from "@/libs/crypto/vc";
import { retrieveAccountMetadata } from "@/libs/util";

@Component({
  components: {
    QrcodeStream,
    QRCodeVue3,
    QuickNav,
    UserNameDialog,
  },
})
export default class ContactQRScanShow extends Vue {
  $notify!: (notification: NotificationIface, timeout?: number) => void;

  activeDid = "";
  apiServer = "";
  givenName = "";
  hideRegisterPromptOnNewContact = false;
  isRegistered = false;
  qrValue = "";

  ETHR_DID_PREFIX = ETHR_DID_PREFIX;

  async created() {
    const settings = await retrieveSettingsForActiveAccount();
    this.activeDid = settings.activeDid || "";
    this.apiServer = settings.apiServer || "";
    this.givenName = settings.firstName || "";
    this.hideRegisterPromptOnNewContact =
      !!settings.hideRegisterPromptOnNewContact;
    this.isRegistered = !!settings.isRegistered;

    const account = await retrieveAccountMetadata(this.activeDid);
    if (account) {
      const name =
        (settings.firstName || "") +
        (settings.lastName ? ` ${settings.lastName}` : ""); // lastName is deprecated, pre v 0.1.3

      this.qrValue = await generateEndorserJwtUrlForAccount(
        account,
        !!settings.isRegistered,
        name,
        settings.profileImageUrl,
        false,
      );
    }
  }

  danger(message: string, title: string = "Error", timeout = 5000) {
    this.$notify(
      {
        group: "alert",
        type: "danger",
        title: title,
        text: message,
      },
      timeout,
    );
  }

  /**
   *
   * @param content is the result of a QR scan, an array with one item with a rawValue property
   */
  // Unfortunately, there are not typescript definitions for the qrcode-stream component yet.
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  async onScanDetect(content: any) {
    const url = content[0]?.rawValue;
    if (url) {
      let newContact: Contact;
      try {
        const payload = getContactPayloadFromJwtUrl(url);
        if (!payload) {
          this.$notify(
            {
              group: "alert",
              type: "danger",
              title: "No Contact Info",
              text: "The contact info could not be parsed.",
            },
            3000,
          );
          return;
        }
        if (Array.isArray(payload.contacts)) {
          // reroute to the ContactsImport
          (this.$router as Router).push({
            path: '/contacts-import/' + url.substring(url.lastIndexOf('/') + 1),
          });
          return;
        }
        newContact = {
          did: payload.iss as string,
          name: payload.own.name,
          nextPubKeyHashB64: payload.own.nextPublicEncKeyHash,
          profileImageUrl: payload.own.profileImageUrl,
          publicKeyBase64: payload.own.publicEncKey,
          registered: payload.own.registered,
        };
        if (!newContact.did) {
          this.danger("There is no DID.", "Incomplete Contact");
          return;
        }
        if (!isDid(newContact.did)) {
          this.danger("The DID must begin with 'did:'", "Invalid DID");
          return;
        }
      } catch (e) {
        console.error("Error parsing QR info:", e);
        this.danger("Could not parse the QR info.", "Read Error");
        return;
      }

      try {
        await db.open();
        await db.contacts.add(newContact);

        let addedMessage;
        if (this.activeDid) {
          await this.setVisibility(newContact, true);
          newContact.seesMe = true; // didn't work inside setVisibility
          addedMessage =
            "They were added, and your activity is visible to them.";
        } else {
          addedMessage = "They were added.";
        }
        this.$notify(
          {
            group: "alert",
            type: "success",
            title: "Contact Added",
            text: addedMessage,
          },
          3000,
        );

        if (this.isRegistered) {
          if (!this.hideRegisterPromptOnNewContact && !newContact.registered) {
            setTimeout(() => {
              this.$notify(
                {
                  group: "modal",
                  type: "confirm",
                  title: "Register",
                  text: "Do you want to register them?",
                  onCancel: async (stopAsking: boolean) => {
                    if (stopAsking) {
                      await db.settings.update(MASTER_SETTINGS_KEY, {
                        hideRegisterPromptOnNewContact: stopAsking,
                      });
                      this.hideRegisterPromptOnNewContact = stopAsking;
                    }
                  },
                  onNo: async (stopAsking: boolean) => {
                    if (stopAsking) {
                      await db.settings.update(MASTER_SETTINGS_KEY, {
                        hideRegisterPromptOnNewContact: stopAsking,
                      });
                      this.hideRegisterPromptOnNewContact = stopAsking;
                    }
                  },
                  onYes: async () => {
                    await this.register(newContact);
                  },
                  promptToStopAsking: true,
                },
                -1,
              );
            }, 500);
          }
        }
      } catch (e) {
        console.error("Error saving contact info:", e);
        this.$notify(
          {
            group: "alert",
            type: "danger",
            title: "Contact Error",
            text: "Could not save contact info. Check if it already exists.",
          },
          5000,
        );
      }
    } else {
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Invalid Contact QR Code",
          text: "No QR code detected with contact information.",
        },
        5000,
      );
    }
  }

  async setVisibility(contact: Contact, visibility: boolean) {
    const result = await setVisibilityUtil(
      this.activeDid,
      this.apiServer,
      this.axios,
      db,
      contact,
      visibility,
    );
    if (result.error) {
      this.danger(result.error as string, "Error Setting Visibility");
    } else if (!result.success) {
      console.error("Got strange result from setting visibility:", result);
    }
  }

  async register(contact: Contact) {
    this.$notify(
      {
        group: "alert",
        type: "toast",
        text: "",
        title: "Registration submitted...",
      },
      1000,
    );

    try {
      const regResult = await register(
        this.activeDid,
        this.apiServer,
        this.axios,
        contact,
      );
      if (regResult.success) {
        contact.registered = true;
        db.contacts.update(contact.did, { registered: true });

        this.$notify(
          {
            group: "alert",
            type: "success",
            title: "Registration Success",
            text:
              (contact.name || "That unnamed person") + " has been registered.",
          },
          5000,
        );
      } else {
        this.$notify(
          {
            group: "alert",
            type: "danger",
            title: "Registration Error",
            text:
              (regResult.error as string) ||
              "Something went wrong during registration.",
          },
          5000,
        );
      }
    } catch (error) {
      console.error("Error when registering:", error);
      let userMessage = "There was an error.";
      const serverError = error as AxiosError;
      if (serverError) {
        if (serverError.response?.data?.error?.message) {
          userMessage = serverError.response.data.error.message;
        } else if (serverError.message) {
          userMessage = serverError.message; // Info for the user
        } else {
          userMessage = JSON.stringify(serverError.toJSON());
        }
      } else {
        userMessage = error as string;
      }
      // Now set that error for the user to see.
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Registration Error",
          text: userMessage,
        },
        5000,
      );
    }
  }

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  onScanError(error: any) {
    console.error("Scan was invalid:", error);
    this.$notify(
      {
        group: "alert",
        type: "danger",
        title: "Invalid Scan",
        text: "The scan was invalid.",
      },
      5000,
    );
  }

  onCopyUrlToClipboard() {
    //this.onScanDetect([{ rawValue: this.qrValue }]); // good for testing
    useClipboard()
      .copy(this.qrValue)
      .then(() => {
        // console.log("Contact URL:", this.qrValue);
        this.$notify(
          {
            group: "alert",
            type: "toast",
            title: "Copied",
            text: "Contact URL was copied to clipboard.",
          },
          2000,
        );
      });
  }

  onCopyDidToClipboard() {
    //this.onScanDetect([{ rawValue: this.qrValue }]); // good for testing
    useClipboard()
      .copy(this.activeDid)
      .then(() => {
        this.$notify(
          {
            group: "alert",
            type: "info",
            title: "Copied",
            text: "Your DID was copied to the clipboard. Have them paste it in the box on their 'People' screen to add you.",
          },
          5000,
        );
      });
  }
}
</script>
