<template>
  <QuickNav />
  <TopMessage />

  <!-- CONTENT -->
  <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
    <!-- Breadcrumb -->
    <div id="ViewBreadcrumb">
      <h1 class="text-lg text-center font-light relative px-7">
        <!-- Back -->
        <button
          @click="$router.go(-1)"
          class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
        >
          <fa icon="chevron-left" class="fa-fw"></fa>
        </button>
        Idea
        <h2 class="text-xl font-semibold">
          {{ name }}
          <button
            v-if="activeDid === issuer || activeDid === agentDid"
            @click="onEditClick()"
            title="Edit"
            data-testId="editClaimButton"
          >
            <fa icon="pen" class="text-sm text-blue-500 ml-2 mb-1" />
          </button>
        </h2>
      </h1>
    </div>

    <!-- Project Details -->
    <div class="bg-slate-100 rounded-md overflow-hidden px-4 py-3 mt-4">
      <div>
        <div class="pb-4 flex gap-4">
          <div class="pt-1">
            <ProjectIcon
              :entityId="projectId"
              :iconSize="64"
              :imageUrl="imageUrl"
              :linkToFull="true"
              class="block border border-slate-300 rounded-md max-h-16 max-w-16"
            />
          </div>

          <div class="overflow-hidden">
            <div class="text-sm mb-3">
              <div class="truncate">
                <fa icon="user" class="fa-fw text-slate-400"></fa>
                {{
                  serverUtil.didInfo(issuer, activeDid, allMyDids, allContacts)
                }}
                <span v-if="!serverUtil.isEmptyOrHiddenDid(issuer)">
                  <button
                    @click="
                      libsUtil.doCopyTwoSecRedo(
                        issuer,
                        () => (showDidCopy = !showDidCopy),
                      )
                    "
                    class="ml-2 mr-2"
                  >
                    <fa icon="copy" class="text-slate-400 fa-fw"></fa>
                  </button>
                  <span v-show="showDidCopy">Copied DID</span>
                </span>
              </div>
              <div v-if="startTime">
                <fa icon="calendar" class="fa-fw text-slate-400"></fa>
                {{ startTime }}
              </div>
              <div v-if="latitude || longitude">
                <fa icon="location-dot" class="fa-fw text-slate-400"></fa>
                <a
                  :href="getOpenStreetMapUrl()"
                  target="_blank"
                  class="underline"
                  >Map View
                  <fa icon="arrow-up-right-from-square" class="fa-fw" />
                </a>
              </div>
              <div v-if="url">
                <fa icon="globe" class="fa-fw text-slate-400"></fa>
                <a :href="addScheme(url)" target="_blank" class="underline">
                  {{ domainForWebsite(this.url) }}
                  <fa icon="arrow-up-right-from-square" class="fa-fw" />
                </a>
              </div>
            </div>
          </div>
        </div>

        <div class="text-sm text-slate-500">
          <div v-if="!expanded">
            {{ truncatedDesc }}
            <a
              v-if="description.length >= truncateLength"
              @click="expandText"
              class="uppercase text-xs font-semibold text-slate-700"
              >... Read More</a
            >
          </div>
          <div v-else>
            {{ description }}
            <a
              @click="collapseText"
              class="uppercase text-xs font-semibold text-slate-700"
              >- Read Less</a
            >
          </div>
        </div>

        <a @click="onClickLoadClaim(projectId)" class="cursor-pointer">
          <fa icon="file-lines" class="pl-2 pt-1 text-blue-500" />
        </a>
      </div>
    </div>

    <div class="grid items-start grid-cols-1 sm:grid-cols-2 gap-4 mt-4">
      <div>
        <div
          v-if="fulfillersToThis.length > 0"
          class="bg-slate-100 px-4 py-3 rounded-md"
        >
          <h3 class="text-sm uppercase font-semibold mt-3">
            Projects That Contribute To This
          </h3>
          <!-- centering because long, wrapped project names didn't left align with blank or "text-left" -->
          <div class="text-center">
            <div v-for="plan in fulfillersToThis" :key="plan.handleId">
              <button
                @click="onClickLoadProject(plan.handleId)"
                class="text-blue-500"
              >
                {{ plan.name }}
              </button>
            </div>
            <div v-if="fulfillersToHitLimit" class="text-center">
              <button @click="loadPlanFulfillersTo()">Load More</button>
            </div>
          </div>
        </div>
      </div>

      <div>
        <div v-if="fulfilledByThis" class="bg-slate-100 px-4 py-3 rounded-md">
          <h3 class="text-sm uppercase font-semibold mb-3">
            Projects Getting Contributions From This
          </h3>
          <!-- centering because long, wrapped project names didn't left align with blank or "text-left" -->
          <div class="text-center">
            <button
              @click="onClickLoadProject(fulfilledByThis.handleId)"
              class="text-blue-500"
            >
              {{ fulfilledByThis.name }}
            </button>
          </div>
        </div>
      </div>
    </div>

    <div v-if="activeDid && isRegistered">
      <div class="text-center">
        <p class="mt-2 mt-4 text-center">Record a contribution from:</p>
      </div>
      <ul
        class="grid grid-cols-4 sm:grid-cols-5 md:grid-cols-6 gap-x-3 gap-y-5 text-center mb-5 mt-2"
      >
        <li @click="openGiftDialogToProject({ name: 'you', did: activeDid })">
          <fa icon="hand" class="fa-fw text-blue-500 text-5xl cursor-pointer" />
          <h3
            class="mt-5 text-xs text-blue-500 font-medium text-ellipsis whitespace-nowrap overflow-hidden cursor-pointer"
          >
            You
          </h3>
        </li>
        <li @click="openGiftDialogToProject()">
          <img
            src="../assets/blank-square.svg"
            class="mx-auto border border-blue-300 rounded-md mb-1 cursor-pointer"
          />
          <h3
            class="text-xs text-blue-500 italic font-medium text-ellipsis whitespace-nowrap overflow-hidden cursor-pointer"
          >
            Unnamed/Unknown
          </h3>
        </li>
        <li
          v-for="contact in allContacts.slice(0, 5)"
          :key="contact.did"
          @click="openGiftDialogToProject(contact)"
        >
          <EntityIcon
            :contact="contact"
            :iconSize="64"
            class="mx-auto border border-blue-300 rounded-md mb-1 cursor-pointer"
          />
          <h3
            class="text-xs text-blue-500 font-medium text-ellipsis whitespace-nowrap overflow-hidden cursor-pointer"
          >
            {{ contact.name || "(no name)" }}
          </h3>
        </li>
        <li>
          <span
            v-if="allContacts.length >= 5"
            @click="onClickAllContactsGifting()"
            class="flex align-bottom text-xs text-blue-500 mt-12 cursor-pointer"
          >
            ... or someone else...
          </span>
        </li>
      </ul>
      <GiftedDialog ref="giveDialogToThis" :toProjectId="this.projectId" />
    </div>

    <!-- Offers & Gifts to & from this -->
    <div class="grid items-start grid-cols-1 sm:grid-cols-3 gap-4 mt-4">
      <!-- First, offers on the left-->
      <div class="bg-slate-100 px-4 py-3 rounded-md">
        <div v-if="activeDid && isRegistered">
          <div class="text-center">
            <button
              data-testId="offerButton"
              @click="openOfferDialog()"
              class="block w-full bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white rounded-md"
            >
              Offer (maybe with conditions)...
            </button>
          </div>
        </div>
        <OfferDialog
          ref="customOfferDialog"
          :projectId="this.projectId"
          :projectName="this.name"
        />

        <h3 class="text-lg font-bold mb-3 mt-4">Offered To This Idea</h3>

        <div v-if="offersToThis.length === 0">
          (None yet. Wanna
          <span @click="openOfferDialog()" class="cursor-pointer text-blue-500"
            >offer something... especially if others join you</span
          >?)
        </div>

        <ul v-else class="text-sm border-t border-slate-300">
          <li
            v-for="offer in offersToThis"
            :key="offer.id"
            class="py-1.5 border-b border-slate-300"
          >
            <div class="flex justify-between gap-4">
              <span>
                <fa icon="user" class="fa-fw text-slate-400"></fa>
                {{
                  serverUtil.didInfo(
                    offer.offeredByDid,
                    activeDid,
                    allMyDids,
                    allContacts,
                  )
                }}
              </span>
              <span v-if="offer.amount" class="whitespace-nowrap">
                <fa
                  :icon="libsUtil.iconForUnitCode(offer.unit)"
                  class="fa-fw text-slate-400"
                />{{ offer.amount }}
              </span>
            </div>
            <div v-if="offer.objectDescription" class="text-slate-500">
              <fa icon="comment" class="fa-fw text-slate-400" />
              {{ offer.objectDescription }}
            </div>
            <div class="flex justify-between">
              <a
                @click="onClickLoadClaim(offer.jwtId as string)"
                class="cursor-pointer"
              >
                <fa icon="file-lines" class="pl-2 pt-1 text-blue-500" />
              </a>
              <a
                v-if="checkIsFulfillable(offer)"
                @click="onClickFulfillGiveToOffer(offer)"
              >
                <fa
                  icon="hand-holding-heart"
                  class="text-blue-500 cursor-pointer"
                />
              </a>
            </div>
          </li>
        </ul>
        <div v-if="offersHitLimit" class="text-center text-blue-500">
          <button @click="loadOffers()">Load More</button>
        </div>
      </div>

      <!-- Now, gives TO this project in the middle -->
      <!-- (similar to "FROM" gift display below) -->
      <div class="bg-slate-100 px-4 py-3 rounded-md" data-testId="gives-to">
        <div v-if="activeDid && isRegistered">
          <div class="text-center">
            <button
              @click="openGiftDialogToProject()"
              class="block w-full bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white rounded-md"
            >
              Given To This...
            </button>
          </div>
        </div>

        <h3 class="text-lg font-bold mb-3 mt-4">Given To This Idea</h3>

        <div v-if="givesToThis.length === 0">
          (None yet. If you've seen something, say something by clicking a
          contact above.)
        </div>

        <ul v-else class="text-sm border-t border-slate-300">
          <li
            v-for="give in givesToThis"
            :key="give.id"
            class="py-1.5 border-b border-slate-300"
          >
            <div class="flex justify-between gap-4">
              <span>
                <fa icon="user" class="fa-fw text-slate-400" />
                {{
                  serverUtil.didInfo(
                    give.agentDid,
                    activeDid,
                    allMyDids,
                    allContacts,
                  )
                }}
              </span>
              <span v-if="give.amount" class="whitespace-nowrap">
                <fa
                  :icon="libsUtil.iconForUnitCode(give.unit)"
                  class="fa-fw text-slate-400"
                />{{ give.amount }}
              </span>
            </div>
            <div class="text-slate-500">
              <fa icon="calendar" class="fa-fw text-slate-400" />
              {{ give.issuedAt?.substring(0, 10) }}
            </div>
            <div v-if="give.description" class="text-slate-500">
              <fa icon="comment" class="fa-fw text-slate-400" />
              {{ give.description }}
            </div>
            <div class="flex justify-between">
              <a @click="onClickLoadClaim(give.jwtId)">
                <fa icon="file-lines" class="text-blue-500 cursor-pointer" />
              </a>

              <a
                v-if="
                  checkIsConfirmable(give) &&
                  !recentlyCheckedAndUnconfirmableJwts.includes(give.jwtId)
                "
                @click="deepCheckConfirmable(give)"
              >
                <fa icon="circle-check" class="text-blue-500 cursor-pointer" />
              </a>
              <a v-else-if="checkingConfirmationForJwtId === give.jwtId">
                <fa icon="spinner" class="fa-spin-pulse" />
              </a>
              <a v-else @click="shallowNotifyWhyCannotConfirm(give)">
                <fa icon="circle-check" class="text-slate-500 cursor-pointer" />
              </a>
            </div>
            <div v-if="give.fullClaim.image" class="flex justify-center">
              <a :href="give.fullClaim.image" target="_blank">
                <img :src="give.fullClaim.image" class="h-24 mt-2 rounded-xl" />
              </a>
            </div>
          </li>
        </ul>
        <div v-if="givesHitLimit" class="text-center text-blue-500">
          <button @click="loadGives()">Load More</button>
        </div>
      </div>

      <!-- Finally, gives FROM this project on the right -->
      <!-- (similar to "TO" gift display above) -->
      <div class="bg-slate-100 px-4 py-3 rounded-md" data-testId="gives-from">
        <div v-if="activeDid && isRegistered">
          <div class="text-center">
            <button
              @click="openGiftDialogFromProject()"
              class="block w-full bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white rounded-md"
            >
              Given By This...
            </button>
          </div>
        </div>
        <GiftedDialog
          ref="giveDialogFromThis"
          :fromProjectId="this.projectId"
        />

        <h3 class="text-lg font-bold mb-3 mt-4">Benefitted By This</h3>

        <div v-if="givesProvidedByThis.length === 0">(None yet.)</div>

        <ul v-else class="text-sm border-t border-slate-300">
          <li
            v-for="give in givesProvidedByThis"
            :key="give.id"
            class="py-1.5 border-b border-slate-300"
          >
            <div class="flex justify-between gap-4">
              <span>
                {{
                  serverUtil.didInfo(
                    give.recipientDid,
                    activeDid,
                    allMyDids,
                    allContacts,
                  )
                }}
              </span>
              <span v-if="give.amount" class="whitespace-nowrap">
                <fa
                  :icon="libsUtil.iconForUnitCode(give.unit)"
                  class="fa-fw text-slate-400"
                />{{ give.amount }}
              </span>
            </div>
            <div class="text-slate-500">
              <fa icon="calendar" class="fa-fw text-slate-400" />
              {{ give.issuedAt?.substring(0, 10) }}
            </div>
            <div v-if="give.description" class="text-slate-500">
              <fa icon="comment" class="fa-fw text-slate-400" />
              {{ give.description }}
            </div>
            <div class="flex justify-between">
              <a @click="onClickLoadClaim(give.jwtId)">
                <fa icon="file-lines" class="text-blue-500 cursor-pointer" />
              </a>

              <a
                v-if="
                  checkIsConfirmable(give) &&
                  !recentlyCheckedAndUnconfirmableJwts.includes(give.jwtId)
                "
                @click="deepCheckConfirmable(give)"
              >
                <fa icon="circle-check" class="text-blue-500 cursor-pointer" />
              </a>
              <a v-else-if="checkingConfirmationForJwtId === give.jwtId">
                <fa icon="spinner" class="fa-spin-pulse" />
              </a>
              <a v-else @click="shallowNotifyWhyCannotConfirm(give)">
                <fa icon="circle-check" class="text-slate-500 cursor-pointer" />
              </a>
            </div>
            <div v-if="give.fullClaim.image" class="flex justify-center">
              <a :href="give.fullClaim.image" target="_blank">
                <img :src="give.fullClaim.image" class="h-24 mt-2 rounded-xl" />
              </a>
            </div>
          </li>
        </ul>
        <div v-if="givesProvidedByHitLimit" class="text-center">
          <button @click="loadGivesProvidedBy()">Load More</button>
        </div>
      </div>
    </div>
  </section>
