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

  <!-- CONTENT -->
  <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
    <TopMessage />

    <!-- 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">
        Identifier Details
      </h1>

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

      <!-- 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>

    <!-- Identity Details -->
    <div
      v-if="!!contactFromDid"
      class="bg-slate-100 rounded-md overflow-hidden px-4 py-3 mb-4"
    >
      <div>
        <h2 class="text-xl font-semibold overflow-hidden text-ellipsis">
          {{ contactFromDid?.name || "(no name)" }}
          <router-link
            :to="{ name: 'contact-edit', params: { did: contactFromDid?.did } }"
          >
            <font-awesome icon="pen" class="text-sm text-blue-500 ml-2 mb-1" />
          </router-link>
        </h2>

        <!-- Labels -->
        <div v-if="contactLabels.length > 0" class="mt-3">
          <div class="flex flex-wrap gap-2">
            <span
              v-for="label in contactLabels"
              :key="label"
              class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800"
            >
              {{ label }}
            </span>
          </div>
        </div>

        <!-- Notes -->
        <div v-if="contactFromDid.notes" class="mt-3">
          <p class="text-sm text-slate-700 whitespace-pre-wrap">
            {{ contactFromDid.notes }}
          </p>
        </div>

        <!-- Contact Methods -->
        <div v-if="contactFromDid.contactMethods?.length" class="mt-3">
          <div class="flex flex-col gap-2">
            <div
              v-for="(method, index) in contactFromDid.contactMethods"
              :key="index"
              class="flex items-center gap-2 text-sm"
            >
              <span class="font-semibold text-slate-600"
                >{{
                  getContactMethodLabel(method.type) || "(unspecified)"
                }}:</span
              >
              <span class="text-slate-700">{{ method.label }}</span>
              <span class="text-slate-600">{{ method.value }}</span>
              <a
                v-if="method.type === 'CELL'"
                :href="`sms:${method.value}`"
                class="ml-2 text-blue-500 hover:text-blue-700"
                title="Send text message"
              >
                <font-awesome icon="message" class="text-base" />
              </a>
              <a
                v-if="method.type === 'EMAIL'"
                :href="`mailto:${method.value}`"
                class="ml-2 text-blue-500 hover:text-blue-700"
                title="Send email"
              >
                <font-awesome icon="envelope" class="text-base" />
              </a>
              <a
                v-if="method.type === 'WHATSAPP'"
                :href="`https://wa.me/${method.value.replace(/\D/g, '')}`"
                target="_blank"
                class="ml-2 text-blue-700"
                title="Send WhatsApp message"
              >
                <font-awesome :icon="['fab', 'whatsapp']" class="text-base" />
              </a>
            </div>
          </div>
        </div>

        <button class="ml-2 mr-2 mt-4" @click="toggleDidDetails">
          Details
          <font-awesome
            v-if="showDidDetails"
            icon="chevron-down"
            class="text-blue-400"
          />
          <font-awesome v-else icon="chevron-right" class="text-blue-400" />
        </button>
        <!-- Keep the dump contents directly between > and < to avoid weird spacing. -->
        <pre
          v-if="showDidDetails"
          class="text-sm overflow-x-scroll px-4 py-3 bg-slate-100 rounded-md"
          >{{ contactYaml }}</pre
        >
      </div>
      <div class="flex justify-center mt-4">
        <span
          v-if="contactFromDid?.profileImageUrl"
          class="flex justify-between"
        >
          <EntityIcon
            :icon-size="96"
            :profile-image-url="contactFromDid?.profileImageUrl"
            class="inline-block align-text-bottom border border-slate-300 rounded"
            @click="showLargeProfileImage"
          />
        </span>
      </div>
      <div class="flex justify-between mt-4">
        <div class="flex items-center">
          <div v-if="activeDid" class="flex justify-between">
            <div>
              <button
                v-if="
                  contactFromDid?.seesMe && contactFromDid.did !== activeDid
                "
                class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
                title="They can see your activity"
                @click="confirmSetVisibility(contactFromDid, false)"
              >
                <font-awesome icon="arrow-up" class="fa-fw" />
                <font-awesome icon="eye" class="fa-fw" />
              </button>
              <button
                v-else-if="
                  !contactFromDid?.seesMe && contactFromDid?.did !== activeDid
                "
                class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
                title="They cannot see your activity"
                @click="confirmSetVisibility(contactFromDid, true)"
              >
                <font-awesome icon="arrow-up" class="fa-fw" />
                <font-awesome icon="eye-slash" class="fa-fw" />
              </button>

              <button
                v-if="
                  contactFromDid?.iViewContent &&
                  contactFromDid.did !== activeDid
                "
                class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
                title="You watch their activity"
                @click="confirmViewContent(contactFromDid, false)"
              >
                <font-awesome icon="arrow-down" class="fa-fw" />
                <font-awesome icon="eye" class="fa-fw" />
              </button>
              <button
                v-else-if="
                  !contactFromDid?.iViewContent &&
                  contactFromDid?.did !== activeDid
                "
                class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
                title="You do not watch their activity"
                @click="confirmViewContent(contactFromDid, true)"
              >
                <font-awesome icon="arrow-down" class="fa-fw" />
                <font-awesome icon="eye-slash" class="fa-fw" />
              </button>

              <button
                v-if="contactFromDid?.did !== activeDid"
                class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
                title="Check Visibility"
                @click="checkVisibility(contactFromDid)"
              >
                <font-awesome icon="rotate" class="fa-fw" />
              </button>
            </div>

            <button
              v-if="contactFromDid?.did !== activeDid"
              class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white ml-6 mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
              title="Registration"
              @click="confirmRegister(contactFromDid)"
            >
              <font-awesome
                v-if="contactFromDid?.registered"
                icon="person-circle-check"
                class="fa-fw"
              />
              <font-awesome
                v-else
                icon="person-circle-question"
                class="fa-fw"
              />
            </button>
          </div>

          <button
            class="text-sm uppercase bg-gradient-to-b from-rose-500 to-rose-800 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white ml-6 mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
            title="Delete"
            @click="confirmDeleteContact(contactFromDid)"
          >
            <font-awesome icon="trash-can" class="fa-fw" />
          </button>
        </div>
        <div v-if="!contactFromDid?.profileImageUrl">
          <div>Auto-Generated Icon</div>
          <div class="flex justify-center">
            <EntityIcon
              :entity-id="viewingDid"
              :icon-size="64"
              class="inline-block align-middle border border-slate-300 rounded-md mr-1"
              @click="showLargeIdenticon"
            />
          </div>
        </div>
      </div>
      <div
        v-if="showLargeIdenticonId || showLargeIdenticonUrl"
        class="fixed z-[100] top-0 inset-x-0 w-full"
      >
        <div
          class="absolute inset-0 h-screen flex flex-col items-center justify-center bg-slate-900/50"
        >
          <EntityIcon
            :entity-id="showLargeIdenticonId"
            :icon-size="512"
            :profile-image-url="showLargeIdenticonUrl"
            class="flex w-11/12 max-w-sm mx-auto mb-3 overflow-hidden bg-white rounded-lg shadow-lg"
            @click="hideLargeImage"
          />
        </div>
      </div>
    </div>
    <div v-else class="bg-slate-100 rounded-md overflow-hidden px-4 py-3 mb-4">
      <!-- !contactFromDid -->
      <div>
        <h2 class="text-xl font-semibold">
          {{ isMyDid ? "You" : "(no name)" }}
        </h2>
      </div>
    </div>

    <!-- Loading Animation -->
    <div
      v-if="isLoading"
      class="fixed left-6 bottom-24 text-center text-4xl leading-none bg-slate-400 text-white w-14 py-2.5 rounded-full"
    >
      <font-awesome icon="spinner" class="fa-spin-pulse"></font-awesome>
    </div>
    <!-- Results List -->
    <div v-if="claims.length > 0" class="mt-4">
      <div class="text-l font-bold text-center">
        Claims That Involve {{ isMyDid ? "You" : "Them" }}
      </div>
    </div>
    <InfiniteScroll @reached-bottom="loadMoreData">
      <ul>
        <li
          v-for="claim in claims"
          :key="claim.handleId"
          class="border-b border-slate-300"
        >
          <div class="grid grid-cols-12 gap-4">
            <span class="col-span-2">
              {{ claim.issuedAt.substring(0, 10) }}
            </span>
            <span class="col-span-2">
              {{
                capitalizeAndInsertSpacesBeforeCaps(
                  claim.claimType ?? "Unknown",
                )
              }}
            </span>
            <span class="col-span-2">
              {{ claimAmount(claim.claim) }}
            </span>
            <span class="col-span-5">
              {{ claimDescription(claim.claim) }}
            </span>
            <span class="col-span-1">
              <a class="cursor-pointer" @click="onClickLoadClaim(claim.id)">
                <font-awesome
                  icon="file-lines"
                  class="pl-2 pt-1 text-blue-500"
                />
              </a>
            </span>
          </div>
        </li>
      </ul>
    </InfiniteScroll>

    <div
      v-if="!isLoading && claims.length === 0"
      class="flex justify-center mt-4"
    >
      <span v-if="isMyDid">You have no claims yet.</span>
      <span v-else>They are in no claims visible to you.</span>
    </div>
  </section>
