<template>
  <QuickNav />
  <!-- CONTENT -->
  <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
    <!-- Sub View Heading -->
    <div id="SubViewHeading" class="flex gap-4 items-start mb-8">
      <h1 class="grow text-xl text-center font-semibold leading-tight">
        Verifiable Claim Details
      </h1>

      <!-- Back -->
      <a
        class="order-first text-lg text-center leading-none p-1"
        @click="$router.go(-1)"
      >
        <font-awesome icon="chevron-left" class="block text-center w-[1em]" />
      </a>

      <!-- Help button -->
      <router-link
        :to="{ name: 'help' }"
        class="block ms-auto text-sm text-center text-white bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] p-1.5 rounded-full"
      >
        <font-awesome icon="question" class="block text-center w-[1em]" />
      </router-link>
    </div>

    <!-- Details -->
    <div class="bg-slate-100 rounded-md overflow-hidden px-4 py-3 mb-4 w-full">
      <div class="block flex gap-4 overflow-hidden w-full">
        <div class="w-full">
          <div class="flex columns-3">
            <h2 class="text-md font-bold w-full">
              {{
                serverUtil.capitalizeAndInsertSpacesBeforeCaps(
                  veriClaim.claimType || "",
                )
              }}
              <button
                v-if="canEditClaim"
                title="Edit"
                data-testId="editClaimButton"
                @click="onClickEditClaim"
              >
                <font-awesome
                  icon="pen"
                  class="text-sm text-blue-500 ml-2 mb-1"
                />
              </button>
            </h2>
            <div class="flex justify-center w-full">
              <router-link
                v-if="veriClaim.id"
                :to="'/claim-cert/' + encodeURIComponent(veriClaim.id)"
                class="text-blue-500 mt-2"
                title="View Printable Certificate"
              >
                <font-awesome
                  icon="square"
                  class="text-white bg-yellow-500 p-1"
                />
              </router-link>
              <button
                v-if="veriClaim.id"
                class="text-blue-500 ml-2 mt-2"
                title="Copy Printable Certificate Link"
                aria-label="Copy printable certificate link"
                @click="
                  copyTextToClipboard(
                    'A link to the certificate page',
                    `${APP_SERVER}/deep-link/claim-cert/${veriClaim.id}`,
                  )
                "
              >
                <font-awesome icon="link" class="text-yellow-500 p-1" />
              </button>
            </div>
            <!-- show link icon to copy this URL to the clipboard -->
            <div class="flex justify-end w-full">
              <button
                title="Copy Link"
                aria-label="Copy page link"
                @click="
                  copyTextToClipboard('A link to this page', windowDeepLink)
                "
              >
                <font-awesome icon="link" class="text-slate-500" />
              </button>
            </div>
          </div>
          <div class="text-sm overflow-hidden">
            <div
              data-testId="description"
              class="flex items-start gap-2 overflow-hidden"
            >
              <font-awesome
                icon="message"
                class="fa-fw text-slate-400 flex-shrink-0 mt-1"
              />
              <vue-markdown
                :source="claimDescription"
                class="markdown-content flex-1 min-w-0"
              />
            </div>
            <div class="overflow-hidden text-ellipsis">
              <font-awesome icon="user" class="fa-fw text-slate-400" />
              {{ didInfo(veriClaim.issuer) }}
            </div>
            <div class="overflow-hidden text-ellipsis">
              <font-awesome icon="calendar" class="fa-fw text-slate-400" />
              Recorded
              {{ formattedIssueDate }}
            </div>
            <div v-if="claimImage" class="flex justify-center">
              <a :href="claimImage" target="_blank">
                <img :src="claimImage" class="h-24 rounded-xl" />
              </a>
            </div>

            <div v-if="veriClaim.claimType === 'PlanAction'" class="mt-4">
              <router-link
                :to="'/project/' + encodeURIComponent(veriClaim.handleId)"
                class="text-blue-500 mt-2"
              >
                Go to Project page
              </router-link>
            </div>

            <!-- Fullfills Links -->
            <div class="mt-4 empty:hidden">
              <!-- fullfills links for a give -->
              <div v-if="detailsForGive?.fulfillsPlanHandleId">
                <router-link
                  :to="
                    '/project/' +
                    encodeURIComponent(detailsForGive?.fulfillsPlanHandleId)
                  "
                  class="text-blue-500 mt-2"
                >
                  This fulfills a bigger plan
                  <font-awesome
                    icon="arrow-up-right-from-square"
                    class="fa-fw"
                  />
                </router-link>
              </div>

              <!-- Show offer fulfillment if this give fulfills an offer -->
              <div v-if="detailsForGiveOfferFulfillment?.offerHandleId">
                <!-- router-link to /claim/ only changes URL path -->
                <a
                  class="text-blue-500 mt-4 cursor-pointer"
                  @click="
                    showDifferentClaimPage(
                      detailsForGiveOfferFulfillment.offerHandleId,
                    )
                  "
                >
                  This fulfills
                  {{
                    serverUtil.capitalizeAndInsertSpacesBeforeCapsWithAPrefix(
                      detailsForGiveOfferFulfillment.offerType || "Offer",
                    )
                  }}
                  <font-awesome
                    icon="arrow-up-right-from-square"
                    class="fa-fw"
                  />
                </a>
              </div>

              <!-- fullfills links for an offer -->
              <div v-if="detailsForOffer?.fulfillsPlanHandleId">
                <router-link
                  :to="
                    '/project/' +
                    encodeURIComponent(detailsForOffer?.fulfillsPlanHandleId)
                  "
                  class="text-blue-500 mt-4"
                >
                  Offered to a bigger plan
                  <font-awesome
                    icon="arrow-up-right-from-square"
                    class="fa-fw"
                  />
                </router-link>
              </div>

              <!-- Providers -->
              <div v-if="providersForGive?.length > 0">
                <span>Other assistance provided by:</span>
                <ul class="ml-4">
                  <li
                    v-for="provider of providersForGive"
                    :key="provider.identifier"
                    class="list-disc ml-4"
                  >
                    <div class="flex gap-4">
                      <div class="grow overflow-hidden">
                        <a
                          class="text-blue-500 mt-4 cursor-pointer"
                          @click="handleProviderClick(provider)"
                        >
                          an activity
                          <font-awesome
                            icon="arrow-up-right-from-square"
                            class="fa-fw"
                          />
                        </a>
                      </div>
                    </div>
                  </li>
                </ul>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
    <div class="mt-2">
      <font-awesome icon="comment" class="text-slate-400" />
      {{ issuerName }} posted that.
    </div>
    <div class="mt-8">
      <button
        v-if="veriClaim.claimType === 'Offer'"
        class="col-span-1 block w-fit text-center text-md px-1.5 py-2 rounded-md"
        :class="
          libsUtil.canFulfillOffer(veriClaim, isRegistered)
            ? // eslint-disable-next-line max-len
              'bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white'
            : 'bg-gray-300 text-gray-500 cursor-pointer opacity-50'
        "
        @click="handleAffirmDeliveryClick()"
      >
        Affirm Delivery
        <font-awesome
          icon="hand-holding-heart"
          :class="
            libsUtil.canFulfillOffer(veriClaim, isRegistered)
              ? 'ml-2 text-white cursor-pointer'
              : 'ml-2 text-gray-500 cursor-pointer'
          "
        />
      </button>
    </div>
    <GiftedDialog
      ref="customGiveDialog"
      :initial-giver-entity-type="'person'"
      :initial-recipient-entity-type="projectInfo ? 'project' : 'person'"
      :to-project-id="
        detailsForGive?.fulfillsPlanHandleId ||
        detailsForOffer?.fulfillsPlanHandleId ||
        ''
      "
    />

    <div v-if="libsUtil.isGiveAction(veriClaim)">
      <div class="flex columns-3">
        <button
          v-if="
            libsUtil.isGiveRecordTheUserCanConfirm(
              isRegistered,
              veriClaim,
              activeDid,
              confirmerIdList,
            )
          "
          class="col-span-1 bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mt-2 px-4 py-2 rounded-md"
          @click="confirmConfirmClaim()"
        >
          Confirm
          <font-awesome
            icon="circle-check"
            class="ml-2 text-white cursor-pointer"
          />
        </button>
        <h2 v-else class="font-bold uppercase text-xl mt-2">Confirmations</h2>

        <span class="mt-0.5 px-4 py-2">
          <router-link
            v-if="libsUtil.isGiveAction(veriClaim)"
            :to="'/confirm-gift/' + encodeURIComponent(veriClaim.id)"
            class="col-span-1 text-blue-500"
            data-testId="confirmGiftLink"
          >
            Details...
          </router-link>
        </span>
      </div>

      <div class="mt-2">
        {{ confirmationStatusText }}
      </div>

      <div v-if="totalConfirmers() > 0">
        <div
          v-if="
            confirmerIdList.length === 0 && confsVisibleToIdList.length === 0
          "
        >
          Nobody that you know confirmed this claim, nor do they have any
          confirmers in their network.
        </div>

        <div
          v-if="confirmerIdList.length === 0 && confsVisibleToIdList.length > 0"
        >
          <!-- Only show if this person has links to confirmers (below). -->
          Nobody that you know has issued or confirmed this claim.
        </div>
        <div v-if="confirmerIdList.length > 0">
          The following people have confirmed this claim.
          <ul class="ml-4">
            <li
              v-for="confirmerId in confirmerIdList"
              :key="confirmerId"
              class="list-disc ml-4"
            >
              <div class="flex gap-4">
                <div class="grow overflow-hidden">
                  <div class="text-sm">
                    {{ didInfo(confirmerId) }}
                    <span v-if="!serverUtil.isEmptyOrHiddenDid(confirmerId)">
                      <router-link
                        :to="{
                          path: '/did/' + encodeURIComponent(confirmerId),
                        }"
                        class="text-blue-500"
                      >
                        <font-awesome
                          icon="arrow-up-right-from-square"
                          class="fa-fw"
                        />
                      </router-link>
                    </span>
                  </div>
                </div>
              </div>
            </li>
          </ul>
        </div>

        <!--
         Never need to show this message:
         "Nobody that you know can see someone who has confirmed this claim."

         If there is nobody in the confirmerIdList then we'll show the combined "nobody" message.
         If there is somebody in the confirmerIdList then that's all they need to show.
         -->

        <!-- Now show anyone linked to confirmers. -->
        <div v-if="confsVisibleToIdList.length > 0">
          The following people can connect you with people who have issued or
          confirmed this claim.
          <ul class="ml-4">
            <li
              v-for="confsVisibleTo in confsVisibleToIdList"
              :key="confsVisibleTo"
              class="list-disc ml-4"
            >
              <div class="flex gap-4">
                <div class="grow overflow-hidden">
                  <div class="text-sm">
                    {{ didInfo(confsVisibleTo) }}
                    <span v-if="!serverUtil.isEmptyOrHiddenDid(confsVisibleTo)">
                      <router-link
                        :to="{
                          path: '/did/' + encodeURIComponent(confsVisibleTo),
                        }"
                        class="text-blue-500"
                      >
                        <font-awesome
                          icon="arrow-up-right-from-square"
                          class="fa-fw"
                        />
                      </router-link>
                    </span>
                  </div>
                </div>
              </div>
            </li>
          </ul>
        </div>
      </div>

      <!-- explain if user cannot confirm -->
      <!-- Note that these conditions are mirrored in userCanConfirm(). -->
      <div v-if="confirmerIdList.includes(activeDid)">
        You have confirmed this claim.
      </div>
      <div v-else-if="veriClaim.issuer == activeDid">
        You cannot confirm this because you issued this claim, so you already
        count as confirming it.
      </div>
      <div v-else-if="serverUtil.containsHiddenDid(veriClaim.claim)">
        You cannot confirm this because it contains hidden identifiers.
      </div>
    </div>

    <!-- 
    Note that a similar section is found in ConfirmGiftView.vue, and kinda in HiddenDidDialog.vue
    -->
    <h2
      class="font-bold uppercase text-xl text-blue-500 mt-8 cursor-pointer"
      @click="showVeriClaimDump = !showVeriClaimDump"
    >
      Details
      <font-awesome v-if="showVeriClaimDump" icon="chevron-up" />
      <font-awesome v-else icon="chevron-right" />
    </h2>
    <div v-if="showVeriClaimDump">
      <div v-if="isFullyHidden" class="mb-2">
        Some of the details are not visible to you; they show as "HIDDEN". They
        are not visible to any of your direct contacts, either.
        <span v-if="canShare">
          You can ask one of your contacts to take a look and see if their
          contacts can see more details:
          <a class="text-blue-500" @click="onClickShareClaim()"
            >click to send them this page info</a
          >
          and see if they can make an introduction. Someone is connected to
          people closer to them; if you don't know who to ask, try the person
          who registered you.
        </span>
        <span v-else>
          You can ask one of your contacts to take a look and see if their
          contacts can see more details:
          <a
            class="text-blue-500"
            @click="copyTextToClipboard('A link to this page', windowDeepLink)"
            >click to copy this page info</a
          >
          and see if they can make an introduction. Someone is connected to
          people closer to them; if you don't know who to ask, try the person
          who registered you.
        </span>
      </div>

      <div v-if="hasPartialVisibility">
        Some of the details are not visible to you but they are visible to some
        of your contacts.
        <span v-if="canShare">
          If you'd like an introduction,
          <a class="text-blue-500" @click="onClickShareClaim()"
            >click to share the information with them and ask if they'll tell
            you more about the participants.</a
          >
        </span>
        <span v-else>
          If you'd like an introduction,
          <a
            class="text-blue-500"
            @click="copyTextToClipboard('A link to this page', windowDeepLink)"
            >share this page with them and ask if they'll tell you more about
            about the participants.</a
          >
        </span>

        <div
          v-for="(visibleDidPath, index) of Object.keys(veriClaimDidsVisible)"
          :key="index"
          class="list-disc p-4"
        >
          <div class="text-sm">
            <font-awesome icon="minus" class="fa-fw" />
            The {{ visibleDidPath }} is visible to:
          </div>
          <div class="ml-12 p-1">
            <ul>
              <li
                v-for="(visDid, idx2) of veriClaimDidsVisible[visibleDidPath]"
                :key="idx2"
                class="list-disc"
              >
                <div class="text-sm mt-2">
                  <span>
                    {{ didInfo(visDid) }}
                    <span v-if="!serverUtil.isEmptyOrHiddenDid(visDid)">
                      <router-link
                        :to="{
                          path: '/did/' + encodeURIComponent(visDid),
                        }"
                        class="text-blue-500"
                      >
                        <font-awesome
                          icon="arrow-up-right-from-square"
                          class="fa-fw"
                        />
                      </router-link>
                    </span>
                    <span v-if="veriClaim.publicUrls?.[visDid]"
                      >, found at&nbsp;<a
                        :href="veriClaim.publicUrls?.[visDid]"
                        target="_blank"
                        class="text-blue-500"
                      >
                        <font-awesome icon="globe" class="fa-fw" />
                        {{ extractDomain(veriClaim.publicUrls[visDid] || "") }}
                      </a>
                    </span>
                  </span>
                </div>
              </li>
            </ul>
          </div>
        </div>
      </div>
      <span v-if="isEditedGlobalId" class="mt-2">
        This record is an edited version. The latest version is shown.
      </span>
      <br />
      <!-- Keep the dump contents directly between > and < to avoid weird spacing. -->
      <pre
        v-if="showVeriClaimDump"
        class="text-sm overflow-x-scroll bg-slate-100 px-4 py-3 rounded-md"
        >{{ veriClaimDump }}</pre
      >

      <h2 class="text-xl mt-8 mb-2">Full Claim</h2>
      <p class="mb-4">
        The full claim includes the claim as it was originally issued, including
        the signature (ie. the proof of issuance by that person).
      </p>
      <div v-if="!fullClaim">
        <p v-if="fullClaimMessage" class="mb-4">
          {{ fullClaimMessage }}
        </p>
        <button
          v-else
          class="text-blue-500 cursor-pointer"
          @click="showFullClaim(veriClaim.id as string)"
        >
          <font-awesome icon="file-lines" class="fa-fw" />
          Load Full Claim Details
        </button>
      </div>
      <div v-else>
        <pre
          class="text-sm overflow-x-scroll bg-slate-100 px-4 py-3 rounded-md"
          >{{ fullClaimDump }}</pre
        >
      </div>

      <a
        :href="apiServer + '/api/claim/' + veriClaim.id"
        target="_blank"
        class="text-blue-500 cursor-pointer"
      >
        <font-awesome icon="file-lines" class="fa-fw" />
        <font-awesome icon="arrow-up-right-from-square" class="ml-1 fa-fw" />
        View on the Public Server
      </a>
    </div>
  </section>
