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

  <!-- CONTENT -->
  <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
    <!-- Breadcrumb -->
    <div id="ViewBreadcrumb" class="mb-8">
      <h1 id="ViewHeading" class="text-lg text-center font-light relative px-7">
        <!-- Back -->
        <button
          class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
          @click="$router.go(-1)"
        >
          <font-awesome icon="chevron-left" class="fa-fw"></font-awesome>
        </button>
        Identifier Details
      </h1>
    </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">
          {{ 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>
        <button
          class="ml-2 mr-2 mt-4"
          @click="showDidDetails = !showDidDetails"
        >
          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="showLargeIdenticonUrl = contactFromDid?.profileImageUrl"
          />
        </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 you"
                @click="confirmSetVisibility(contactFromDid, false)"
              >
                <font-awesome icon="eye" class="fa-fw" />
                <font-awesome icon="arrow-up" 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 you"
                @click="confirmSetVisibility(contactFromDid, true)"
              >
                <font-awesome icon="eye-slash" class="fa-fw" />
                <font-awesome icon="arrow-up" 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="I view their content"
                @click="confirmViewContent(contactFromDid, false)"
              >
                <font-awesome icon="eye" class="fa-fw" />
                <font-awesome icon="arrow-down" 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="I do not view their content"
                @click="confirmViewContent(contactFromDid, true)"
              >
                <font-awesome icon="eye-slash" class="fa-fw" />
                <font-awesome icon="arrow-down" 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="showLargeIdenticonId = viewingDid"
            />
          </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="
              showLargeIdenticonId = undefined;
              showLargeIdenticonUrl = undefined;
            "
          />
        </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) }}
            </span>
            <span class="col-span-2">
              {{ claimAmount(claim) }}
            </span>
            <span class="col-span-5">
              {{ claimDescription(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, USE_DEXIE_DB } from "../constants/app";
import { db, retrieveSettingsForActiveAccount } from "../db/index";
import { Contact } from "../db/tables/contacts";
import { BoundingBox } from "../db/tables/settings";
import * as databaseUtil from "../db/databaseUtil";
import {
  GenericCredWrapper,
  GenericVerifiableCredential,
  GiveActionClaim,
  OfferClaim,
} from "../interfaces";
import {
  capitalizeAndInsertSpacesBeforeCaps,
  didInfoForContact,
  displayAmount,
  getHeaders,
  register,
  setVisibilityUtil,
} from "../libs/endorserServer";
import * as libsUtil from "../libs/util";
import EntityIcon from "../components/EntityIcon.vue";
import { logger } from "../utils/logger";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";

/**
 * 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({
  components: {
    EntityIcon,
    InfiniteScroll,
    QuickNav,
    TopMessage,
  },
})
export default class DIDView extends Vue {
  $notify!: (notification: NotificationIface, timeout?: number) => void;
  $route!: RouteLocationNormalizedLoaded;
  $router!: Router;

  libsUtil = libsUtil;
  yaml = yaml;

  activeDid = "";
  apiServer = "";
  claims: Array<GenericCredWrapper<GenericVerifiableCredential>> = [];
  contactFromDid?: Contact;
  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;

  /**
   * 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() {
    let settings = await databaseUtil.retrieveSettingsForActiveAccount();
    if (USE_DEXIE_DB) {
      settings = await retrieveSettingsForActiveAccount();
    }
    this.activeDid = settings.activeDid || "";
    this.apiServer = settings.apiServer || "";
  }

  /**
   * Determines which DID to display based on URL parameters
   * Falls back to active DID if no parameter provided
   */
  private async determineDIDToDisplay() {
    const pathParam = window.location.pathname.substring("/did/".length);
    let showDid = pathParam;

    if (!showDid) {
      showDid = this.activeDid;
      if (showDid) {
        this.notifyDefaultToActiveDID();
      }
    }

    if (showDid) {
      this.viewingDid = decodeURIComponent(showDid);
    }
  }

  /**
   * Notifies user that we're showing their DID info by default
   */
  private notifyDefaultToActiveDID() {
    this.$notify(
      {
        group: "alert",
        type: "toast",
        title: "Your Info",
        text: "No user was specified so showing your info.",
      },
      3000,
    );
  }

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

    const platformService = PlatformServiceFactory.getInstance();
    const dbContacts = await platformService.dbQuery(
      "SELECT * FROM contacts WHERE did = ?",
      [this.viewingDid],
    );
    this.contactFromDid = databaseUtil.mapQueryResultToValues(
      dbContacts,
    )[0] as unknown as Contact;
    if (USE_DEXIE_DB) {
      this.contactFromDid = await db.contacts.get(this.viewingDid);
    }
    if (this.contactFromDid) {
      this.contactYaml = yaml.dump(this.contactFromDid);
    }
  }

  /**
   * 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();
    }
  }

  /**
   * 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.";
    }
    this.$notify(
      {
        group: "modal",
        type: "confirm",
        title: "Delete",
        text: message,
        onYes: async () => {
          await this.deleteContact(contact);
        },
      },
      -1,
    );
  }

  /**
   * Deletes contact from local database and navigates back to contacts list
   *
   * @param contact - Contact object to be deleted
   */
  async deleteContact(contact: Contact) {
    const platformService = PlatformServiceFactory.getInstance();
    await platformService.dbExec("DELETE FROM contacts WHERE did = ?", [
      contact.did,
    ]);
    if (USE_DEXIE_DB) {
      await db.open();
      await db.contacts.delete(contact.did);
    }
    this.$notify(
      {
        group: "alert",
        type: "success",
        title: "Deleted",
        text: "Contact has been removed.",
      },
      3000,
    );
    this.$router.push({ name: "contacts" });
  }

  /**
   * 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) {
    this.$notify(
      {
        group: "modal",
        type: "confirm",
        title: "Register",
        text:
          "Are you sure you want to register " +
          libsUtil.nameForContact(this.contactFromDid, false) +
          (contact.registered
            ? " -- especially since they are already marked as registered"
            : "") +
          "?",
        onYes: async () => {
          await this.register(contact);
        },
      },
      -1,
    );
  }

  /**
   * Registers a contact with the endorser server
   * Updates local database with registration status
   *
   * @param contact - Contact to register
   */
  async register(contact: Contact) {
    this.$notify({ group: "alert", type: "toast", title: "Sent..." }, 1000);

    try {
      const regResult = await register(
        this.activeDid,
        this.apiServer,
        this.axios,
        contact,
      );
      if (regResult.success) {
        contact.registered = true;
        const platformService = PlatformServiceFactory.getInstance();
        await platformService.dbExec(
          "UPDATE contacts SET registered = ? WHERE did = ?",
          [true, contact.did],
        );
        if (USE_DEXIE_DB) {
          await db.contacts.update(contact.did, { registered: true });
        }

        this.$notify(
          {
            group: "alert",
            type: "success",
            title: "Registration Success",
            text:
              (contact.name || "That unnamed person") + " has been registered.",
          },
          5000,
        );
      } else {
        this.$notify(
          {
            group: "alert",
            type: "danger",
            title: "Registration Error",
            text:
              (regResult.error as string) ||
              "Something went wrong during registration.",
          },
          5000,
        );
      }
    } catch (error) {
      logger.error("Error when registering:", error);
      let userMessage = "There was an error.";
      const serverError = error as AxiosError;
      if (serverError) {
        if (serverError.response?.data?.error?.message) {
          userMessage = serverError.response.data.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(
        {
          group: "alert",
          type: "danger",
          title: "Registration Error",
          text: userMessage,
        },
        5000,
      );
    }
  }

  /**
   * 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(
          {
            group: "alert",
            type: "danger",
            title: "Error",
            text: `There was a problem accessing the server. Try again later.`,
          },
          5000,
        );
        return;
      }

      const results = await response.json();
      this.claims = this.claims.concat(results.data);
      this.hitEnd = !results.hitLimit;

      // eslint-disable-next-line @typescript-eslint/no-explicit-any
    } catch (e: any) {
      logger.error("Error with feed load:", e);
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error",
          text: e.userMessage || "There was a problem retrieving claims.",
        },
        3000,
      );
    } 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) {
    return claim.claim.name || claim.claim.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(
      {
        group: "modal",
        type: "confirm",
        title: "Set Visibility",
        text: visibilityPrompt,
        onYes: async () => {
          const success = await this.setVisibility(contact, visibility, true);
          if (success) {
            contact.seesMe = visibility; // didn't work inside setVisibility
          }
        },
      },
      -1,
    );
  }

  /**
   * 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,
      db,
      contact,
      visibility,
    );
    if (result.success) {
      //contact.seesMe = visibility; // why doesn't it affect the UI from here?
      //console.log("Set result & seesMe", result, contact.seesMe, contact.did);
      if (showSuccessAlert) {
        this.$notify(
          {
            group: "alert",
            type: "success",
            title: "Visibility Set",
            text:
              (contact.name || "That user") +
              " can " +
              (visibility ? "" : "not ") +
              "see your activity.",
          },
          3000,
        );
      }
      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(
        {
          group: "alert",
          type: "danger",
          title: "Error Setting Visibility",
          text: message,
        },
        5000,
      );
      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(
        {
          group: "alert",
          type: "danger",
          title: "No Identity",
          text: "There is no identity to use to check visibility.",
        },
        3000,
      );
      return;
    }

    try {
      const resp = await this.axios.get(url, { headers });
      if (resp.status === 200) {
        const visibility = resp.data;
        contact.seesMe = visibility;
        //console.log("Visi check:", visibility, contact.seesMe, contact.did);
        const platformService = PlatformServiceFactory.getInstance();
        await platformService.dbExec(
          "UPDATE contacts SET seesMe = ? WHERE did = ?",
          [visibility, contact.did],
        );
        if (USE_DEXIE_DB) {
          await db.contacts.update(contact.did, { seesMe: visibility });
        }

        this.$notify(
          {
            group: "alert",
            type: "info",
            title: "Visibility Refreshed",
            text:
              libsUtil.nameForContact(contact, true) +
              " can" +
              (visibility ? "" : " not") +
              " see your activity.",
          },
          3000,
        );
      } else {
        logger.error("Got bad server response checking visibility:", resp);
        const message = resp.data.error?.message || "Got bad server response.";
        this.$notify(
          {
            group: "alert",
            type: "danger",
            title: "Error Checking Visibility",
            text: message,
          },
          5000,
        );
      }
    } catch (err) {
      logger.error("Caught error from request to check visibility:", err);
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error Checking Visibility",
          text: "Check connectivity and try again.",
        },
        3000,
      );
    }
  }

  /**
   * 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(
      {
        group: "modal",
        type: "confirm",
        title: "Set Content Visibility",
        text: contentVisibilityPrompt,
        onYes: async () => {
          const success = await this.setViewContent(contact, view);
          if (success) {
            contact.iViewContent = view; // see visibility note about not working inside setVisibility
          }
        },
      },
      -1,
    );
  }

  /**
   * 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) {
    const platformService = PlatformServiceFactory.getInstance();
    await platformService.dbExec(
      "UPDATE contacts SET iViewContent = ? WHERE did = ?",
      [visibility, contact.did],
    );
    if (USE_DEXIE_DB) {
      db.contacts.update(contact.did, { iViewContent: visibility });
    }
    this.$notify(
      {
        group: "alert",
        type: "success",
        title: "Visibility Set",
        text:
          "You will" +
          (visibility ? "" : " not") +
          ` see ${contact.name}'s activity.`,
      },
      3000,
    );
    return true;
  }
}
</script>

<style>
.dialog-overlay {
  z-index: 50;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.5);
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 1.5rem;
}
.dialog {
  background-color: white;
  padding: 1rem;
  border-radius: 0.5rem;
  width: 100%;
  max-width: 500px;
}
</style>
