<template>
  <!-- QUICK NAV -->
  <nav id="QuickNav" class="fixed bottom-0 left-0 right-0 bg-slate-200 z-50">
    <ul class="flex text-2xl p-2 gap-2">
      <!-- Home Feed -->
      <li class="basis-1/5 rounded-md text-slate-500">
        <router-link :to="{ name: 'home' }" class="block text-center py-3 px-1">
          <fa icon="house-chimney" class="fa-fw"></fa>
        </router-link>
      </li>
      <!-- Search -->
      <li class="basis-1/5 rounded-md text-slate-500">
        <router-link
          :to="{ name: 'discover' }"
          class="block text-center py-3 px-1"
        >
          <fa icon="magnifying-glass" class="fa-fw"></fa>
        </router-link>
      </li>
      <!-- Projects -->
      <li class="basis-1/5 rounded-md text-slate-500">
        <router-link
          :to="{ name: 'projects' }"
          class="block text-center py-3 px-1"
        >
          <fa icon="folder-open" class="fa-fw"></fa>
        </router-link>
      </li>
      <!-- Contacts -->
      <li class="basis-1/5 rounded-md text-slate-500">
        <router-link
          :to="{ name: 'contacts' }"
          class="block text-center py-3 px-1"
        >
          <fa icon="users" class="fa-fw"></fa>
        </router-link>
      </li>
      <!-- Profile -->
      <li class="basis-1/5 rounded-md bg-slate-400 text-white">
        <router-link
          :to="{ name: 'account' }"
          class="block text-center py-3 px-1"
        >
          <fa icon="circle-user" class="fa-fw"></fa>
        </router-link>
      </li>
    </ul>
  </nav>

  <!-- CONTENT -->
  <section id="Content" class="p-6 pb-24">
    <!-- Heading -->
    <h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8">
      Your Identity
    </h1>

    <div class="flex justify-between py-2">
      <span />
      <span>
        <router-link
          :to="{ name: 'help' }"
          class="text-xs uppercase bg-blue-500 text-white px-1.5 py-1 rounded-md ml-1"
        >
          Help
        </router-link>
      </span>
    </div>

    <!-- Registration notice -->
    <!-- We won't show any loading indicator; we'll just pop the message in once we know they need it. -->
    <div
      v-if="!loadingLimits && !limits?.nextWeekBeginDateTime"
      class="bg-amber-200 text-amber-900 border-amber-500 border-dashed border text-center rounded-md overflow-hidden px-4 py-3 mb-4"
    >
      <p class="mb-4">
        <b>Note:</b> Before you can publicly announce a new project or time
        commitment, a friend needs to register you.
      </p>
      <router-link
        :to="{ name: 'contact-qr' }"
        class="inline-block text-md uppercase bg-amber-600 text-white px-4 py-2 rounded-md"
      >
        Share Your Info
      </router-link>
    </div>

    <!-- Identity Details -->
    <div class="bg-slate-100 rounded-md overflow-hidden px-4 py-3 mb-4">
      <h2 class="text-xl font-semibold mb-2">{{ firstName }} {{ lastName }}</h2>

      <div class="text-slate-500 text-sm font-bold">ID</div>
      <div class="text-sm text-slate-500 flex justify-start items-center mb-1">
        <code class="truncate">{{ activeDid }}</code>
        <button
          @click="
            doCopyTwoSecRedo(activeDid, () => (showDidCopy = !showDidCopy))
          "
          class="ml-2"
        >
          <fa icon="copy" class="text-slate-400 fa-fw"></fa>
        </button>
        <span v-show="showDidCopy">Copied!</span>
        <span class="whitespace-nowrap ml-4">
          <router-link
            :to="{ name: 'contact-qr' }"
            class="text-xs uppercase bg-slate-500 text-white px-1.5 py-1 rounded-md ml-1"
          >
            <fa icon="qrcode" class="fa-fw"></fa>
          </router-link>
        </span>
      </div>

      <div class="text-slate-500 text-sm font-bold">Public Key (base 64)</div>
      <div class="text-sm text-slate-500 flex justify-start items-center mb-1">
        <code class="truncate">{{ publicBase64 }}</code>
        <button
          @click="
            doCopyTwoSecRedo(publicBase64, () => (showB64Copy = !showB64Copy))
          "
          class="ml-2"
        >
          <fa icon="copy" class="text-slate-400 fa-fw"></fa>
        </button>
        <span v-show="showB64Copy">Copied!</span>
      </div>

      <div class="text-slate-500 text-sm font-bold">Public Key (hex)</div>
      <div class="text-sm text-slate-500 flex justify-start items-center mb-1">
        <code class="truncate">{{ publicHex }}</code>
        <button
          @click="
            doCopyTwoSecRedo(publicHex, () => (showPubCopy = !showPubCopy))
          "
          class="ml-2"
        >
          <fa icon="copy" class="text-slate-400 fa-fw"></fa>
        </button>
        <span v-show="showPubCopy">Copied!</span>
      </div>

      <div class="text-slate-500 text-sm font-bold">Derivation Path</div>
      <div class="text-sm text-slate-500 flex justify-start items-center mb-1">
        <code class="truncate">{{ derivationPath }}</code>
        <button
          @click="
            doCopyTwoSecRedo(derivationPath, () => (showDerCopy = !showDerCopy))
          "
          class="ml-2"
        >
          <fa icon="copy" class="text-slate-400 fa-fw"></fa>
        </button>
        <span v-show="showDerCopy">Copied!</span>
      </div>
    </div>

    <router-link
      :to="{ name: 'new-edit-account' }"
      class="block text-center text-lg font-bold uppercase bg-blue-600 text-white px-2 py-3 rounded-md mb-8"
    >
      Edit Identity
    </router-link>

    <h3 class="text-sm uppercase font-semibold mb-3">Data</h3>

    <router-link
      :to="{ name: 'seed-backup' }"
      href=""
      class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-2"
    >
      Backup Identifier Seed
    </router-link>
    <a
      class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-6"
      @click="exportDatabase()"
    >
      Download Settings & Contacts (excluding Identifier Data)
    </a>
    <a ref="downloadLink" />

    <!-- QR code popup -->
    <dialog id="dlgQR" class="backdrop:bg-black/75 rounded-md">
      <form method="dialog">
        <div class="text-slate-500 text-center">
          <b>ID:</b> <code>did:peer:kl45kj41lk451kl3</code>
        </div>
        <img src="/img/sample-qr-code.png" class="w-full mb-3" />

        <button
          value="cancel"
          class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-2"
        >
          Copy to Clipboard
        </button>
        <button
          value="cancel"
          class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md"
        >
          Close
        </button>
      </form>
    </dialog>

    <h3 class="text-sm uppercase font-semibold mb-3">Advanced</h3>

    <label
      for="toggleShowAmounts"
      class="flex items-center cursor-pointer mb-6"
      @click="handleChange"
    >
      <!-- toggle -->
      <div class="relative">
        <!-- input -->
        <input
          type="checkbox"
          v-model="showContactGives"
          name="showContactGives"
          class="sr-only"
        />
        <!-- line -->
        <div class="block bg-slate-500 w-14 h-8 rounded-full"></div>
        <!-- dot -->
        <div
          class="dot absolute left-1 top-1 bg-slate-400 w-6 h-6 rounded-full transition"
        ></div>
      </div>
      <!-- label -->
      <div class="ml-2">Show amounts given with contacts</div>
    </label>

    <div class="flex py-2">
      <button class="text-center text-md text-blue-500" @click="checkLimits()">
        Check Limits
      </button>
      <!-- show spinner if loading limits -->
      <div v-if="loadingLimits" class="ml-2">
        Checking... <fa icon="spinner" class="fa-spin"></fa>
      </div>
      <div class="ml-2">
        {{ limitsMessage }}
      </div>
      <div v-if="!!limits?.nextWeekBeginDateTime" class="px-9">
        <span class="font-bold">Rate Limits</span>
        <p>
          You have done {{ limits.doneClaimsThisWeek }} claims out of
          {{ limits.maxClaimsPerWeek }} for this week. Your claims counter
          resets at {{ readableTime(limits.nextWeekBeginDateTime) }}
        </p>
        <p>
          You have done {{ limits.doneRegistrationsThisMonth }} registrations
          out of {{ limits.maxRegistrationsPerMonth }} for this month. Your
          registrations counter resets at
          {{ readableTime(limits.nextMonthBeginDateTime) }}
        </p>
      </div>
    </div>

    <div class="flex py-2">
      Claim Server
      <input
        type="text"
        class="block w-full rounded border border-slate-400 px-3 py-2"
        v-model="apiServerInput"
      />
      <button
        v-if="apiServerInput != apiServer"
        class="px-4 rounded bg-red-500 border border-slate-400"
        @click="onClickSaveApiServer()"
      >
        <fa icon="floppy-disk" class="fa-fw" color="white"></fa>
      </button>
      <button
        class="px-4 rounded bg-slate-200 border border-slate-400"
        @click="setApiServerInput(Constants.PROD_ENDORSER_API_SERVER)"
      >
        Use Prod
      </button>
      <button
        class="px-4 rounded bg-slate-200 border border-slate-400"
        @click="setApiServerInput(Constants.TEST_ENDORSER_API_SERVER)"
      >
        Use Test
      </button>
      <button
        class="px-4 rounded bg-slate-200 border border-slate-400"
        @click="setApiServerInput(Constants.LOCAL_ENDORSER_API_SERVER)"
      >
        Use Local
      </button>
    </div>

    <div v-if="numAccounts > 0" class="flex py-2">
      Switch Identifier
      <span v-for="accountNum in numAccounts" :key="accountNum">
        <button class="text-blue-500 px-2" @click="switchAccount(accountNum)">
          #{{ accountNum }}
        </button>
      </span>
    </div>

    <div>
      <button class="text-blue-500">
        <router-link
          :to="{ name: 'statistics' }"
          class="block text-center py-3"
        >
          See Achievements & Statistics
        </router-link>
      </button>
    </div>
    <AlertMessage
      :alertTitle="alertTitle"
      :alertMessage="alertMessage"
    ></AlertMessage>
  </section>