</template>

<script lang="ts">
import { AxiosError } from "axios";
import * as yaml from "js-yaml";
import * as R from "ramda";
import { Component, Vue } from "vue-facing-decorator";
import VueMarkdown from "vue-markdown-render";
import { Router, RouteLocationNormalizedLoaded } from "vue-router";
import { copyToClipboard } from "../services/ClipboardService";

import { EmojiClaim, GenericVerifiableCredential } from "../interfaces";
import GiftedDialog from "../components/GiftedDialog.vue";
import QuickNav from "../components/QuickNav.vue";
import { NotificationIface } from "../constants/app";
import { Contact } from "../db/tables/contacts";
import * as serverUtil from "../libs/endorserServer";
import {
  GenericCredWrapper,
  OfferClaim,
  GiveActionClaim,
  ProviderInfo,
} from "../interfaces";
import * as libsUtil from "../libs/util";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
import { APP_SERVER } from "@/constants/app";

@Component({
  components: { GiftedDialog, QuickNav, VueMarkdown },
  mixins: [PlatformServiceMixin],
})
export default class ClaimView extends Vue {
  $notify!: (notification: NotificationIface, timeout?: number) => void;
  $route!: RouteLocationNormalizedLoaded;
  $router!: Router;

  activeDid = "";
  allMyDids: Array<string> = [];
  allContacts: Array<Contact> = [];
  apiServer = "";