</template>

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

import QuickNav from "../components/QuickNav.vue";
import InfiniteScroll from "../components/InfiniteScroll.vue";
import TopMessage from "../components/TopMessage.vue";
import { NotificationIface } from "../constants/app";
import { Contact } from "../db/tables/contacts";
import { BoundingBox } from "../db/tables/settings";

import {
  GenericCredWrapper,
  GenericVerifiableCredential,
  GiveActionClaim,
  OfferClaim,
} from "../interfaces";
import {
  capitalizeAndInsertSpacesBeforeCaps,
  didInfoForContact,
  displayAmount,
  getHeaders,
  isDid,
  register,
  setVisibilityUtil,
} from "../libs/endorserServer";
import * as libsUtil from "../libs/util";
import EntityIcon from "../components/EntityIcon.vue";
import { logger } from "../utils/logger";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
import {
  NOTIFY_CONTACT_DELETED,
  NOTIFY_CONTACT_DELETE_FAILED,
  NOTIFY_REGISTRATION_SUCCESS,
  NOTIFY_REGISTRATION_ERROR,
  NOTIFY_SERVER_ACCESS_ERROR,
  NOTIFY_NO_IDENTITY_ERROR,
  NOTIFY_CONTACT_INVALID_DID,
} from "@/constants/notifications";
import { THAT_UNNAMED_PERSON } from "@/constants/entities";
import { getContactMethodLabel } from "@/constants/contacts";

