<template>
  <QuickNav selected="Contacts" />

  <section class="p-6 pb-24 max-w-3xl mx-auto">
    <!-- Header -->
    <div class="mb-8">
      <router-link
        :to="{ name: 'contacts' }"
        class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
      >
        <font-awesome icon="chevron-left" class="fa-fw" />
      </router-link>

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

    <!-- Info Messages -->
    <div class="text-center text-sm text-slate-600 mb-6 space-y-1">
      <p>(Only 50 most recent)</p>
      <p>
        (This does not include claims by them if they're not visible to you.)
      </p>
    </div>

    <!-- Transfer History Table -->
    <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 class="px-1 py-2">Date</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.jwtId"
          class="border-b border-slate-300"
        >
          <!-- Date -->
          <td class="p-1 text-xs sm:text-sm text-left text-slate-500">
            {{ new Date(record.issuedAt).toLocaleString() }}
          </td>

          <!-- From Them -->
          <td class="p-1">
            <div v-if="record.agentDid === contact?.did">
              <div class="font-bold">
                {{ displayAmount(record.unit, record.amount) }}
                <font-awesome
                  v-if="record.amountConfirmed"
                  icon="circle-check"
                  class="text-green-600 fa-fw"
                  title="Confirmed"
                />
                <button v-else title="Unconfirmed" @click="confirm(record)">
                  <font-awesome icon="circle" class="text-blue-600 fa-fw" />
                </button>
              </div>
              <div class="italic text-xs sm:text-sm text-slate-500">
                {{ record.description }}
              </div>
            </div>
          </td>

          <!-- Direction Arrow -->
          <td class="p-1">
            <font-awesome
              :icon="
                record.agentDid === contact?.did ? 'arrow-left' : 'arrow-right'
              "
              class="text-slate-400 fa-fw"
            />
          </td>

          <!-- To Them -->
          <td class="p-1">
            <div v-if="record.agentDid !== contact?.did">
              <div class="font-bold">
                {{ displayAmount(record.unit, record.amount) }}
                <font-awesome
                  v-if="record.amountConfirmed"
                  icon="circle-check"
                  class="text-green-600 fa-fw"
                  title="Confirmed"
                />
                <button
                  v-else
                  title="Unconfirmed"
                  @click="cannotConfirmMessage()"
                >
                  <font-awesome icon="circle" class="text-slate-600 fa-fw" />
                </button>
              </div>
              <div class="italic text-xs sm:text-sm text-slate-500">
                {{ record.description }}
              </div>
            </div>
          </td>
        </tr>
      </tbody>
    </table>
  </section>
</template>

<script lang="ts">
import { AxiosError, AxiosRequestHeaders } from "axios";
import * as R from "ramda";
import { Component, Vue } from "vue-facing-decorator";
import { RouteLocationNormalizedLoaded, Router } from "vue-router";

import QuickNav from "../components/QuickNav.vue";
import { NotificationIface } from "../constants/app";
import { PlatformServiceMixin } from "../utils/PlatformServiceMixin";
import { createNotifyHelpers, TIMEOUTS } from "../utils/notify";
import {
  NOTIFY_SETTINGS_RETRIEVAL_ERROR,
  NOTIFY_SERVER_RETRIEVAL_ERROR,
  NOTIFY_CONFIRMATION_RESTRICTION,
} from "../constants/notifications";
import { Contact } from "../db/tables/contacts";

import { GiveSummaryRecord, GiveActionClaim } from "../interfaces";
import { AgreeActionClaim } from "../interfaces/claims";
import {
  createEndorserJwtVcFromClaim,
  displayAmount,
  getHeaders,
  SCHEMA_ORG_CONTEXT,
} from "../libs/endorserServer";
import { retrieveAccountCount } from "../libs/util";

/**
 * Contact Amounts View Component
 * @author Matthew Raymer
 *
 * This component displays the transfer history between the current user and a specific contact.
 * It shows both incoming and outgoing transfers with confirmation status and allows users
 * to confirm unconfirmed transfers.
 *
 * Features:
 * - Displays transfer history in a table format
 * - Shows confirmation status for each transfer
 * - Allows confirmation of unconfirmed transfers
 * - Handles server communication for transfer data
 * - Provides error handling and user feedback
 *
 * Workflow:
 * 1. Component loads with contact DID from route query
 * 2. Fetches contact information from database via PlatformServiceMixin
 * 3. Loads transfer history from server API
 * 4. Displays transfers in chronological order
 * 5. Allows user to confirm unconfirmed transfers
 *
 * Transfer Types:
 * - From Them: Transfers received from the contact
 * - To Them: Transfers sent to the contact
 *
 * Confirmation Status:
 * - Confirmed: Transfer has been acknowledged by recipient
 * - Unconfirmed: Transfer pending confirmation
 * - Cannot Confirm: User is not the recipient of the transfer
 */
@Component({
  components: { QuickNav },
  mixins: [PlatformServiceMixin],
})
export default class ContactAmountssView extends Vue {
  /** Notification function injected by Vue */
  $notify!: (notification: NotificationIface, timeout?: number) => void;
  /** Current route instance */
  $route!: RouteLocationNormalizedLoaded;
  /** Router instance for navigation */
  $router!: Router;

  /** Notification helpers */
  notify!: ReturnType<typeof createNotifyHelpers>;

  /** Active user DID */
  activeDid = "";
  /** API server URL */
  apiServer = "";
  /** Current contact data */
  contact: Contact | null = null;
  /** Array of transfer records */
  giveRecords: Array<GiveSummaryRecord> = [];
  /** Number of user accounts */
  numAccounts = 0;

  /** Display amount utility function */
  displayAmount = displayAmount;

  /**
   * Component lifecycle hook that initializes account count
   */
  async beforeCreate() {
    this.numAccounts = await retrieveAccountCount();
  }

  /**
   * Component lifecycle hook that initializes the contact amounts view
   *
   * Workflow:
   * 1. Extracts contact DID from route query parameters
   * 2. Queries database for contact information via PlatformServiceMixin
   * 3. Retrieves user settings for active DID and API server
   * 4. Loads transfer history if both active DID and contact are available
   * 5. Handles errors with appropriate user notifications
   *
   * @throws Will not throw but notifies on errors
   * @emits Notification on database or settings errors
   */
  async created() {
    this.notify = createNotifyHelpers(this.$notify);

    try {
      const contactDid = this.$route.query["contactDid"] as string;
      const contact = await this.$getContact(contactDid);
      this.contact = contact;

      const settings = await this.$getMasterSettings();

      // Get activeDid from active_identity table (single source of truth)
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      const activeIdentity = await (this as any).$getActiveIdentity();
      this.activeDid = activeIdentity.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) {
      await this.$logError("Error retrieving settings or gives.");
      this.notify.error(
        err.userMessage || NOTIFY_SETTINGS_RETRIEVAL_ERROR.message,
        TIMEOUTS.LONG,
      );
    }
  }

  /**
   * Loads transfer history from the server API
   *
   * Fetches both incoming and outgoing transfers between the active user and contact,
   * then sorts them by date and updates the component state.
   *
   * @param activeDid The active user's DID
   * @param contact The contact to load transfers with
   * @throws Will not throw but notifies on server errors
   * @emits Notification on server communication errors
   */
  async loadGives(activeDid: string, contact: Contact) {
    try {
      let result: Array<GiveSummaryRecord> = [];
      const url =
        this.apiServer +
        "/api/v2/report/gives?agentDid=" +
        encodeURIComponent(this.activeDid) +
        "&recipientDid=" +
        encodeURIComponent(contact.did);
      const headers = await getHeaders(activeDid);
      const resp = await this.axios.get(url, { headers });
      if (resp.status === 200) {
        result = resp.data.data;
      } else {
        await this.$logError(
          `Got bad response status & data of ${resp.status} ${JSON.stringify(resp.data)}`,
        );
        this.notify.error(NOTIFY_SERVER_RETRIEVAL_ERROR.message, TIMEOUTS.LONG);
      }

      const url2 =
        this.apiServer +
        "/api/v2/report/gives?agentDid=" +
        encodeURIComponent(contact.did) +
        "&recipientDid=" +
        encodeURIComponent(this.activeDid);
      const headers2 = await getHeaders(activeDid);
      const resp2 = await this.axios.get(url2, { headers: headers2 });
      if (resp2.status === 200) {
        result = R.concat(result, resp2.data.data);
      } else {
        await this.$logError(
          `Got bad response status & data of ${resp2.status} ${JSON.stringify(resp2.data)}`,
        );
        this.notify.error(NOTIFY_SERVER_RETRIEVAL_ERROR.message, TIMEOUTS.LONG);
      }

      const sortedResult: Array<GiveSummaryRecord> = R.sort(
        (a, b) =>
          new Date(b.issuedAt).getTime() - new Date(a.issuedAt).getTime(),
        result,
      );
      this.giveRecords = sortedResult;
    } catch (error) {
      this.notify.error(error as string, TIMEOUTS.LONG);
    }
  }

  /**
   * Confirms an unconfirmed transfer by creating and submitting a confirmation claim
   *
   * Creates an AgreeAction claim for the transfer and submits it to the server
   * to confirm receipt of the transfer.
   *
   * @param record The transfer record to confirm
   * @throws Will not throw but notifies on server errors
   * @emits Notification on confirmation success or failure
   */
  async confirm(record: GiveSummaryRecord) {
    // Make claim
    // I use clone here because otherwise it gets a Proxy object.
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const origClaim: GiveActionClaim = R.clone(record.fullClaim);
    if (record.fullClaim["@context"] == SCHEMA_ORG_CONTEXT) {
      delete origClaim["@context"];
    }
    origClaim["identifier"] = record.handleId;
    const vcClaim: AgreeActionClaim = {
      "@context": SCHEMA_ORG_CONTEXT,
      "@type": "AgreeAction",
      object: origClaim,
    };

    const vcJwt: string = await createEndorserJwtVcFromClaim(
      this.activeDid,
      vcClaim,
    );

    // Make the xhr request payload
    const payload = JSON.stringify({ jwtEncoded: vcJwt });
    const url = this.apiServer + "/api/v2/claim";
    const headers = (await getHeaders(this.activeDid)) as AxiosRequestHeaders;

    try {
      const resp = await this.axios.post(url, payload, { headers });
      if (resp.data?.success) {
        record.amountConfirmed =
          (origClaim.object?.amountOfThisGood as number) || 1;
      }
    } catch (error) {
      let userMessage = "There was an error.";
      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.error(userMessage, TIMEOUTS.LONG);
    }
  }

  /**
   * Shows notification that user cannot confirm a transfer they didn't receive
   *
   * Only the recipient of a transfer can confirm its receipt.
   * This method notifies users when they try to confirm a transfer
   * that was sent to someone else.
   *
   * @emits Notification explaining confirmation restrictions
   */
  cannotConfirmMessage() {
    this.notify.error(
      NOTIFY_CONFIRMATION_RESTRICTION.message,
      TIMEOUTS.STANDARD,
    );
  }
}
</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>