  canShare = false;
  confirmerIdList: string[] = []; // list of DIDs that have confirmed this claim excluding the issuer
  confsVisibleErrorMessage = "";
  confsVisibleToIdList: string[] = []; // list of DIDs that can see any confirmer
  detailsForGive: {
    fulfillsPlanHandleId?: string;
    fulfillsType?: string;
    fulfillsHandleId?: string;
    fullClaim?: {
      fulfills?: Array<{
        "@type": string;
        identifier?: string;
      }>;
    };
  } | null = null;
  // Additional offer information extracted from the fulfills array
  detailsForGiveOfferFulfillment: {
    offerHandleId?: string;
    offerType?: string;
  } | null = null;
  detailsForOffer: { fulfillsPlanHandleId?: string } | null = null;
  // Project information for fulfillsPlanHandleId
  projectInfo: {
    name: string;
    imageUrl?: string;
    issuer: string;
  } | null = null;
  fullClaim = null;
  fullClaimDump = "";
  fullClaimMessage = "";
  isEditedGlobalId = false;
  isRegistered = false;
  issuerName = "";
  numConfsNotVisible = 0; // number of hidden DIDs in the confirmerIdList, minus the issuer if they aren't visible
  providersForGive: ProviderInfo[] = [];
  showIdCopy = false;
  showVeriClaimDump = false;
  veriClaim = serverUtil.BLANK_GENERIC_SERVER_RECORD;
  veriClaimDump = "";
  veriClaimDidsVisible: { [key: string]: string[] } = {};
  windowDeepLink = window.location.href; // changed in the setup for deep linking

