<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()"
        >
          <font-awesome icon="chevron-left" class="fa-fw"></font-awesome>
        </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
        class="font-bold capitalize bg-slate-900 text-white px-3 py-2 rounded-md mr-2"
        @click="
          $notify(
            {
              group: 'alert',
              type: 'toast',
              title: 'Toast',
              text: 'I\'m a toast. Without a timeout, I\'m stuck.',
            },
            5000,
          )
        "
      >
        Toast
      </button>

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

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

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

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

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

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

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

    <div class="mt-8">
      <h2 class="text-xl font-bold mb-4">SQL Operations</h2>
      <div class="flex gap-2 mt-2">
        <button
          class="text-sm text-blue-600 hover:text-blue-800 underline"
          @click="
            sqlQuery = 'SELECT * FROM sqlite_master WHERE type=\'table\';';
            executeSql();
          "
        >
          All Tables
        </button>
        <button
          class="text-sm text-blue-600 hover:text-blue-800 underline"
          @click="
            sqlQuery = 'SELECT * FROM accounts;';
            executeSql();
          "
        >
          Accounts
        </button>
        <button
          class="text-sm text-blue-600 hover:text-blue-800 underline"
          @click="
            sqlQuery = 'SELECT * FROM contacts;';
            executeSql();
          "
        >
          Contacts
        </button>
        <button
          class="text-sm text-blue-600 hover:text-blue-800 underline"
          @click="
            sqlQuery = 'SELECT * FROM settings;';
            executeSql();
          "
        >
          Settings
        </button>
      </div>
      <div>
        <textarea
          v-model="sqlQuery"
          class="w-full h-32 p-2 border border-gray-300 rounded-md font-mono"
          placeholder="Enter your SQL query here..."
        ></textarea>
      </div>
      <div class="mt-4">
        <button
          class="font-bold capitalize bg-slate-500 text-white px-3 py-2 rounded-md mr-2"
          @click="executeSql"
        >
          Execute
        </button>
      </div>
      <div v-if="sqlResult" class="mt-4">
        <h3 class="text-lg font-semibold mb-2">Result:</h3>
        <pre class="bg-gray-100 p-4 rounded-md overflow-x-auto">{{
          JSON.stringify(sqlResult, null, 2)
        }}</pre>
      </div>
    </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" data-testId="fileInput" @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"
        data-testId="fileUploadButton"
      >
        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
          class="font-bold capitalize bg-slate-500 text-white px-3 py-2 rounded-md mr-2"
          @click="register()"
        >
          Simplewebauthn
        </button>
      </div>

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

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

    <div class="mt-8">
      <h2 class="text-xl font-bold mb-4">Encryption & Decryption</h2>
      See console for more output.
      <div>
        <button
          class="font-bold capitalize bg-slate-500 text-white px-3 py-2 rounded-md mr-2"
          @click="testMessageEncryptionDecryption()"
        >
          Run Test for Message Encryption/Decryption
        </button>
        Result: {{ messageEncryptionTestResult }}
      </div>
      <div>
        <button
          class="font-bold capitalize bg-slate-500 text-white px-3 py-2 rounded-md mr-2"
          @click="testSimpleEncryptionDecryption()"
        >
          Run Test for Simple Encryption/Decryption
        </button>
        Result: {{ simpleEncryptionTestResult }}
      </div>
    </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 { Router } from "vue-router";

import QuickNav from "../components/QuickNav.vue";
import { AppString, NotificationIface, USE_DEXIE_DB } from "../constants/app";
import * as databaseUtil from "../db/databaseUtil";
import { db, retrieveSettingsForActiveAccount } from "../db/index";
import * as vcLib from "../libs/crypto/vc";
import * as cryptoLib from "../libs/crypto";