</template>

<script lang="ts">
import "dexie-export-import";
import * as R from "ramda";
import { Component, Vue } from "vue-facing-decorator";
import { useClipboard } from "@vueuse/core";

import { AppString } from "@/constants/app";
import { db, accountsDB } from "@/db";
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
import { accessToken } from "@/libs/crypto";
import { AxiosError } from "axios/index";
import AlertMessage from "@/components/AlertMessage";

// eslint-disable-next-line @typescript-eslint/no-var-requires
const Buffer = require("buffer/").Buffer;

interface RateLimits {
  doneClaimsThisWeek: string;
  doneRegistrationsThisMonth: string;
  maxClaimsPerWeek: string;
  maxRegistrationsPerMonth: string;
  nextMonthBeginDateTime: string;
  nextWeekBeginDateTime: string;
}

@Component({ components: { AlertMessage } })
export default class AccountViewView extends Vue {
  Constants = AppString;

  activeDid = "";
  apiServer = "";
  apiServerInput = "";
  derivationPath = "";
  firstName = "";
  lastName = "";
  numAccounts = 0;
  publicHex = "";
  publicBase64 = "";
  limits: RateLimits | null = null;
  limitsMessage = "";
  loadingLimits = true; // might as well now that we do it on mount, to avoid flashing the registration message
  showContactGives = false;