  APP_SERVER = APP_SERVER;
  R = R;
  yaml = yaml;
  libsUtil = libsUtil;
  serverUtil = serverUtil;

  notify!: ReturnType<typeof createNotifyHelpers>;

  // =================================================
  // COMPUTED PROPERTIES
  // =================================================

  /**
   * Whether the current user can edit this claim
   */
  get canEditClaim(): boolean {
    return (
      ["GiveAction", "Offer", "PlanAction"].includes(
        this.veriClaim.claimType as string,
      ) && this.veriClaim.issuer === this.activeDid
    );
  }

  /**
   * The description to display for this claim
   */
  get claimDescription(): string {
    const claim = this.veriClaim.claim;

    // Handle Offer claims with itemOffered
    if (this.veriClaim.claimType === "Offer") {
      const offerClaim = claim as OfferClaim;
      return (
        offerClaim.itemOffered?.description || offerClaim.description || ""
      );
    }

    // Handle GiveAction claims
    if (this.veriClaim.claimType === "GiveAction") {
      const giveClaim = claim as GiveActionClaim;
      return giveClaim.description || "";
    }

    if (this.veriClaim.claimType === "Emoji") {
      return (claim as EmojiClaim).text || "";
    }

    // Fallback for other claim types
    return (claim as { description?: string })?.description || "";
  }