/**
 * DIDView Component
 *
 * Displays detailed information about a DID (Decentralized Identifier) entity, including:
 * - Basic identity information (name, profile image)
 * - Contact management controls (visibility, registration status)
 * - Associated claims and their details
 *
 * The view supports both viewing one's own DID and other contacts' DIDs.
 * It provides infinite scrolling for claims and interactive controls for contact management.
 */
@Component({
  name: "DIDView",
  components: {
    EntityIcon,
    InfiniteScroll,
    QuickNav,
    TopMessage,
  },
  mixins: [PlatformServiceMixin],
})
export default class DIDView extends Vue {
  $notify!: (notification: NotificationIface, timeout?: number) => void;
  $route!: RouteLocationNormalizedLoaded;
  $router!: Router;

  notify!: ReturnType<typeof createNotifyHelpers>;

  libsUtil = libsUtil;
  yaml = yaml;

  activeDid = "";
  apiServer = "";
  claims: Array<GenericCredWrapper<GenericVerifiableCredential>> = [];
  contactFromDid?: Contact;
  contactLabels: string[] = [];

  contactYaml = "";
  hitEnd = false;
  isLoading = false;
  isMyDid = false;
  searchBox: { name: string; bbox: BoundingBox } | null = null;
  showDidDetails = false;
  showLargeIdenticonId?: string;
  showLargeIdenticonUrl?: string;
  viewingDid?: string;

  capitalizeAndInsertSpacesBeforeCaps = capitalizeAndInsertSpacesBeforeCaps;
  didInfoForContact = didInfoForContact;
  displayAmount = displayAmount;
  getContactMethodLabel = getContactMethodLabel;

  /**
   * Initializes notification helpers
   */
  created() {
    this.notify = createNotifyHelpers(this.$notify);
  }

  /**
   * Initializes the view with DID information
   *
   * Workflow:
   * 1. Retrieves active account settings (DID and API server)
   * 2. Determines which DID to display from URL params or defaults to active DID
   * 3. Loads contact information if available
   * 4. Loads associated claims
   * 5. Determines if viewing own DID
   */
  async mounted() {
    await this.initializeSettings();
    await this.determineDIDToDisplay();
    if (this.viewingDid) {
      await this.loadContactInformation();
      await this.loadClaimsAbout();
      await this.checkIfOwnDID();
    }
  }