import {
  PeerSetup,
  verifyJwtP256,
  verifyJwtSimplewebauthn,
  verifyJwtWebCrypto,
} from "../libs/crypto/vc/passkeyDidPeer";
import {
  AccountKeyInfo,
  blobToBase64,
  retrieveAccountMetadata,
  registerAndSavePasskey,
  SHARED_PHOTO_BASE64_KEY,
} from "../libs/util";
import { logger } from "../utils/logger";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
import { Temp } from "@/db/tables/temp";
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;
  $router!: Router;

  // for encryption/decryption
  messageEncryptionTestResult?: boolean;
  simpleEncryptionTestResult?: boolean;

  // for file import
  fileName?: string;

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

  // for SQL operations
  sqlQuery = "";
  sqlResult: unknown = null;

  cryptoLib = cryptoLib;

  async mounted() {
    let settings = await databaseUtil.retrieveSettingsForActiveAccount();
    if (USE_DEXIE_DB) {
      settings = await retrieveSettingsForActiveAccount();
    }
    this.activeDid = settings.activeDid || "";
    this.userName = settings.firstName;

    const account = await retrieveAccountMetadata(this.activeDid);
    if (this.activeDid) {
      if (account) {
        this.credIdHex = account.passkeyCredIdHex as string;
      } else {
        alert("No account found for DID " + this.activeDid);
      }
    }
  }

  async uploadFile(event: Event) {
    const target = event.target as HTMLInputElement;
    inputFileNameRef.value = 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,
          });
          const blobB64 = await blobToBase64(blob);
          this.fileName = (file as File).name;

          const platformService = PlatformServiceFactory.getInstance();
          const tempQuery = await platformService.dbQuery(
            "SELECT * FROM temp WHERE id = ?",
            [SHARED_PHOTO_BASE64_KEY],
          );
          let temp = databaseUtil.mapQueryResultToValues(
            tempQuery,
          )?.[0] as Temp;
          if (temp) {
            await platformService.dbExec(
              "UPDATE temp SET blobB64 = ? WHERE id = ?",
              [blobB64, SHARED_PHOTO_BASE64_KEY],
            );
          } else {
            await platformService.dbExec(
              "INSERT INTO temp (id, blobB64) VALUES (?, ?)",
              [SHARED_PHOTO_BASE64_KEY, blobB64],
            );
          }
          if (USE_DEXIE_DB) {
            temp = (await db.temp.get(
              SHARED_PHOTO_BASE64_KEY,
            )) as unknown as Temp;
            if (temp) {
              await db.temp.update(SHARED_PHOTO_BASE64_KEY, { blobB64 });
            } else {
              await db.temp.add({ id: SHARED_PHOTO_BASE64_KEY, blobB64 });
            }
          }
        }
      };
      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 testMessageEncryptionDecryption() {
    this.messageEncryptionTestResult =
      await cryptoLib.testMessageEncryptionDecryption();
  }

  public async testSimpleEncryptionDecryption() {
    this.simpleEncryptionTestResult =
      await cryptoLib.testSimpleEncryptionDecryption();
  }

  public async createJwtSimplewebauthn() {
    const account: AccountKeyInfo | undefined = await retrieveAccountMetadata(
      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,
    );
    logger.log("simple jwt4url", this.jwt);
  }

  public async createJwtNavigator() {
    const account: AccountKeyInfo | undefined = await retrieveAccountMetadata(
      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,
    );
    logger.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,
    );
    logger.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,
    );
    logger.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,
    );
    logger.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,
    );
    logger.log("decoded", decoded);
  }

  async executeSql() {
    const platformService = PlatformServiceFactory.getInstance();
    try {
      const isSelect = this.sqlQuery.trim().toLowerCase().startsWith("select");
      if (isSelect) {
        this.sqlResult = await platformService.dbQuery(this.sqlQuery);
      } else {
        this.sqlResult = await platformService.dbExec(this.sqlQuery);
      }
      logger.log("Test SQL Result:", this.sqlResult);
    } catch (error) {
      logger.error("Test SQL Error:", error);
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "SQL Error",
          text: error instanceof Error ? error.message : String(error),
        },
        5000,
      );
    }
  }
}
</script>