  /**
   * Formatted issue date for display
   */
  get formattedIssueDate(): string {
    return (
      this.veriClaim.issuedAt?.replace(/T/, " ").replace(/Z/, " UTC") || ""
    );
  }

  /**
   * Text describing the confirmation status
   */
  get confirmationStatusText(): string {
    const count = this.totalConfirmers();
    if (count === 0) return "Nobody has confirmed this.";
    if (count === 1) return "One person has confirmed this.";
    return `${count} people have confirmed this.`;
  }

  /**
   * Whether the claim is fully hidden from the user
   */
  get isFullyHidden(): boolean {
    return (
      serverUtil.containsHiddenDid(this.veriClaim) &&
      R.isEmpty(this.veriClaimDidsVisible)
    );
  }

  /**
   * Whether the claim has some visibility through contacts
   */
  get hasPartialVisibility(): boolean {
    return !R.isEmpty(this.veriClaimDidsVisible);
  }

  /**
   * The image URL for this claim if available
   */
  get claimImage(): string | undefined {
    const claim = this.veriClaim.claim;

    // Handle different claim types that might have images
    if (this.veriClaim.claimType === "Offer") {
      const offerClaim = claim as OfferClaim;
      return offerClaim.image;
    }

    if (this.veriClaim.claimType === "GiveAction") {
      const giveClaim = claim as GiveActionClaim;
      return giveClaim.image;
    }

    // Fallback for other claim types
    return (claim as { image?: string })?.image;
  }

  resetThisValues() {
    this.confirmerIdList = [];
    this.confsVisibleErrorMessage = "";
    this.confsVisibleToIdList = [];
    this.detailsForGive = null;
    this.detailsForOffer = null;
    this.detailsForGiveOfferFulfillment = null;
    this.projectInfo = null;
    this.fullClaim = null;
    this.fullClaimDump = "";
    this.fullClaimMessage = "";
    this.isEditedGlobalId = false;
    this.numConfsNotVisible = 0;
    this.providersForGive = [];
    this.veriClaim = serverUtil.BLANK_GENERIC_SERVER_RECORD;
    this.veriClaimDump = "";
    this.veriClaimDidsVisible = {};
  }

  /**
   * Extract offer fulfillment information from the fulfills array
   */
  extractOfferFulfillment() {
    this.detailsForGiveOfferFulfillment = libsUtil.extractOfferFulfillment(
      this.detailsForGive?.fullClaim?.fulfills,
    );
  }