  /**
   * Initializes component settings from active account
   */
  private async initializeSettings() {
    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 || "";
  }

  /**
   * Determines which DID to display based on URL parameters
   * Validates DID format and shows error for invalid DIDs
   */
  private async determineDIDToDisplay() {
    const pathParam = window.location.pathname.substring("/did/".length);
    let showDid = pathParam;

    if (!showDid) {
      // No DID provided in URL, use active DID
      showDid = this.activeDid;
    } else {
      // DID provided in URL, validate it
      const decodedDid = decodeURIComponent(showDid);
      if (!isDid(decodedDid)) {
        // Invalid DID format - show error and redirect
        this.notify.error(NOTIFY_CONTACT_INVALID_DID.message, TIMEOUTS.LONG);
        this.$router.push({ name: "home" });
        return;
      }
      showDid = decodedDid;
    }

    this.viewingDid = showDid;
  }

  /**
   * Loads contact information for the viewing DID
   * Updates contact YAML representation if contact exists
   */
  private async loadContactInformation() {
    if (!this.viewingDid) return;

    const contact = await this.$getContact(this.viewingDid);

    if (contact) {
      this.contactFromDid = contact;
      this.contactYaml = yaml.dump(this.contactFromDid);

      // Load labels for this contact
      this.contactLabels = await this.$getContactLabelsForDid(this.viewingDid);
    } else {
      this.contactFromDid = undefined;
      this.contactYaml = "";
      this.contactLabels = [];
    }
  }

  /**
   * Checks if the viewing DID belongs to the current user
   */
  private async checkIfOwnDID() {
    if (!this.viewingDid) return;

    const allAccountDids = await libsUtil.retrieveAccountDids();
    this.isMyDid = allAccountDids.includes(this.viewingDid);
  }

  /**
   * Loads additional claims when user scrolls to bottom
   * Used by infinite scroll component to implement pagination
   *
   * @param payload - Boolean indicating if more data should be loaded
   */
  async loadMoreData(payload: boolean) {
    if (this.claims.length > 0 && !this.hitEnd && payload) {
      this.loadClaimsAbout();
    }
  }

  /**
   * Navigation helper methods
   */
  goBack() {
    this.$router.back();
  }

  /**
   * UI state helper methods
   */
  toggleDidDetails() {
    this.showDidDetails = !this.showDidDetails;
  }

  showLargeProfileImage() {
    this.showLargeIdenticonUrl = this.contactFromDid?.profileImageUrl;
  }

  showLargeIdenticon() {
    this.showLargeIdenticonId = this.viewingDid;
  }

  hideLargeImage() {
    this.showLargeIdenticonId = undefined;
    this.showLargeIdenticonUrl = undefined;
  }

  /**
   * Prompts user to confirm contact deletion
   * Shows additional warning if contact has visibility permissions
   *
   * @param contact - Contact object to be deleted
   */
  confirmDeleteContact(contact: Contact) {
    let message =
      "Are you sure you want to remove " +
      libsUtil.nameForContact(contact, false) +
      " from your contact list?";
    if (contact.seesMe) {
      message +=
        " Note that they can see your activity, so if you want to hide your activity from them then you should do that first.";
    }
    message +=
      " Note that this will also remove anyone with the same DID underneath.";
    this.notify.confirm(message, async () => {
      await this.deleteContact(contact);
    });
  }

  /**
   * Deletes contact from local database and navigates back to contacts list
   *
   * @param contact - Contact object to be deleted
   */
  async deleteContact(contact: Contact) {
    const success = await this.$deleteContact(contact.did);
    const successLabels = await this.$deleteContactLabelsForDid(contact.did);
    if (success) {
      this.notify.success(NOTIFY_CONTACT_DELETED.message, TIMEOUTS.SHORT);
      if (!successLabels) {
        logger.error(
          `[DIDView] Failed to delete contact labels for ${contact.did}`,
        );
      }
      this.$router.push({ name: "contacts" });
    } else {
      this.notify.error(NOTIFY_CONTACT_DELETE_FAILED.message, TIMEOUTS.LONG);
    }
  }

