<template>
  <QuickNav />

  <!-- 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"></fa>
        </h1>
      </div>

      <!-- Heading -->
      <h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8">
        Test
      </h1>
    </div>

    <div>
      <h2 class="text-xl font-bold mb-4">Notiwind Alerts</h2>

      <button
        @click="
          this.$notify(
            {
              group: 'alert',
              type: 'toast',
              text: 'I\'m a toast. Without a timeout, I\'m stuck.',
            },
            5000,
          )
        "
        class="font-bold uppercase bg-slate-900 text-white px-3 py-2 rounded-md mr-2"
      >
        Toast
      </button>

      <button
        @click="
          this.$notify(
            {
              group: 'alert',
              type: 'info',
              title: 'Information Alert',
              text: 'Just wanted you to know.',
            },
            -1,
          )
        "
        class="font-bold uppercase bg-slate-600 text-white px-3 py-2 rounded-md mr-2"
      >
        Info
      </button>

      <button
        @click="
          this.$notify(
            {
              group: 'alert',
              type: 'success',
              title: 'Success Alert',
              text: 'Congratulations!',
            },
            -1,
          )
        "
        class="font-bold uppercase bg-emerald-600 text-white px-3 py-2 rounded-md mr-2"
      >
        Success
      </button>

      <button
        @click="
          this.$notify(
            {
              group: 'alert',
              type: 'warning',
              title: 'Warning Alert',
              text: 'You might wanna look at this.',
            },
            -1,
          )
        "
        class="font-bold uppercase bg-amber-600 text-white px-3 py-2 rounded-md mr-2"
      >
        Warning
      </button>

      <button
        @click="
          this.$notify(
            {
              group: 'alert',
              type: 'danger',
              title: 'Danger Alert',
              text: 'Something terrible has happened!',
            },
            -1,
          )
        "
        class="font-bold uppercase bg-rose-600 text-white px-3 py-2 rounded-md mr-2"
      >
        Danger
      </button>

      <button
        @click="
          this.$notify(
            {
              group: 'modal',
              type: 'notification-permission',
            },
            -1,
          )
        "
        class="font-bold uppercase bg-slate-600 text-white px-3 py-2 rounded-md mr-2"
      >
        Notif ON
      </button>

      <button
        @click="
          this.$notify(
            {
              group: 'modal',
              type: 'notification-mute',
            },
            -1,
          )
        "
        class="font-bold uppercase bg-slate-600 text-white px-3 py-2 rounded-md mr-2"
      >
        Notif MUTE
      </button>

      <button
        @click="
          this.$notify(
            {
              group: 'modal',
              type: 'notification-off',
            },
            -1,
          )
        "
        class="font-bold uppercase bg-slate-600 text-white px-3 py-2 rounded-md mr-2"
      >
        Notif OFF
      </button>
    </div>

    <div class="mt-8">
      <h2 class="text-xl font-bold mb-4">Image Sharing</h2>
      Populates the "shared-photo" view as if they used "share_target".
      <input type="file" @change="uploadFile" />
      <router-link
        v-if="showFileNextStep()"
        :to="{
          name: 'shared-photo',
          query: { fileName },
        }"
        class="block w-full text-center text-md bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mb-2 mt-2"
      >
        Go to Shared Page
      </router-link>
    </div>

    <div class="mt-8">
      <h2 class="text-xl font-bold mb-4">Passkeys</h2>
      See console for results.
      <br />
      See existing passkeys in Chrome at: chrome://settings/passkeys
      <br />
      Active DID: {{ activeDid || "nothing, which" }}
      {{ credIdHex ? "has a passkey ID" : "has no passkey ID" }}

      <div>
        Register Passkey
        <button
          @click="register()"
          class="font-bold uppercase bg-slate-500 text-white px-3 py-2 rounded-md mr-2"
        >
          Simplewebauthn
        </button>
      </div>

      <div>
        Create JWT
        <button
          @click="createJwtSimplewebauthn()"
          class="font-bold uppercase bg-slate-500 text-white px-3 py-2 rounded-md mr-2"
        >
          Simplewebauthn
        </button>
        <button
          @click="createJwtNavigator()"
          class="font-bold uppercase bg-slate-500 text-white px-3 py-2 rounded-md mr-2"
        >
          Navigator
        </button>
      </div>

      <div v-if="jwt">
        Verify New JWT
        <button
          @click="verifySimplewebauthn()"
          class="font-bold uppercase bg-slate-500 text-white px-3 py-2 rounded-md mr-2"
        >
          Simplewebauthn
        </button>
        <button
          @click="verifyWebCrypto()"
          class="font-bold uppercase bg-slate-500 text-white px-3 py-2 rounded-md mr-2"
        >
          WebCrypto
        </button>
        <button
          @click="verifyP256()"
          class="font-bold uppercase bg-slate-500 text-white px-3 py-2 rounded-md mr-2"
        >
          p256 - broken
        </button>
      </div>
      <div v-else>Verify New JWT -- requires creation first</div>
      <button
        @click="verifyMyJwt()"
        class="font-bold uppercase bg-slate-500 text-white px-3 py-2 rounded-md mr-2"
      >
        Verify Hard-Coded JWT
      </button>
    </div>
  </section>