  // =================================================
  // UTILITY METHODS
  // =================================================

  /**
   * Handle provider click navigation
   */
  handleProviderClick(provider: ProviderInfo): void {
    if (provider.identifier.startsWith("did:")) {
      this.$router.push("/did/" + encodeURIComponent(provider.identifier));
    } else {
      this.showDifferentClaimPage(provider.identifier);
    }
  }

  /**
   * Extract domain from URL for display
   */
  extractDomain(url: string): string {
    return url.substring(url.indexOf("//") + 2);
  }

  async created() {
    this.notify = createNotifyHelpers(this.$notify);

    const settings = await this.$accountSettings();

    // 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 || "";
    this.allContacts = await this.$contacts();

    this.isRegistered = settings.isRegistered || false;

    try {
      this.allMyDids = await libsUtil.retrieveAccountDids();
    } catch (error) {
      await this.$logAndConsole(
        "Error retrieving all account DIDs on home page:" + error,
        true,
      );
      this.notify.error(
        "See the Help page for problems with your personal data.",
      );
    }

    const claimId = this.$route.params.id as string;
    if (claimId) {
      await this.loadClaim(claimId, this.activeDid);
    } else {
      this.notify.error("No claim ID was provided.");
    }
    // Use production URL for sharing to avoid localhost issues in development
    this.windowDeepLink = `${APP_SERVER}/deep-link/claim/${claimId}`;

    this.canShare = !!navigator.share;
  }

  totalConfirmers() {
    return (
      this.numConfsNotVisible +
      this.confirmerIdList.length +
      this.confsVisibleToIdList.length
    );
  }

  // Isn't there a better way to make this available to the template?
  didInfo(did: string): string {
    return serverUtil.didInfo(
      did,
      this.activeDid,
      this.allMyDids,
      this.allContacts,
    );
  }

  async loadClaim(claimId: string, userDid: string) {
    const urlPath = libsUtil.isGlobalUri(claimId)
      ? "/api/claim/byHandle/"
      : "/api/claim/";
    const url = this.apiServer + urlPath + encodeURIComponent(claimId);
    const headers = await serverUtil.getHeaders(userDid);

    try {
      const resp = await this.axios.get(url, { headers });
      if (resp.status === 200) {
        this.veriClaim = resp.data;
        this.issuerName = this.didInfo(this.veriClaim.issuer);
        this.veriClaimDump = yaml.dump(this.veriClaim);
        this.veriClaimDidsVisible = libsUtil.findAllVisibleToDids(
          this.veriClaim,
          true,
        );
      } else {
        // actually, axios typically throws an error so we never get here
        await this.$logError("Error getting claim: " + JSON.stringify(resp));
        this.notify.error("There was a problem retrieving that claim.");
        return;
      }

      this.isEditedGlobalId = !this.veriClaim.handleId.endsWith(claimId);

      // retrieve more details on Give, Offer, or Plan
      if (this.veriClaim.claimType === "GiveAction") {
        const giveUrl =
          this.apiServer +
          "/api/v2/report/gives?handleId=" +
          encodeURIComponent(this.veriClaim.handleId as string);
        const giveHeaders = await serverUtil.getHeaders(userDid);
        const giveResp = await this.axios.get(giveUrl, {
          headers: giveHeaders,
        });
        if (giveResp.status === 200 && giveResp.data.data?.length > 0) {
          this.detailsForGive = giveResp.data.data[0];
          // Extract offer information from the fulfills array
          this.extractOfferFulfillment();
        } else {
          await this.$logError(
            "Error getting detailed give info: " + JSON.stringify(giveResp),
          );
        }

        // look for providers
        const providerUrl =
          this.apiServer +
          "/api/v2/report/providersToGive?handleId=" +
          encodeURIComponent(this.veriClaim.handleId as string);
        const providerHeaders = await serverUtil.getHeaders(userDid);
        const providerResp = await this.axios.get(providerUrl, {
          headers: providerHeaders,
        });
        // should be at least an empty array
        if (
          providerResp.status === 200 &&
          Array.isArray(providerResp.data.data)
        ) {
          this.providersForGive = providerResp.data.data;
        } else {
          await this.$logError(
            "Error getting give providers: " + JSON.stringify(giveResp),
          );
          this.notify.warning("Got error retrieving linked provider data.");
        }
      } else if (this.veriClaim.claimType === "Offer") {
        const offerUrl =
          this.apiServer +
          "/api/v2/report/offers?handleId=" +
          encodeURIComponent(this.veriClaim.handleId as string);
        const offerHeaders = await serverUtil.getHeaders(userDid);
        const offerResp = await this.axios.get(offerUrl, {
          headers: offerHeaders,
        });
        if (offerResp.status === 200) {
          this.detailsForOffer = offerResp.data.data[0];
        } else {
          await this.$logError(
            "Error getting detailed offer info: " + JSON.stringify(offerResp),
          );
          this.notify.warning("Got error retrieving linked offer data.");
        }
      }

      // Load project information if there's a fulfillsPlanHandleId
      const planHandleId =
        this.detailsForGive?.fulfillsPlanHandleId ||
        this.detailsForOffer?.fulfillsPlanHandleId;
      if (planHandleId) {
        await this.loadProjectInfo(planHandleId, userDid);
      }

      // retrieve the list of confirmers
      const confirmerInfo = await libsUtil.retrieveConfirmerIdList(
        this.apiServer,
        claimId,
        this.veriClaim.issuer,
        userDid,
      );
      if (confirmerInfo) {
        this.confirmerIdList = confirmerInfo.confirmerIdList;
        this.confsVisibleToIdList = confirmerInfo.confsVisibleToIdList;
        this.numConfsNotVisible = confirmerInfo.numConfsNotVisible;
      } else {
        this.confsVisibleErrorMessage =
          "Had problems retrieving confirmations.";
      }
    } catch (error: unknown) {
      const serverError = error as AxiosError;
      await this.$logError(
        "Error retrieving claim: " + JSON.stringify(serverError),
      );
      this.notify.error(
        "Something went wrong retrieving claim data.",
        TIMEOUTS.STANDARD,
      );
    }
  }

