<template>
  <QuickNav selected="Contacts"></QuickNav>
  <section id="Content" class="p-6 pb-24">
    <!-- Breadcrumb -->
    <div class="mb-8">
      <h1
        id="ViewBreadcrumb"
        class="text-lg text-center font-light relative px-7"
      >
        <!-- Back -->
        <router-link
          :to="{ name: 'contacts' }"
          class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
          ><fa icon="chevron-left" class="fa-fw"></fa
        ></router-link>
      </h1>

      <h1 id="ViewHeading" class="text-4xl text-center font-light pt-4">
        Given with {{ contact?.name }}
      </h1>
    </div>

    <div class="flex justify-around">
      <span />
      <span class="justify-around">(Only 50 most recent)</span>
      <span />
    </div>

    <!-- Results List -->
    <table
      class="table-auto w-full border-t border-slate-300 text-sm sm:text-base text-center"
    >
      <thead class="bg-slate-100">
        <tr class="border-b border-slate-300">
          <th></th>
          <th class="px-1 py-2">From Them</th>
          <th></th>
          <th class="px-1 py-2">To Them</th>
        </tr>
      </thead>
      <tbody>
        <tr
          v-for="record in giveRecords"
          :key="record.id"
          class="border-b border-slate-300"
        >
          <td class="p-1 text-xs sm:text-sm text-left text-slate-500">
            {{ new Date(record.issuedAt).toLocaleString() }}
          </td>
          <td class="p-1">
            <span v-if="record.agentDid == contact.did">
              <div class="font-bold">
                {{ record.amount }} {{ record.unit }}
                <span v-if="record.amountConfirmed" title="Confirmed">
                  <fa icon="circle-check" class="text-green-600 fa-fw" />
                </span>
                <button v-else @click="confirm(record)" title="Unconfirmed">
                  <fa icon="circle" class="text-blue-600 fa-fw" />
                </button>
              </div>
              <div class="italic text-xs sm:text-sm text-slate-500">
                {{ record.description }}
              </div>
            </span>
          </td>
          <td class="p-1">
            <span v-if="record.agentDid == contact.did">
              <fa icon="arrow-left" class="text-slate-400 fa-fw" />
            </span>
            <span v-else>
              <fa icon="arrow-right" class="text-slate-400 fa-fw" />
            </span>
          </td>
          <td class="p-1">
            <span v-if="record.agentDid != contact.did">
              <div class="font-bold">
                {{ record.amount }} {{ record.unit }}
                <span v-if="record.amountConfirmed" title="Confirmed">
                  <fa icon="circle-check" class="text-green-600 fa-fw" />
                </span>
                <button
                  v-else
                  @click="cannotConfirmMessage()"
                  title="Unconfirmed"
                >
                  <fa icon="circle" class="text-slate-600 fa-fw" />
                </button>
              </div>
              <div class="italic text-xs sm:text-sm text-slate-500">
                {{ record.description }}
              </div>
            </span>
          </td>
        </tr>
      </tbody>
    </table>
  </section>
</template>

<script lang="ts">
import * as R from "ramda";

import { Component, Vue } from "vue-facing-decorator";
import { accountsDB, db } from "@/db/index";
import { Contact } from "@/db/tables/contacts";
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
import { accessToken, SimpleSigner } from "@/libs/crypto";
import {
  AgreeVerifiableCredential,
  GiveServerRecord,
  GiveVerifiableCredential,
  SCHEMA_ORG_CONTEXT,
} from "@/libs/endorserServer";
import * as didJwt from "did-jwt";
import { AxiosError } from "axios";
import QuickNav from "@/components/QuickNav.vue";
import { IIdentifier } from "@veramo/core";