</template>

<script lang="ts">
import { AxiosError } from "axios";
import { Component, Vue } from "vue-facing-decorator";
import { Router } from "vue-router";

import GiftedDialog from "@/components/GiftedDialog.vue";
import OfferDialog from "@/components/OfferDialog.vue";
import TopMessage from "@/components/TopMessage.vue";
import QuickNav from "@/components/QuickNav.vue";
import EntityIcon from "@/components/EntityIcon.vue";
import ProjectIcon from "@/components/ProjectIcon.vue";
import { NotificationIface } from "@/constants/app";
import { accountsDB, db, retrieveSettingsForActiveAccount } from "@/db/index";
import { Account } from "@/db/tables/accounts";
import { Contact } from "@/db/tables/contacts";
import * as libsUtil from "@/libs/util";
import {
  BLANK_GENERIC_SERVER_RECORD,
  GenericCredWrapper,
  getHeaders,
  GiveSummaryRecord,
  GiveVerifiableCredential,
  OfferSummaryRecord,
  OfferVerifiableCredential,
  PlanSummaryRecord,
} from "@/libs/endorserServer";
import * as serverUtil from "@/libs/endorserServer";

@Component({
  components: {
    EntityIcon,
    GiftedDialog,
    OfferDialog,
    ProjectIcon,
    QuickNav,
    TopMessage,
  },
})
export default class ProjectViewView extends Vue {
  $notify!: (notification: NotificationIface, timeout?: number) => void;