  async loadProjectInfo(planHandleId: string, userDid: string) {
    const url =
      this.apiServer +
      "/api/claim/byHandle/" +
      encodeURIComponent(planHandleId);
    const headers = await serverUtil.getHeaders(userDid);

    try {
      const resp = await this.axios.get(url, { headers });
      if (resp.status === 200) {
        this.projectInfo = {
          name: resp.data.claim?.name || "(no name)",
          imageUrl: resp.data.claim?.image,
          issuer: resp.data.issuer,
        };
      } else {
        await this.$logError(
          "Error getting project info: " + JSON.stringify(resp),
        );
      }
    } catch (error: unknown) {
      await this.$logError(
        "Error retrieving project info: " + JSON.stringify(error),
      );
    }
  }

  async showFullClaim(claimId: string) {
    const url =
      this.apiServer + "/api/claim/full/" + encodeURIComponent(claimId);
    const headers = await serverUtil.getHeaders(this.activeDid);

    try {
      const resp = await this.axios.get(url, { headers });
      if (resp.status === 200) {
        this.fullClaim = resp.data;
        this.fullClaimDump = yaml.dump(this.fullClaim);
      } else {
        // actually, axios typically throws an error so we never get here
        await this.$logError(
          "Error getting full claim: " + JSON.stringify(resp),
        );
        this.notify.error("There was a problem getting that claim.");
      }
    } catch (error: unknown) {
      await this.$logError(
        "Error retrieving full claim: " + JSON.stringify(error),
      );
      const serverError = error as AxiosError;
      if (serverError.response?.status === 403) {
        let issuerPhrase = "";
        const issuerContact = serverUtil.contactForDid(
          this.veriClaim.issuer,
          this.allContacts,
        );
        if (issuerContact?.name) {
          issuerPhrase +=
            "Ask " +
            issuerContact.name +
            " to show you the full claim details.";
        }
        if (
          this.confirmerIdList.length > 0 ||
          this.confsVisibleToIdList.length > 0
        ) {
          if (issuerContact?.name) {
            issuerPhrase +=
              "You could also ask someone in the Confirmations section to make an introduction.";
          } else {
            issuerPhrase +=
              "Ask someone in the Confirmations section to make an introduction.";
          }
        }
        this.fullClaimMessage =
          "You are not authorized to view the full contents of this claim." +
          issuerPhrase +
          " You might ask someone in your network -- like the person who registered you --" +
          " if they can find out more and make an introduction: " +
          " send them this page and see if they can make a connection for you.";
      } else {
        this.notify.error(
          "Something went wrong retrieving that claim.",
          TIMEOUTS.LONG,
        );
      }
    }
  }

  confirmConfirmClaim() {
    this.notify.confirm(
      "Do you personally confirm that this is true?",
      async () => {
        await this.confirmClaim();
      },
    );
  }

  // similar code is found in ProjectViewView
  async confirmClaim() {
    // similar logic is found in endorser-mobile
    const goodClaim = serverUtil.removeSchemaContext(
      serverUtil.removeVisibleToDids(
        serverUtil.addLastClaimOrHandleAsIdIfMissing(
          this.veriClaim.claim,
          this.veriClaim.id,
          this.veriClaim.handleId,
        ),
      ),
    );
    const confirmationClaim: GenericVerifiableCredential = {
      "@context": "https://schema.org",
      "@type": "AgreeAction",
      object: goodClaim,
    };
    const result = await serverUtil.createAndSubmitClaim(
      confirmationClaim,
      this.activeDid,
      this.apiServer,
      this.axios,
    );
    if (result.success) {
      this.notify.confirmationSubmitted();
    } else {
      await this.$logError(
        "Got error submitting the confirmation: " + JSON.stringify(result),
      );
      this.notify.error("There was a problem submitting the confirmation.");
    }
  }