  /**
   * Prompts user to confirm registering a contact
   * Shows additional warning if contact is already registered
   *
   * @param contact - Contact to be registered
   */
  async confirmRegister(contact: Contact) {
    const message =
      "Are you sure you want to register " +
      libsUtil.nameForContact(this.contactFromDid, false) +
      (contact.registered
        ? " -- especially since they are already marked as registered"
        : "") +
      "?";
    this.notify.confirm(message, async () => {
      await this.register(contact);
    });
  }

  /**
   * Registers a contact with the endorser server
   * Updates local database with registration status
   *
   * @param contact - Contact to register
   */
  async register(contact: Contact) {
    this.notify.toast("Processing", "Sent...", TIMEOUTS.SHORT);

    try {
      const regResult = await register(
        this.activeDid,
        this.apiServer,
        this.axios,
        contact,
      );
      if (regResult.success) {
        contact.registered = true;
        await this.$updateContact(contact.did, { registered: true });

        const name = contact.name || THAT_UNNAMED_PERSON;
        this.notify.success(
          `${name} ${NOTIFY_REGISTRATION_SUCCESS.message}`,
          TIMEOUTS.LONG,
        );
      } else {
        this.notify.error(
          (regResult.error as string) || NOTIFY_REGISTRATION_ERROR.message,
          TIMEOUTS.LONG,
        );
      }
    } catch (error) {
      logger.error("Error when registering:", error);
      let userMessage = "There was an error.";
      const serverError = error as AxiosError;
      if (serverError) {
        const errorData = serverError.response?.data as {
          error?: { message?: string };
        };
        if (errorData?.error?.message) {
          userMessage = errorData.error.message;
        } else 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);
    }
  }

  /**
   * Loads claims that involve the viewed DID
   * Implements pagination using beforeId parameter
   * Updates loading state and hit-end status
   */
  public async loadClaimsAbout() {
    if (!this.viewingDid) {
      logger.error("This should never be called without a DID.");
      return;
    }

    const queryParams = "claimContents=" + encodeURIComponent(this.viewingDid);
    let postfix = "";
    if (this.claims.length > 0) {
      postfix = "&beforeId=" + this.claims[this.claims.length - 1].id;
    }

    try {
      this.isLoading = true;
      const response = await fetch(
        this.apiServer + "/api/v2/report/claims?" + queryParams + postfix,
        {
          method: "GET",
          headers: await getHeaders(this.activeDid),
        },
      );

      if (response.status !== 200) {
        const details = await response.text();
        logger.error("Problem with full search:", details);
        this.notify.error(NOTIFY_SERVER_ACCESS_ERROR.message, TIMEOUTS.LONG);
        return;
      }

      const results = await response.json();
      this.claims = this.claims.concat(results.data);
      this.hitEnd = !results.hitLimit;
    } catch (e: unknown) {
      logger.error("Error with feed load:", e);
      const error = e as { userMessage?: string };
      this.notify.error(
        error.userMessage || "There was a problem retrieving claims.",
        TIMEOUTS.SHORT,
      );
    } finally {
      this.isLoading = false;
    }
  }

  /**
   * Navigates to detailed claim view
   *
   * @param jwtId - JWT ID of the claim to view
   */
  onClickLoadClaim(jwtId: string) {
    const route = {
      path: "/claim/" + encodeURIComponent(jwtId),
    };
    this.$router.push(route);
  }

  /**
   * Extracts and formats claim amount information
   * Handles different claim types (GiveAction, Offer)
   *
   * @param claim - Claim object to process
   * @returns Formatted amount string or empty string if no amount
   */
  public claimAmount(claim: GenericVerifiableCredential) {
    if (claim.claimType === "GiveAction") {
      const giveClaim = claim.claim as GiveActionClaim;
      if (giveClaim.object?.unitCode && giveClaim.object?.amountOfThisGood) {
        return displayAmount(
          giveClaim.object.unitCode,
          giveClaim.object.amountOfThisGood,
        );
      } else {
        return "";
      }
    } else if (claim.claimType === "Offer") {
      const offerClaim = claim.claim as OfferClaim;
      if (
        offerClaim.includesObject?.unitCode &&
        offerClaim.includesObject?.amountOfThisGood
      ) {
        return displayAmount(
          offerClaim.includesObject.unitCode,
          offerClaim.includesObject.amountOfThisGood,
        );
      } else {
        return "";
      }
    }
    return "";
  }