  activeDid = "";
  agentDid = "";
  allMyDids: Array<string> = [];
  allContacts: Array<Contact> = [];
  apiServer = "";
  checkingConfirmationForJwtId = "";
  description = "";
  expanded = false;
  fulfilledByThis: PlanSummaryRecord | null = null;
  fulfillersToThis: Array<PlanSummaryRecord> = [];
  fulfillersToHitLimit = false;
  givesToThis: Array<GiveSummaryRecord> = [];
  givesHitLimit = false;
  givesProvidedByThis: Array<GiveSummaryRecord> = [];
  givesProvidedByHitLimit = false;
  imageUrl = "";
  isRegistered = false;
  issuer = "";
  latitude = 0;
  longitude = 0;
  name = "";
  offersToThis: Array<OfferSummaryRecord> = [];
  offersHitLimit = false;
  projectId = ""; // handle ID
  recentlyCheckedAndUnconfirmableJwts: string[] = [];
  showDidCopy = false;
  startTime = "";
  truncatedDesc = "";
  truncateLength = 40;
  url = "";

  libsUtil = libsUtil;
  serverUtil = serverUtil;

  async created() {
    const settings = await retrieveSettingsForActiveAccount();
    this.activeDid = settings.activeDid || "";
    this.apiServer = settings.apiServer || "";
    this.allContacts = await db.contacts.toArray();
    this.isRegistered = !!settings.isRegistered;

    await accountsDB.open();
    const accounts = accountsDB.accounts;
    const accountsArr: Account[] = await accounts?.toArray();
    this.allMyDids = accountsArr.map((acc) => acc.did);

    const pathParam = window.location.pathname.substring("/project/".length);
    if (pathParam) {
      this.projectId = decodeURIComponent(pathParam);
    }
    this.loadProject(this.projectId, this.activeDid);
  }