  showDidCopy = false;
  showDerCopy = false;
  showB64Copy = false;
  showPubCopy = false;

  // call fn, copy text to the clipboard, then redo fn after 2 seconds
  doCopyTwoSecRedo(text, fn) {
    fn();
    useClipboard()
      .copy(text)
      .then(() => setTimeout(fn, 2000));
  }

  handleChange() {
    this.showContactGives = !this.showContactGives;
    this.updateShowContactAmounts();
  }

  readableTime(timeStr: string) {
    return timeStr.substring(0, timeStr.indexOf("T"));
  }

  // 'created' hook runs when the Vue instance is first created
  async created() {
    // Uncomment this to register this user on the test server.
    // To manage within the vue devtools browser extension https://devtools.vuejs.org/
    // assign this to a class variable, eg. "registerThisUser = testServerRegisterUser",
    // select a component in the extension, and enter in the console: $vm.ctx.registerThisUser()
    //testServerRegisterUser();

    try {
      await db.open();
      const settings = await db.settings.get(MASTER_SETTINGS_KEY);
      this.activeDid = settings?.activeDid || "";
      this.apiServer = settings?.apiServer || "";
      this.apiServerInput = settings?.apiServer || "";
      this.firstName = settings?.firstName || "";
      this.lastName = settings?.lastName || "";
      this.showContactGives = !!settings?.showContactGivesInline;

      await accountsDB.open();
      const accounts = await accountsDB.accounts.toArray();
      this.numAccounts = accounts.length;
      const account = R.find((acc) => acc.did === this.activeDid, accounts);
      const identity = JSON.parse(account?.identity || "null");
      if (!identity) {
        throw new Error("No identity found.");
      }
      this.publicHex = identity.keys[0].publicKeyHex;
      this.publicBase64 = Buffer.from(this.publicHex, "hex").toString("base64");
      this.derivationPath = identity.keys[0].meta.derivationPath;

      db.settings.update(MASTER_SETTINGS_KEY, {
        activeDid: identity.did,
      });
      this.checkLimits();
    } catch (err) {
      this.alertMessage =
        "Clear your cache and start over (after data backup).";
      console.error("Telling user to clear cache at page create because:", err);
      this.alertTitle = "Error Creating Account";
    }
  }