  /**
   * Extracts claim description
   * Falls back to name if no description available
   *
   * @param claim - Claim to get description from
   * @returns Description string or empty string
   */
  claimDescription(claim: GenericVerifiableCredential) {
    if (!claim || !claim.claim) {
      return "";
    }
    const claimData = claim.claim as { name?: string; description?: string };
    return claimData.name || claimData.description || "";
  }

  /**
   * Prompts user to confirm visibility change for a contact
   *
   * @param contact - Contact to modify visibility for
   * @param visibility - New visibility state to set
   */
  async confirmSetVisibility(contact: Contact, visibility: boolean) {
    const visibilityPrompt = visibility
      ? "Are you sure you want to make your activity visible to them?"
      : "Are you sure you want to hide all your activity from them?";
    this.notify.confirm(visibilityPrompt, async () => {
      const success = await this.setVisibility(contact, visibility, true);
      if (success) {
        contact.seesMe = visibility; // didn't work inside setVisibility
      }
    });
  }

  /**
   * Updates contact visibility on server and local database
   *
   * @param contact - Contact to update visibility for
   * @param visibility - New visibility state
   * @param showSuccessAlert - Whether to show success notification
   * @returns Boolean indicating success
   */
  async setVisibility(
    contact: Contact,
    visibility: boolean,
    showSuccessAlert: boolean,
  ) {
    const result = await setVisibilityUtil(
      this.activeDid,
      this.apiServer,
      this.axios,
      contact,
      visibility,
    );

    if (result.success) {
      if (showSuccessAlert) {
        const message =
          (contact.name || "That user") +
          " can " +
          (visibility ? "" : "not ") +
          "see your activity.";
        this.notify.success(message, TIMEOUTS.SHORT);
      }
      return true;
    } else {
      logger.error("Got strange result from setting visibility:", result);
      const message =
        (result.error as string) || "Could not set visibility on the server.";
      this.notify.error(message, TIMEOUTS.LONG);
      return false;
    }
  }

  /**
   * Checks current visibility status of contact on server
   * Updates local database with current status
   *
   * @param contact - Contact to check visibility for
   */
  async checkVisibility(contact: Contact) {
    const url =
      this.apiServer +
      "/api/report/canDidExplicitlySeeMe?did=" +
      encodeURIComponent(contact.did);
    const headers = await getHeaders(this.activeDid);
    if (!headers["Authorization"]) {
      this.notify.error(NOTIFY_NO_IDENTITY_ERROR.message, TIMEOUTS.SHORT);
      return;
    }

    try {
      const resp = await this.axios.get(url, { headers });
      if (resp.status === 200) {
        const visibility = resp.data;
        contact.seesMe = visibility;
        await this.$updateContact(contact.did, { seesMe: visibility });

        const message =
          libsUtil.nameForContact(contact, true) +
          " can" +
          (visibility ? "" : " not") +
          " see your activity.";
        this.notify.info(message, TIMEOUTS.SHORT);
      } else {
        logger.error("Got bad server response checking visibility:", resp);
        const message = resp.data.error?.message || "Got bad server response.";
        this.notify.error(message, TIMEOUTS.LONG);
      }
    } catch (err) {
      logger.error("Caught error from request to check visibility:", err);
      this.notify.error("Check connectivity and try again.", TIMEOUTS.SHORT);
    }
  }

  /**
   * Confirm whether the user want to see/hide the other's content, then execute it
   *
   * @param contact Contact content to show/hide from user
   * @param view whether user wants to view this contact
   */
  async confirmViewContent(contact: Contact, view: boolean) {
    const contentVisibilityPrompt = view
      ? "Are you sure you want to see their content?"
      : "Are you sure you want to hide their content from you?";
    this.notify.confirm(contentVisibilityPrompt, async () => {
      const success = await this.setViewContent(contact, view);
      if (success) {
        contact.iViewContent = view; // see visibility note about not working inside setVisibility
      }
    });
  }

  /**
   * Updates contact content visibility for this device
   *
   * @param contact - Contact to update content visibility for
   * @param visibility - New content visibility state
   * @returns Boolean indicating success
   */
  async setViewContent(contact: Contact, visibility: boolean) {
    await this.$updateContact(contact.did, { iViewContent: visibility });
    const message =
      "You will" +
      (visibility ? "" : " not") +
      ` see ${contact.name}'s activity.`;
    this.notify.success(message, TIMEOUTS.SHORT);
    return true;
  }
}
</script>