  onEditClick() {
    const route = {
      name: "new-edit-project",
      query: { projectId: this.projectId },
    };
    (this.$router as Router).push(route);
  }

  // Isn't there a better way to make this available to the template?
  expandText() {
    this.expanded = true;
  }

  collapseText() {
    this.expanded = false;
  }

  async loadProject(projectId: string, userDid: string) {
    this.projectId = projectId;

    const url =
      this.apiServer + "/api/claim/byHandle/" + encodeURIComponent(projectId);
    const headers = await getHeaders(userDid);

    try {
      const resp = await this.axios.get(url, { headers });
      if (resp.status === 200) {
        const startTime = resp.data.claim?.startTime;
        if (startTime != null) {
          const startDateTime = new Date(startTime);
          this.startTime =
            startDateTime.toLocaleDateString() +
            " " +
            startDateTime.toLocaleTimeString();
        }
        this.agentDid = resp.data.claim?.agent?.identifier;
        this.imageUrl = resp.data.claim?.image;
        this.issuer = resp.data.issuer;
        this.name = resp.data.claim?.name || "(no name)";
        this.description = resp.data.claim?.description || "(no description)";
        this.truncatedDesc = this.description.slice(0, this.truncateLength);
        this.latitude = resp.data.claim?.location?.geo?.latitude || 0;
        this.longitude = resp.data.claim?.location?.geo?.longitude || 0;
        this.url = resp.data.claim?.url || "";
      } else {
        // actually, axios throws an error on 404 so we probably never get here
        console.error("Error getting project:", resp);
        this.$notify(
          {
            group: "alert",
            type: "danger",
            title: "Error",
            text: "There was a problem getting that project.",
          },
          5000,
        );
      }
    } catch (error: unknown) {
      console.error("Error retrieving project:", error);
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error",
          text: "Something went wrong retrieving that project.",
        },
        5000,
      );
    }

    this.loadGives();

    this.loadGivesProvidedBy();

    this.loadOffers();

    this.loadPlanFulfillersTo();

    const fulfilledByUrl =
      this.apiServer +
      "/api/v2/report/planFulfilledByPlan?planHandleId=" +
      encodeURIComponent(projectId);
    try {
      const resp = await this.axios.get(fulfilledByUrl, { headers });
      if (resp.status === 200) {
        this.fulfilledByThis = resp.data.data;
      } else {
        this.$notify(
          {
            group: "alert",
            type: "danger",
            title: "Error",
            text: "Failed to retrieve plans fulfilled by this project.",
          },
          5000,
        );
      }
    } catch (error: unknown) {
      const serverError = error as AxiosError;
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error",
          text: "Something went wrong retrieving plans fulfilled by this project.",
        },
        5000,
      );
      console.error(
        "Error retrieving plans fulfilled by this project:",
        serverError.message,
      );
    }
  }

  async loadGives() {
    const givesUrl =
      this.apiServer +
      "/api/v2/report/givesToPlans?planIds=" +
      encodeURIComponent(JSON.stringify([this.projectId]));
    let postfix = "";
    if (this.givesToThis.length > 0) {
      postfix =
        "&beforeId=" + this.givesToThis[this.givesToThis.length - 1].jwtId;
    }
    const givesInUrl = givesUrl + postfix;

    const headers = await getHeaders(this.activeDid);
    try {
      const resp = await this.axios.get(givesInUrl, { headers });
      if (resp.status === 200 && resp.data.data) {
        this.givesToThis = this.givesToThis.concat(resp.data.data);
        this.givesHitLimit = resp.data.hitLimit;
      } else {
        this.$notify(
          {
            group: "alert",
            type: "danger",
            title: "Error",
            text: "Failed to retrieve more gives to this project.",
          },
          5000,
        );
      }
    } catch (error: unknown) {
      const serverError = error as AxiosError;
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error",
          text: "Something went wrong retrieving more gives to this project.",
        },
        5000,
      );
      console.error(
        "Something went wrong retrieving more gives to this project:",
        serverError.message,
      );
    }
  }

  async loadOffers() {
    const offersUrl =
      this.apiServer +
      "/api/v2/report/offersToPlans?planIds=" +
      encodeURIComponent(JSON.stringify([this.projectId]));
    let postfix = "";
    if (this.offersToThis.length > 0) {
      postfix =
        "&beforeId=" + this.offersToThis[this.offersToThis.length - 1].jwtId;
    }
    const offersInUrl = offersUrl + postfix;

    const headers = await getHeaders(this.activeDid);
    try {
      const resp = await this.axios.get(offersInUrl, { headers });
      if (resp.status === 200 && resp.data.data) {
        this.offersToThis = this.offersToThis.concat(resp.data.data);
        this.offersHitLimit = resp.data.hitLimit;
      } else {
        this.$notify(
          {
            group: "alert",
            type: "danger",
            title: "Error",
            text: "Failed to retrieve more offers to this project.",
          },
          5000,
        );
      }
    } catch (error: unknown) {
      const serverError = error as AxiosError;
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error",
          text: "Something went wrong retrieving more offers to this project.",
        },
        5000,
      );
      console.error(
        "Something went wrong retrieving more offers to this project:",
        serverError.message,
      );
    }
  }

  async loadPlanFulfillersTo() {
    const fulfillsUrl =
      this.apiServer +
      "/api/v2/report/planFulfillersToPlan?planHandleId=" +
      encodeURIComponent(this.projectId);
    let postfix = "";
    if (this.fulfillersToThis.length > 0) {
      postfix =
        "&beforeId=" +
        this.fulfillersToThis[this.fulfillersToThis.length - 1].jwtId;
    }
    const fulfillsInUrl = fulfillsUrl + postfix;

    const headers = await getHeaders(this.activeDid);
    try {
      const resp = await this.axios.get(fulfillsInUrl, { headers });
      if (resp.status === 200) {
        this.fulfillersToThis = this.fulfillersToThis.concat(resp.data.data);
        this.fulfillersToHitLimit = resp.data.hitLimit;
      } else {
        this.$notify(
          {
            group: "alert",
            type: "danger",
            title: "Error",
            text: "Failed to retrieve more plans that fullfill this project.",
          },
          5000,
        );
      }
    } catch (error: unknown) {
      const serverError = error as AxiosError;
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error",
          text: "Something went wrong retrieving more plans that fulfull this project.",
        },
        5000,
      );
      console.error(
        "Something went wrong retrieving more plans that fulfill this project:",
        serverError.message,
      );
    }
  }

  async loadGivesProvidedBy() {
    const providedByUrl =
      this.apiServer +
      "/api/v2/report/givesProvidedBy?providerId=" +
      encodeURIComponent(this.projectId);
    let postfix = "";
    if (this.givesProvidedByThis.length > 0) {
      postfix =
        "&beforeId=" +
        this.givesProvidedByThis[this.givesProvidedByThis.length - 1].jwtId;
    }
    const providedByFullUrl = providedByUrl + postfix;

    const headers = await getHeaders(this.activeDid);
    try {
      const resp = await this.axios.get(providedByFullUrl, { headers });
      if (resp.status === 200) {
        this.givesProvidedByThis = this.givesProvidedByThis.concat(
          resp.data.data,
        );
        this.givesProvidedByHitLimit = resp.data.hitLimit;
      } else {
        this.$notify(
          {
            group: "alert",
            type: "danger",
            title: "Error",
            text: "Failed to retrieve gives that were provided by this project.",
          },
          5000,
        );
      }
    } catch (error: unknown) {
      const serverError = error as AxiosError;
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error",
          text: "Something went wrong retrieving gives that were provided by this project.",
        },
        5000,
      );
      console.error(
        "Something went wrong retrieving gives that were provided by this project:",
        serverError.message,
      );
    }
  }

  /**
   * Handle clicking on a project entry found in the list
   * @param id of the project
   **/
  async onClickLoadProject(projectId: string) {
    const route = {
      path: "/project/" + encodeURIComponent(projectId),
    };
    (this.$router as Router).push(route);
    this.loadProject(projectId, this.activeDid);
  }

  getOpenStreetMapUrl() {
    // Google URL is https://maps.google.com/?q=LAT,LONG
    return (
      "https://www.openstreetmap.org/?mlat=" +
      this.latitude +
      "&mlon=" +
      this.longitude +
      "#map=15/" +
      this.latitude +
      "/" +
      this.longitude
    );
  }

  openGiftDialogToProject(contact?: libsUtil.GiverReceiverInputInfo) {
    (this.$refs.giveDialogToThis as GiftedDialog).open(
      contact,
      undefined,
      undefined,
      (contact?.name || "Someone not named") + ` gave to this project`,
    );
  }

  openGiftDialogFromProject() {
    (this.$refs.giveDialogFromThis as GiftedDialog).open(
      undefined,
      { did: this.activeDid, name: "You" },
      undefined,
      `This project gave to you`,
    );
  }

  openOfferDialog() {
    (this.$refs.customOfferDialog as OfferDialog).open();
  }

  onClickAllContactsGifting() {
    const route = {
      name: "contact-gift",
      query: {
        projectId: this.projectId,
      },
    };
    (this.$router as Router).push(route);
  }

  onClickLoadClaim(jwtId: string) {
    const route = {
      path: "/claim/" + encodeURIComponent(jwtId),
    };
    (this.$router as Router).push(route);
  }

  checkIsFulfillable(offer: OfferSummaryRecord) {
    const offerRecord: GenericCredWrapper<OfferVerifiableCredential> = {
      ...BLANK_GENERIC_SERVER_RECORD,
      claim: offer.fullClaim,
      claimType: "Offer",
      issuer: offer.offeredByDid,
    };
    return libsUtil.canFulfillOffer(offerRecord);
  }

  onClickFulfillGiveToOffer(offer: OfferSummaryRecord) {
    const offerRecord: GenericCredWrapper<OfferVerifiableCredential> = {
      ...BLANK_GENERIC_SERVER_RECORD,
      claim: offer.fullClaim,
      issuer: offer.offeredByDid,
    };
    const giver: libsUtil.GiverReceiverInputInfo = {
      did: libsUtil.offerGiverDid(offerRecord),
    };
    (this.$refs.giveDialogToThis as GiftedDialog).open(
      giver,
      undefined,
      offer.handleId,
      "Given by " + (giver?.name || "someone not named"),
    );
  }

  // return an HTTPS URL if it's not a global URL
  addScheme(url: string) {
    if (!libsUtil.isGlobalUri(url)) {
      return "https://" + url;
    }
    return url;
  }

  // return just the domain for display, if possible
  domainForWebsite(url: string) {
    try {
      const hostname = new URL(url).hostname;
      if (!hostname) {
        // happens for non-http URLs
        return url;
      } else if (url.endsWith(hostname)) {
        // it's just the domain
        return hostname;
      } else {
        // there's more, but don't bother displaying the whole thing
        return hostname + "...";
      }
    } catch (error: unknown) {
      // must not be a valid URL
      return url;
    }
  }

  /**
   * @param confirmerIdList optional list of DIDs who confirmed; if missing, doesn't do a full server check
   */
  checkIsConfirmable(give: GiveSummaryRecord, confirmerIdList?: string[]) {
    const giveDetails: GenericCredWrapper<GiveVerifiableCredential> = {
      ...BLANK_GENERIC_SERVER_RECORD,
      claim: give.fullClaim,
      claimType: "GiveAction",
      issuer: give.issuerDid,
    };
    return libsUtil.isGiveRecordTheUserCanConfirm(
      this.isRegistered,
      giveDetails,
      this.activeDid,
      confirmerIdList,
    );
  }

  shallowNotifyWhyCannotConfirm(give: GiveSummaryRecord) {
    const confirmerIds = this.recentlyCheckedAndUnconfirmableJwts.includes(
      give.jwtId,
    )
      ? [this.activeDid]
      : [];
    libsUtil.notifyWhyCannotConfirm(
      this.$notify,
      this.isRegistered,
      "GiveAction",
      give,
      this.activeDid,
      confirmerIds,
    );
  }

  async deepCheckConfirmable(give: GiveSummaryRecord) {
    this.checkingConfirmationForJwtId = give.jwtId;
    const confirmerInfo: libsUtil.ConfirmerData | undefined =
      await libsUtil.retrieveConfirmerIdList(
        this.apiServer,
        give.jwtId,
        give.issuerDid,
        this.activeDid,
      );
    if (
      this.checkIsConfirmable(give, confirmerInfo?.confirmerIdList as string[])
    ) {
      this.confirmConfirmClaim(give);
    } else {
      this.recentlyCheckedAndUnconfirmableJwts = [
        ...this.recentlyCheckedAndUnconfirmableJwts,
        give.jwtId,
      ];
      libsUtil.notifyWhyCannotConfirm(
        this.$notify,
        this.isRegistered,
        "GiveAction",
        give,
        this.activeDid,
        confirmerInfo?.confirmerIdList as string[],
      );
    }
    this.checkingConfirmationForJwtId = "";
  }

  confirmConfirmClaim(give: GiveSummaryRecord) {
    this.$notify(
      {
        group: "modal",
        type: "confirm",
        title: "Confirm",
        text: "Do you personally confirm that this is true?",
        onYes: async () => {
          await this.confirmClaim(give);
        },
      },
      -1,
    );
  }

  // similar code is found in ClaimView
  async confirmClaim(give: GiveSummaryRecord) {
    // similar logic is found in endorser-mobile
    const goodClaim = serverUtil.removeSchemaContext(
      serverUtil.removeVisibleToDids(
        serverUtil.addLastClaimOrHandleAsIdIfMissing(
          give.fullClaim,
          give.jwtId,
          give.handleId,
        ),
      ),
    );
    const confirmationClaim: serverUtil.GenericVerifiableCredential = {
      "@context": "https://schema.org",
      "@type": "AgreeAction",
      object: goodClaim,
    };
    const result = await serverUtil.createAndSubmitClaim(
      confirmationClaim,
      this.activeDid,
      this.apiServer,
      this.axios,
    );
    if (result.type === "success") {
      this.$notify(
        {
          group: "alert",
          type: "success",
          title: "Success",
          text: "Confirmation submitted.",
        },
        5000,
      );
      this.recentlyCheckedAndUnconfirmableJwts = [
        ...this.recentlyCheckedAndUnconfirmableJwts,
        give.jwtId,
      ];
    } else {
      console.error("Got error submitting the confirmation:", result);
      const message =
        (result.error?.error as string) ||
        "There was a problem submitting the confirmation.";
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error",
          text: message,
        },
        5000,
      );
    }
  }
}
</script>