  public async updateShowContactAmounts() {
    try {
      await db.open();
      db.settings.update(MASTER_SETTINGS_KEY, {
        showContactGivesInline: this.showContactGives,
      });
    } catch (err) {
      this.alertMessage =
        "Clear your cache and start over (after data backup).";
      console.error(
        "Telling user to clear cache after contact setting update because:",
        err
      );
      this.alertTitle = "Error Updating Contact Setting";
    }
  }

  public async exportDatabase() {
    try {
      const blob = await db.export({ prettyJson: true });
      const url = URL.createObjectURL(blob);

      const downloadAnchor = this.$refs.downloadLink as HTMLAnchorElement;
      downloadAnchor.href = url;
      downloadAnchor.download = db.name + "-backup.json";
      downloadAnchor.click();

      URL.revokeObjectURL(url);

      this.alertTitle = "Download Started";
      this.alertMessage = "See your downloads directory for the backup.";
    } catch (error) {
      this.alertTitle = "Export Error";
      this.alertMessage = "See console logs for more info.";
      console.error("Export Error:", error);
    }
  }

  async checkLimits() {
    this.loadingLimits = true;
    this.limitsMessage = "";

    const url = this.apiServer + "/api/report/rateLimits";
    await accountsDB.open();
    const accounts = await accountsDB.accounts.toArray();
    const account = R.find((acc) => acc.did === this.activeDid, accounts);
    const identity = JSON.parse(account?.identity || "null");
    if (!identity) {
      throw new Error("No identity found.");
    }
    const token = await accessToken(identity);
    const headers = {
      "Content-Type": "application/json",
      Authorization: "Bearer " + token,
    };

    try {
      const resp = await this.axios.get(url, { headers });
      // axios throws an exception on a 400
      if (resp.status === 200) {
        this.limits = resp.data;
      }
    } catch (error: unknown) {
      const serverError = error as AxiosError;

      console.error("Bad response retrieving limits: ", serverError);
      // Anybody know how to access items inside "response.data" without this?
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      const data: any = serverError.response?.data;
      this.limitsMessage = data?.error?.message || "Bad server response.";
    }

    this.loadingLimits = false;
  }

  async switchAccount(accountNum: number) {
    await accountsDB.open();
    const accounts = await accountsDB.accounts.toArray();
    const account = accounts[accountNum - 1];

    await db.open();
    db.settings.update(MASTER_SETTINGS_KEY, {
      activeDid: account.did,
    });

    this.activeDid = account.did;
    this.derivationPath = account.derivationPath;
    this.publicHex = account.publicKeyHex;
    this.publicBase64 = Buffer.from(this.publicHex, "hex").toString("base64");
  }

  public showContactGivesClassNames() {
    return {
      "bg-slate-900": !this.showContactGives,
      "bg-green-600": this.showContactGives,
    };
  }

  async onClickSaveApiServer() {
    await db.open();
    db.settings.update(MASTER_SETTINGS_KEY, {
      apiServer: this.apiServerInput,
    });
    this.apiServer = this.apiServerInput;
  }

  setApiServerInput(value) {
    this.apiServerInput = value;
  }

  // This same popup code is in many files.
  alertMessage = "";
  alertTitle = "";
}
</script>