interface Notification {
  group: string;
  type: string;
  title: string;
  text: string;
}

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

  activeDid = "";
  apiServer = "";
  contact: Contact | null = null;
  giveRecords: Array<GiveServerRecord> = [];
  numAccounts = 0;

  async beforeCreate() {
    await accountsDB.open();
    this.numAccounts = await accountsDB.accounts.count();
  }

  public async getIdentity(activeDid: string) {
    await accountsDB.open();
    const account = await accountsDB.accounts
      .where("did")
      .equals(activeDid)
      .first();
    const identity = JSON.parse(account?.identity || "null");

    if (!identity) {
      throw new Error(
        "Attempted to load Give records with no identity available.",
      );
    }
    return identity;
  }

  public async getHeaders(identity: IIdentifier) {
    const token = await accessToken(identity);
    const headers = {
      "Content-Type": "application/json",
      Authorization: "Bearer " + token,
    };
    return headers;
  }

  async created() {
    try {
      await db.open();
      const contactDid = this.$route.query.contactDid as string;
      this.contact = (await db.contacts.get(contactDid)) || null;

      const settings = await db.settings.get(MASTER_SETTINGS_KEY);
      this.activeDid = settings?.activeDid || "";
      this.apiServer = settings?.apiServer || "";

      if (this.activeDid && this.contact) {
        this.loadGives(this.activeDid, this.contact);
      }
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
    } catch (err: any) {
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error",
          text:
            err.userMessage ||
            "There was an error retrieving the latest sweet, sweet action.",
        },
        -1,
      );
    }
  }

  async loadGives(activeDid: string, contact: Contact) {
    try {
      const identity = await this.getIdentity(this.activeDid);
      let result: Array<GiveServerRecord> = [];
      const url =
        this.apiServer +
        "/api/v2/report/gives?agentDid=" +
        encodeURIComponent(identity.did) +
        "&recipientDid=" +
        encodeURIComponent(contact.did);
      const headers = await this.getHeaders(identity);
      const resp = await this.axios.get(url, { headers });
      if (resp.status === 200) {
        result = resp.data.data;
      } else {
        console.error(
          "Got bad response status & data of",
          resp.status,
          resp.data,
        );
        this.$notify(
          {
            group: "alert",
            type: "danger",
            title: "Error With Server",
            text: "Got an error retrieving your given time from the server.",
          },
          -1,
        );
      }

      const url2 =
        this.apiServer +
        "/api/v2/report/gives?agentDid=" +
        encodeURIComponent(contact.did) +
        "&recipientDid=" +
        encodeURIComponent(identity.did);
      const headers2 = await this.getHeaders(identity);
      const resp2 = await this.axios.get(url2, { headers: headers2 });
      if (resp2.status === 200) {
        result = R.concat(result, resp2.data.data);
      } else {
        console.error(
          "Got bad response status & data of",
          resp2.status,
          resp2.data,
        );
        this.$notify(
          {
            group: "alert",
            type: "danger",
            title: "Error With Server",
            text: "Got an error retrieving your given time from the server.",
          },
          -1,
        );
      }

      const sortedResult: Array<GiveServerRecord> = R.sort(
        (a, b) =>
          new Date(b.issuedAt).getTime() - new Date(a.issuedAt).getTime(),
        result,
      );
      this.giveRecords = sortedResult;
    } catch (error) {
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error With Server",
          text: error as string,
        },
        -1,
      );
    }
  }

  async confirm(record: GiveServerRecord) {
    // Make claim
    // I use clone here because otherwise it gets a Proxy object.
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const origClaim: GiveVerifiableCredential = R.clone(record.fullClaim);
    if (record.fullClaim["@context"] == SCHEMA_ORG_CONTEXT) {
      delete origClaim["@context"];
    }
    origClaim["identifier"] = record.handleId;
    const vcClaim: AgreeVerifiableCredential = {
      "@context": SCHEMA_ORG_CONTEXT,
      "@type": "AgreeAction",
      object: origClaim,
    };

    // Make a payload for the claim
    const vcPayload = {
      vc: {
        "@context": ["https://www.w3.org/2018/credentials/v1"],
        type: ["VerifiableCredential"],
        credentialSubject: vcClaim,
      },
    };

    // Create a signature using private key of identity
    const identity = await this.getIdentity(this.activeDid);
    if (identity.keys[0].privateKeyHex !== null) {
      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
      const privateKeyHex: string = identity.keys[0].privateKeyHex!;
      const signer = await SimpleSigner(privateKeyHex);
      const alg = undefined;
      // Create a JWT for the request
      const vcJwt: string = await didJwt.createJWT(vcPayload, {
        alg: alg,
        issuer: identity.did,
        signer: signer,
      });

      // Make the xhr request payload
      const payload = JSON.stringify({ jwtEncoded: vcJwt });
      const url = this.apiServer + "/api/v2/claim";
      const token = await accessToken(identity);
      const headers = {
        "Content-Type": "application/json",
        Authorization: "Bearer " + token,
      };

      try {
        const resp = await this.axios.post(url, payload, { headers });
        if (resp.data?.success) {
          record.amountConfirmed = origClaim.object?.amountOfThisGood || 1;
        }
      } catch (error) {
        let userMessage = "There was an error. See logs for more info.";
        const serverError = error as AxiosError;
        if (serverError) {
          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: "Error With Server",
            text: userMessage,
          },
          -1,
        );
      }
    }
  }

  cannotConfirmMessage() {
    this.$notify(
      {
        group: "alert",
        type: "danger",
        title: "Not Allowed",
        text: "Only the recipient can confirm final receipt.",
      },
      -1,
    );
  }
}
</script>

<style>
/*
  Tooltip, generated on "title" attributes on "fa" icons
  Kudos to https://www.w3schools.com/css/css_tooltip.asp
*/
/* Tooltip container */
.tooltip {
  position: relative;
  display: inline-block;
  border-bottom: 1px dotted black; /* If you want dots under the hoverable text */
}

/* Tooltip text */
.tooltip .tooltiptext {
  visibility: hidden;
  width: 200px;
  background-color: black;
  color: #fff;
  text-align: center;
  padding: 5px 0;
  border-radius: 6px;

  position: absolute;
  z-index: 1;
}

/* Show the tooltip text when you mouse over the tooltip container */
.tooltip:hover .tooltiptext {
  visibility: visible;
}
.tooltip:hover .tooltiptext-left {
  visibility: visible;
}
</style>