  showDifferentClaimPage(claimId: string) {
    const route = {
      path: "/claim/" + encodeURIComponent(claimId),
    };
    (this.$router as Router).push(route).then(async () => {
      this.resetThisValues();
      await this.loadClaim(claimId, this.activeDid);
    });
  }

  handleAffirmDeliveryClick() {
    if (!this.isRegistered) {
      this.notify.error("You must be registered to affirm delivery.");
      return;
    }
    if (!libsUtil.canFulfillOffer(this.veriClaim, this.isRegistered)) {
      this.notify.error(
        "You cannot see all the information in this offer so you cannot affirm delivery.",
      );
      return;
    }
    this.openFulfillGiftDialog();
  }

  openFulfillGiftDialog() {
    const giverDid = libsUtil.offerGiverDid(
      this.veriClaim as GenericCredWrapper<OfferClaim>,
    );
    // Look up the giver contact to get their name
    const giverContact = serverUtil.contactForDid(giverDid, this.allContacts);
    const giver: libsUtil.GiverReceiverInputInfo = {
      did: giverDid,
      name: giverContact?.name || giverDid, // Use contact name if available, otherwise DID
    };

    // Determine recipient based on whether it's a project or person
    let recipient: libsUtil.GiverReceiverInputInfo | undefined;

    if (this.projectInfo) {
      // Recipient is a project
      recipient = {
        name: this.projectInfo.name,
        handleId: this.detailsForOffer?.fulfillsPlanHandleId,
        image: this.projectInfo.imageUrl,
      };
    } else {
      // Recipient is a person - we need to determine who that person is
      // For offers, the recipient is typically the person who made the offer
      const offerClaim = this.veriClaim.claim as OfferClaim;
      const recipientDid =
        offerClaim.recipient?.identifier || this.veriClaim.issuer;

      if (recipientDid) {
        const recipientContact = serverUtil.contactForDid(
          recipientDid,
          this.allContacts,
        );
        recipient = {
          did: recipientDid,
          name: recipientContact?.name || recipientDid,
        };
      }
    }

    // Extract offer information from the claim
    const offerClaim = this.veriClaim.claim as OfferClaim;
    const description =
      offerClaim.itemOffered?.description || offerClaim.description;
    const amount =
      offerClaim.includesObject?.amountOfThisGood?.toString() || "0";
    const unitCode = offerClaim.includesObject?.unitCode || "HUR";

    (this.$refs.customGiveDialog as GiftedDialog).open(
      giver,
      recipient,
      this.veriClaim.handleId,
      undefined, // prompt
      description,
      amount,
      unitCode,
    );
  }

  async copyTextToClipboard(name: string, text: string) {
    try {
      await copyToClipboard(text);
      this.notify.copied(name || "That");
    } catch (error) {
      this.$logAndConsole(
        `Error copying ${name || "content"} to clipboard: ${error}`,
        true,
      );
      this.notify.error(`Failed to copy ${name || "content"} to clipboard.`);
    }
  }

  onClickShareClaim() {
    this.copyTextToClipboard("A link to this page", this.windowDeepLink);
    window.navigator.share({
      title: "Help Connect Me",
      text: "I'm trying to find the people who recorded this. Can you help me?",
      url: this.windowDeepLink,
    });
  }

  async onClickEditClaim() {
    if (this.veriClaim.claimType === "GiveAction") {
      const route = {
        name: "gifted-details",
        query: {
          prevCredToEdit: JSON.stringify(this.veriClaim),
          destinationPathAfter:
            "/claim/" + encodeURIComponent(this.veriClaim.handleId),
        },
      };
      (this.$router as Router).push(route);
    } else if (this.veriClaim.claimType === "Offer") {
      const route = {
        name: "offer-details",
        query: {
          prevCredToEdit: JSON.stringify(this.veriClaim),
          destinationPathAfter:
            "/claim/" + encodeURIComponent(this.veriClaim.handleId),
        },
      };
      (this.$router as Router).push(route);
    } else if (this.veriClaim.claimType === "PlanAction") {
      const route = {
        name: "new-edit-project",
        query: { projectId: this.veriClaim.handleId },
      };
      (this.$router as Router).push(route);
    } else {
      await this.$logError(
        "Unrecognized claim type for edit: " + this.veriClaim.claimType,
      );
      this.notify.error(
        "This is an unrecognized claim type.",
        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>