</template>

<script lang="ts">
import { Buffer } from "buffer/";
import { Base64URLString } from "@simplewebauthn/types";
import { ref } from "vue";
import { Component, Vue } from "vue-facing-decorator";

import QuickNav from "@/components/QuickNav.vue";
import { AppString, NotificationIface } from "@/constants/app";
import { accountsDB, db } from "@/db/index";
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
import * as vcLib from "@/libs/crypto/vc";
import {
  PeerSetup,
  verifyJwtP256,
  verifyJwtSimplewebauthn,
  verifyJwtWebCrypto,
} from "@/libs/crypto/vc/passkeyDidPeer";
import {
  AccountKeyInfo,
  getAccount,
  registerAndSavePasskey,
} from "@/libs/util";

const inputFileNameRef = ref<Blob>();

const TEST_PAYLOAD = {
  vc: {
    credentialSubject: {
      "@context": "https://schema.org",
      "@type": "GiveAction",
      description: "pizza",
    },
  },
};

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

  // for file import
  fileName?: string;

  // for passkeys
  credIdHex?: string;
  activeDid?: string;
  jwt?: string;
  peerSetup?: PeerSetup;
  userName?: string;

  async mounted() {
    await db.open();
    const settings = await db.settings.get(MASTER_SETTINGS_KEY);
    this.activeDid = (settings?.activeDid as string) || "";
    this.userName = settings?.firstName as string;

    await accountsDB.open();
    const account: { identity?: string } | undefined = await accountsDB.accounts
      .where("did")
      .equals(this.activeDid)
      .first();
    if (this.activeDid) {
      if (account) {
        this.credIdHex = account.passkeyCredIdHex as string;
      } else {
        alert("No account found for DID " + this.activeDid);
      }
    }
  }

  async uploadFile(event: Event) {
    inputFileNameRef.value = event.target?.["files"][0];
    // https://developer.mozilla.org/en-US/docs/Web/API/File
    // ... plus it has a `type` property from my testing
    const file = inputFileNameRef.value;
    if (file != null) {
      const reader = new FileReader();
      reader.onload = async (e) => {
        const data = e.target?.result as ArrayBuffer;
        if (data) {
          const blob = new Blob([new Uint8Array(data)], {
            type: file.type,
          });
          this.fileName = file.name as string;
          const temp = await db.temp.get("shared-photo");
          if (temp) {
            await db.temp.update("shared-photo", { blob });
          } else {
            await db.temp.add({ id: "shared-photo", blob });
          }
        }
      };
      reader.readAsArrayBuffer(file as Blob);
    }
  }

  showFileNextStep() {
    return !!inputFileNameRef.value;
  }

  public async register() {
    const DEFAULT_USERNAME = AppString.APP_NAME + " Tester";
    if (!this.userName) {
      this.$notify(
        {
          group: "modal",
          type: "confirm",
          title: "No Name",
          text: "You should have a name to attach to this passkey. Would you like to enter your own name first?",
          onNo: async () => {
            this.userName = DEFAULT_USERNAME;
          },
          onYes: async () => {
            this.$router.push({ name: "new-edit-account" });
          },
          noText: "try again and use " + DEFAULT_USERNAME,
        },
        -1,
      );
      return;
    }
    const account = await registerAndSavePasskey(
      AppString.APP_NAME + " - " + this.userName,
    );
    this.activeDid = account.did;
    this.credIdHex = account.passkeyCredIdHex;
  }

  public async createJwtSimplewebauthn() {
    const account: AccountKeyInfo | undefined = await getAccount(
      this.activeDid || "",
    );
    if (!vcLib.isFromPasskey(account)) {
      alert(`The DID ${this.activeDid} is not passkey-enabled.`);
      return;
    }
    this.peerSetup = new PeerSetup();
    this.jwt = await this.peerSetup.createJwtSimplewebauthn(
      this.activeDid as string,
      TEST_PAYLOAD,
      this.credIdHex as string,
    );
    console.log("simple jwt4url", this.jwt);
  }

  public async createJwtNavigator() {
    const account: AccountKeyInfo | undefined = await getAccount(
      this.activeDid || "",
    );
    if (!vcLib.isFromPasskey(account)) {
      alert(`The DID ${this.activeDid} is not passkey-enabled.`);
      return;
    }
    this.peerSetup = new PeerSetup();
    this.jwt = await this.peerSetup.createJwtNavigator(
      this.activeDid as string,
      TEST_PAYLOAD,
      this.credIdHex as string,
    );
    console.log("lower jwt4url", this.jwt);
  }

  public async verifyP256() {
    const decoded = await verifyJwtP256(
      this.credIdHex as string,
      this.activeDid as string,
      this.peerSetup?.authenticatorData as ArrayBuffer,
      this.peerSetup?.challenge as Uint8Array,
      this.peerSetup?.clientDataJsonBase64Url as Base64URLString,
      this.peerSetup?.signature as Base64URLString,
    );
    console.log("decoded", decoded);
  }

  public async verifySimplewebauthn() {
    const decoded = await verifyJwtSimplewebauthn(
      this.credIdHex as string,
      this.activeDid as string,
      this.peerSetup?.authenticatorData as ArrayBuffer,
      this.peerSetup?.challenge as Uint8Array,
      this.peerSetup?.clientDataJsonBase64Url as Base64URLString,
      this.peerSetup?.signature as Base64URLString,
    );
    console.log("decoded", decoded);
  }

  public async verifyWebCrypto() {
    const decoded = await verifyJwtWebCrypto(
      this.credIdHex as string,
      this.activeDid as string,
      this.peerSetup?.authenticatorData as ArrayBuffer,
      this.peerSetup?.challenge as Uint8Array,
      this.peerSetup?.clientDataJsonBase64Url as Base64URLString,
      this.peerSetup?.signature as Base64URLString,
    );
    console.log("decoded", decoded);
  }

  public async verifyMyJwt() {
    const did =
      "did:peer:0zKMFjvUgYrM1hXwDciYHiA9MxXtJPXnRLJvqoMNAKoDLX9pKMWLb3VDsgua1p2zW1xXRsjZSTNsfvMnNyMS7dB4k7NAhFwL3pXBrBXgyYJ9ri";
    const jwt =
      "eyJ0eXAiOiJKV0FOVCIsImFsZyI6IkVTMjU2In0.eyJBdXRoZW50aWNhdGlvbkRhdGFCNjRVUkwiOiJTWllONVlnT2pHaDBOQmNQWkhaZ1c0X2tycm1paGpMSG1Wenp1b01kbDJNRkFBQUFBQSIsIkNsaWVudERhdGFKU09OQjY0VVJMIjoiZXlKMGVYQmxJam9pZDJWaVlYVjBhRzR1WjJWMElpd2lZMmhoYkd4bGJtZGxJam9pWlhsS01sbDVTVFpsZVVwcVkyMVdhMXBYTlRCaFYwWnpWVE5XYVdGdFZtcGtRMGsyWlhsS1FWa3lPWFZrUjFZMFpFTkpOa2x0YURCa1NFSjZUMms0ZG1NeVRtOWFWekZvVEcwNWVWcDVTWE5KYTBJd1pWaENiRWxxYjJsU01td3lXbFZHYW1SSGJIWmlhVWx6U1cxU2JHTXlUbmxoV0VJd1lWYzVkVWxxYjJsalIydzJaVzFGYVdaWU1ITkpiV3hvWkVOSk5rMVVZM2hQUkZVMFRtcHJOVTFEZDJsaFdFNTZTV3B2YVZwSGJHdFBia0pzV2xoSk5rMUljRXhVVlZweFpHeFdibGRZU2s1TlYyaFpaREJTYW1GV2JFbGhWVVUxVkZob1dXUkZjRkZYUnpWVFZFVndNbU5YT1U1VWEwWk1ZakJTVFZkRWJIZFRNREZZVkVkSmVsWnJVbnBhTTFab1RWaEJlV1ZzWTNobFJtaFRZekp3WVZVeFVrOWpNbG95VkZjMVQyVlZNVlJPTWxKRFRrZHpNMVJyUm05U2JtUk5UVE5DV1ZGdVNrTlhSMlExVjFWdk5XTnRhMmxtVVNJc0ltOXlhV2RwYmlJNkltaDBkSEE2THk5c2IyTmhiR2h2YzNRNk9EQTRNQ0lzSW1OeWIzTnpUM0pwWjJsdUlqcG1ZV3h6WlgwIiwiaWF0IjoxNzE4NTg2OTkyLCJpc3MiOiJkaWQ6cGVlcjowektNRmp2VWdZck0xaFh3RGNpWUhpQTlNeFh0SlBYblJMSnZxb01OQUtvRExYOXBLTVdMYjNWRHNndWExcDJ6VzF4WFJzalpTVE5zZnZNbk55TVM3ZEI0azdOQWhGd0wzcFhCckJYZ3lZSjlyaSJ9.MEUCIQDJyCTbMPIFnuBoW3FYnlgtDEIHZ2OrkCEvqVnHU7kJDQIgVxjBjfW1TwQfcSOYwK8Z7AdCWGJlyxtLEsrnPif7caE";
    const pieces = jwt.split(".");
    const payload = JSON.parse(Buffer.from(pieces[1], "base64").toString());
    const authData = Buffer.from(payload["AuthenticationDataB64URL"], "base64");
    const clientJSON = Buffer.from(
      payload["ClientDataJSONB64URL"],
      "base64",
    ).toString();
    const clientData = JSON.parse(clientJSON);
    const challenge = clientData.challenge;
    const signatureB64URL = pieces[2];
    const decoded = await verifyJwtWebCrypto(
      this.credIdHex as string,
      did,
      authData,
      challenge,
      payload["ClientDataJSONB64URL"],
      signatureB64URL,
    );
    console.log("decoded", decoded);
  }
}
</script>