<template>
  <QuickNav selected="Contacts"></QuickNav>
  <section id="Content" class="p-6 pb-24">
    <h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8">
      Given with {{ contact?.name }}
    </h1>

    <!-- 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>
    <AlertMessage
      :alertTitle="alertTitle"
      :alertMessage="alertMessage"
    ></AlertMessage>
  </section>
</template>

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

import { Component, Vue } from "vue-facing-decorator";
import { accountsDB, db } from "@/db";
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 AlertMessage from "@/components/AlertMessage";
import QuickNav from "@/components/QuickNav";

@Component({ components: { AlertMessage, QuickNav } })
export default class ContactsView extends Vue {
  activeDid = "";
  apiServer = "";
  contact: Contact | null = null;
  giveRecords: Array<GiveServerRecord> = [];
  alertTitle = "";
  alertMessage = "";
  numAccounts = 0;

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

  public async getIdentity(activeDid) {
    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) {
    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);
      }
    } catch (err) {
      this.alertTitle = "Error";
      this.alertMessage =
        err.userMessage ||
        "There was an error retrieving the latest sweet, sweet action.";
    }
  }

  async loadGives(activeDid: string, contact: Contact) {
    try {
      const identity = await this.getIdentity(this.activeDid);
      let result = [];
      const url =
        this.apiServer +
        "/api/v2/report/gives?agentDid=" +
        encodeURIComponent(identity.did) +
        "&recipientDid=" +
        encodeURIComponent(contact.did);
      const headers = 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.alertTitle = "Error With Server";
        this.alertMessage =
          "Got an error retrieving your given time from the server.";
      }

      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.alertTitle = "Error With Server";
        this.alertMessage =
          "Got an error retrieving your given time from the server.";
      }

      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.alertTitle = "Error With Server";
      this.alertMessage = error as string;
    }
  }

  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.alertTitle = "Error With Server";
        this.alertMessage = userMessage;
      }
    }
  }

  cannotConfirmMessage() {
    this.alertTitle = "Not Allowed";
    this.alertMessage = "Only the recipient can confirm final receipt.";
  }
}
</script>

<style>
/* Tooltip from 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>