<template>
  <QuickNav />
  <!-- CONTENT -->
  <section id="Content" class="p-6 pb-24">
    <!-- Breadcrumb -->
    <div id="ViewBreadcrumb" class="mb-8">
      <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
      </h1>
    </div>

    <!-- Project Details -->
    <div class="bg-slate-100 rounded-md overflow-hidden px-4 py-3 mb-4">
      <div>
        <div class="block pb-4 flex gap-4">
          <div class="flex-none w-16 pt-1">
            <EntityIcon
              :entityId="projectId"
              :iconSize="64"
              class="block border border-slate-300 rounded-md"
            ></EntityIcon>
          </div>

          <div class="overflow-hidden">
            <h2 class="text-xl font-semibold">{{ name }}</h2>
            <div class="text-sm mb-3">
              <div class="truncate">
                <fa icon="user" class="fa-fw text-slate-400"></fa>
                {{ issuer }}
              </div>
              <div v-if="timeSince">
                <fa icon="calendar" class="fa-fw text-slate-400"></fa>
                {{ timeSince }}
              </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
                </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) }}
                </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>
      </div>
      <button
        v-if="issuer == activeDid"
        type="button"
        class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md"
        @click="onEditClick()"
      >
        Edit
      </button>
    </div>

    <div class="mb-4">
      <div v-if="activeDid" class="text-center">
        <button
          @click="openOfferDialog({ name: 'you', did: activeDid })"
          class="block w-full text-lg font-bold uppercase bg-blue-600 text-white px-2 py-3 rounded-md"
        >
          I offer&hellip;
        </button>
      </div>
    </div>

    <div>
      <div v-if="activeDid" class="text-center">
        <button
          @click="openGiftDialog({ name: 'you', did: activeDid })"
          class="block w-full text-lg font-bold uppercase bg-blue-600 text-white px-2 py-3 rounded-md"
        >
          I gave&hellip;
        </button>
        <p class="mt-2 mb-4 text-center">Or, record a gift from:</p>
      </div>
      <p v-if="!activeDid" class="mt-2 mb-4">Record a gift from:</p>

      <ul class="grid grid-cols-4 gap-x-3 gap-y-5 text-center mb-5">
        <li @click="openGiftDialog()">
          <EntityIcon
            :entityId="null"
            :iconSize="64"
            class="mx-auto border border-slate-300 rounded-md mb-1"
          ></EntityIcon>
          <h3
            class="text-xs italic font-medium text-ellipsis whitespace-nowrap overflow-hidden"
          >
            Anonymous/Unnamed
          </h3>
        </li>
        <li
          v-for="contact in allContacts"
          :key="contact.did"
          @click="openGiftDialog(contact)"
        >
          <EntityIcon
            :entityId="contact.did"
            :iconSize="64"
            class="mx-auto border border-slate-300 rounded-md mb-1"
          ></EntityIcon>
          <h3
            class="text-xs font-medium text-ellipsis whitespace-nowrap overflow-hidden"
          >
            {{ contact.name || "(no name)" }}
          </h3>
        </li>
      </ul>

      <!-- Ideally, this button should only be visible when the active account has more than 7 or 11 contacts in their list (we want to limit the grid count above to 8 or 12 accounts to keep it compact) -->
      <router-link
        v-if="allContacts.length >= 7"
        :to="{ name: 'contact-gives' }"
        class="block text-center text-md font-bold uppercase bg-slate-500 text-white px-2 py-3 rounded-md"
      >
        Show More Contacts&hellip;
      </router-link>
    </div>

    <!-- Gifts to & from this -->
    <div class="grid items-start grid-cols-1 sm:grid-cols-3 gap-4">
      <div class="bg-slate-100 px-4 py-3 rounded-md">
        <h3 class="text-sm uppercase font-semibold mb-3">
          Offered To This Idea
        </h3>

        <div v-if="offersToThis.length === 0">
          (None yet. Record one above.)
        </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>
                {{ didInfo(offer.agentDid, activeDid, allMyDids, allContacts) }}
              </span>
              <span v-if="offer.amount">
                <fa
                  :icon="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"></fa>
              {{ offer.objectDescription }}
            </div>
          </li>
        </ul>
      </div>

      <div class="bg-slate-100 px-4 py-3 rounded-md">
        <h3 class="text-sm uppercase font-semibold mb-3">Given To This Idea</h3>

        <div v-if="givesToThis.length === 0">(None yet. Record one 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"></fa>
                {{ didInfo(give.agentDid, activeDid, allMyDids, allContacts) }}
              </span>
              <span v-if="give.amount">
                <fa
                  :icon="iconForUnitCode(give.unit)"
                  class="fa-fw text-slate-400"
                />{{ give.amount }}
              </span>
            </div>
            <div v-if="give.description" class="text-slate-500">
              <fa icon="comment" class="fa-fw text-slate-400"></fa>
              {{ give.description }}
            </div>
          </li>
        </ul>
      </div>

      <div class="grid items-start grid-cols-1 gap-4">
        <div
          v-if="fulfillersToThis.length > 0"
          class="bg-slate-100 px-4 py-3 rounded-md"
        >
          <h3 class="text-sm uppercase font-semibold mb-3">
            Contributions To This Idea
          </h3>
          <ul>
            <li v-for="plan in fulfillersToThis" :key="plan.handleId">
              <button
                @click="onClickLoadProject(plan.handleId)"
                class="text-blue-500"
              >
                {{ plan.name }}
              </button>
            </li>
          </ul>
        </div>

        <div v-if="fulfilledByThis" class="bg-slate-100 px-4 py-3 rounded-md">
          <h3 class="text-sm uppercase font-semibold mb-3">
            Contributions By This Idea
          </h3>
          <button
            @click="onClickLoadProject(fulfilledByThis.handleId)"
            class="text-blue-500"
          >
            {{ fulfilledByThis.name }}
          </button>
        </div>
      </div>
    </div>

    <GiftedDialog
      ref="customGiveDialog"
      message="Received from"
      :projectId="this.projectId"
    >
    </GiftedDialog>
    <OfferDialog ref="customOfferDialog" :projectId="this.projectId">
    </OfferDialog>
  </section>
</template>

<script lang="ts">
import { AxiosError, RawAxiosRequestHeaders } from "axios";
import * as moment from "moment";
import { IIdentifier } from "@veramo/core";
import { Component, Vue } from "vue-facing-decorator";

import GiftedDialog from "@/components/GiftedDialog.vue";
import OfferDialog from "@/components/OfferDialog.vue";
import { accountsDB, db } from "@/db/index";
import { Contact } from "@/db/tables/contacts";
import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings";
import { accessToken } from "@/libs/crypto";
import { isGlobalUri } from "@/libs/util";
import {
  didInfo,
  GiverInputInfo,
  GiveServerRecord,
  OfferServerRecord,
  PlanServerRecord,
} from "@/libs/endorserServer";
import QuickNav from "@/components/QuickNav.vue";
import EntityIcon from "@/components/EntityIcon.vue";
import { Account } from "@/db/tables/accounts";

interface Notification {
  group: string;
  type: string;
  title: string;
  text: string;
}

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

  activeDid = "";
  allMyDids: Array<string> = [];
  allContacts: Array<Contact> = [];
  apiServer = "";
  description = "";
  expanded = false;
  fulfilledByThis: PlanServerRecord | null = null;
  fulfillersToThis: Array<PlanServerRecord> = [];
  givesToThis: Array<GiveServerRecord> = [];
  issuer = "";
  latitude = 0;
  longitude = 0;
  name = "";
  offersToThis: Array<OfferServerRecord> = [];
  projectId = localStorage.getItem("projectId") || ""; // handle ID
  timeSince = "";
  truncatedDesc = "";
  truncateLength = 40;
  url = "";

  async created() {
    await db.open();
    const settings = (await db.settings.get(MASTER_SETTINGS_KEY)) as Settings;
    this.activeDid = settings?.activeDid || "";
    this.apiServer = settings?.apiServer || "";
    this.allContacts = await db.contacts.toArray();

    await accountsDB.open();
    const accounts = accountsDB.accounts;
    const accountsArr = await accounts?.toArray();
    this.allMyDids = accountsArr.map((acc) => acc.did);
    const account = accountsArr.find((acc) => acc.did === this.activeDid);
    const identity = JSON.parse(account?.identity || "null");

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

  public async getIdentity(activeDid: string) {
    await accountsDB.open();
    const account = (await accountsDB.accounts
      .where("did")
      .equals(activeDid)
      .first()) as Account;
    const identity = JSON.parse(account?.identity || "null");

    if (!identity) {
      throw new Error(
        "Attempted to load project records with no identity available.",
      );
    }
    return identity;
  }

  public async getHeaders(identity: IIdentifier) {
    const token = await accessToken(identity);
    const headers = {
      "Content-Type": "application/json",
      Authorization: "Bearer " + token,
    };
    return headers;
  }

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

  // Isn't there a better way to make this available to the template?
  didInfo(
    did: string,
    activeDid: string,
    dids: Array<string>,
    contacts: Array<Contact>,
  ) {
    return didInfo(did, activeDid, dids, contacts);
  }

  expandText() {
    this.expanded = true;
  }

  collapseText() {
    this.expanded = false;
  }

  async LoadProject(projectId: string, identity: IIdentifier) {
    this.projectId = projectId;

    const url =
      this.apiServer + "/api/claim/byHandle/" + encodeURIComponent(projectId);
    const headers: RawAxiosRequestHeaders = {
      "Content-Type": "application/json",
    };
    if (identity) {
      const token = await accessToken(identity);
      headers["Authorization"] = "Bearer " + token;
    }

    try {
      const resp = await this.axios.get(url, { headers });
      if (resp.status === 200) {
        const startTime = resp.data.startTime;
        if (startTime != null) {
          const eventDate = new Date(startTime);
          const now = moment.now();
          this.timeSince = moment.utc(now).to(eventDate);
        }
        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.log("Error getting project:", resp);
        this.$notify(
          {
            group: "alert",
            type: "danger",
            title: "Error",
            text: "There was a problem getting that project. See logs for more info.",
          },
          -1,
        );
      }
    } catch (error: unknown) {
      console.error("Error retrieving project:", error);
      const serverError = error as AxiosError;
      if (serverError.response?.status === 404) {
        this.$notify(
          {
            group: "alert",
            type: "danger",
            title: "Error",
            text: "That project does not exist.",
          },
          -1,
        );
      } else {
        this.$notify(
          {
            group: "alert",
            type: "danger",
            title: "Error",
            text: "Something went wrong retrieving that project. See logs for more info.",
          },
          -1,
        );
      }
    }

    const givesInUrl =
      this.apiServer +
      "/api/v2/report/givesForPlans?planIds=" +
      encodeURIComponent(JSON.stringify([projectId]));
    try {
      const resp = await this.axios.get(givesInUrl, { headers });
      if (resp.status === 200 && resp.data.data) {
        this.givesToThis = resp.data.data;
      } else {
        this.$notify(
          {
            group: "alert",
            type: "danger",
            title: "Error",
            text: "Failed to retrieve gives to this project.",
          },
          -1,
        );
      }
    } catch (error: unknown) {
      const serverError = error as AxiosError;
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error",
          text: "Something went wrong retrieving gives to this project.",
        },
        -1,
      );
      console.error(
        "Error retrieving gives to this project:",
        serverError.message,
      );
    }

    const offersToUrl =
      this.apiServer +
      "/api/v2/report/offersToPlans?planIds=" +
      encodeURIComponent(JSON.stringify([projectId]));
    try {
      const resp = await this.axios.get(offersToUrl, { headers });
      if (resp.status === 200 && resp.data.data) {
        this.offersToThis = resp.data.data;
      } else {
        this.$notify(
          {
            group: "alert",
            type: "danger",
            title: "Error",
            text: "Failed to retrieve offers to this project.",
          },
          -1,
        );
      }
    } catch (error: unknown) {
      const serverError = error as AxiosError;
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error",
          text: "Something went wrong retrieving offers to this project.",
        },
        -1,
      );
      console.error(
        "Error retrieving offers to this project:",
        serverError.message,
      );
    }

    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.",
          },
          -1,
        );
      }
    } 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.",
        },
        -1,
      );
      console.error(
        "Error retrieving plans fulfilled by this project:",
        serverError.message,
      );
    }

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

  /**
   * Handle clicking on a project entry found in the list
   * @param id of the project
   **/
  async onClickLoadProject(projectId: string) {
    localStorage.setItem("projectId", projectId);
    const route = {
      path: "/project/" + encodeURIComponent(projectId),
    };
    this.$router.push(route);
    this.LoadProject(projectId, await this.getIdentity(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
    );
  }

  openGiftDialog(contact: GiverInputInfo) {
    (this.$refs.customGiveDialog as GiftedDialog).open(contact);
  }

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

  UNIT_CODES: Record<string, Record<string, string>> = {
    BTC: {
      name: "Bitcoin",
      faIcon: "bitcoin-sign",
    },
    HUR: {
      name: "hours",
      faIcon: "clock",
    },
    USD: {
      name: "US Dollars",
      faIcon: "dollar",
    },
  };

  iconForUnitCode(unitCode: string) {
    return this.UNIT_CODES[unitCode]?.faIcon || "question";
  }

  // return an HTTPS URL if it's not a global URL
  addScheme(url: string) {
    if (!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;
    }
  }
}
</script>