<template>
  <QuickNav />
  <TopMessage />

  <!-- CONTENT -->
  <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
    <!-- Back -->
    <div class="text-lg text-center font-light relative px-7">
      <h1
        class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
        @click="$router.back()"
      >
        <fa icon="chevron-left" class="fa-fw"></fa>
      </h1>
    </div>

    <!-- Heading -->
    <h1 id="ViewHeading" class="text-4xl text-center font-light px-4 mb-4">
      End of BVC Saturday Meeting
    </h1>

    <div>
      <h2 class="text-2xl m-2">Confirm</h2>
      <div v-if="loadingConfirms" class="flex justify-center">
        <fa icon="spinner" class="animate-spin" />
      </div>
      <div v-else-if="claimsToConfirm.length === 0">
        There are no claims yet today for you to confirm.
      </div>
      <ul class="border-t border-slate-300 m-2">
        <li
          class="border-b border-slate-300 py-2"
          v-for="record in claimsToConfirm"
          :key="record.id"
        >
          <div class="grid grid-cols-12">
            <span class="col-span-11 justify-self-start">
              <span>
                <input
                  type="checkbox"
                  :checked="claimsToConfirmSelected.includes(record.id)"
                  @click="
                    claimsToConfirmSelected.includes(record.id)
                      ? claimsToConfirmSelected.splice(
                          claimsToConfirmSelected.indexOf(record.id),
                          1,
                        )
                      : claimsToConfirmSelected.push(record.id)
                  "
                  class="mr-2 h-6 w-6"
                />
              </span>
              {{
                claimSpecialDescription(
                  record,
                  activeDid,
                  allMyDids,
                  allContacts,
                )
              }}
              <a @click="onClickLoadClaim(record.id)">
                <fa
                  icon="file-lines"
                  class="pl-2 text-blue-500 cursor-pointer"
                />
              </a>
            </span>
          </div>
        </li>
      </ul>
    </div>
    <div v-if="claimCountWithHidden > 0" class="border-b border-slate-300 pb-2">
      <span>
        {{
          claimCountWithHidden === 1
            ? "There is 1 other claim with hidden details,"
            : `There are ${claimCountWithHidden} other claims with hidden details,`
        }}
        so if you expected but do not see details from someone then ask them to
        check that their activity is visible to you on their Contacts
        <fa icon="users" class="text-slate-500" />
        page.
      </span>
    </div>
    <div v-if="claimCountByUser > 0" class="border-b border-slate-300 pb-2">
      <span>
        {{
          claimCountByUser === 1
            ? "There is 1 other claim by you"
            : `There are ${claimCountByUser} other claims by you`
        }}
        which you don't need to confirm.
      </span>
    </div>

    <div>
      <h2 class="text-2xl m-2">Anything else?</h2>
      <div class="m-2 flex">
        <input type="checkbox" v-model="someoneGave" class="h-6 w-6" />
        <span class="pb-2 pl-2 pr-2">Someone else gave</span>
        <span v-if="someoneGave">
          <input
            type="text"
            v-model="description"
            size="20"
            class="border border-slate-400 h-6 px-2"
          />
          <br />
          (Everyone likes personalized messages! 😁)
        </span>
        <!-- This is to match input height to avoid shifting when hiding & showing. -->
        <span v-else class="h-6">...</span>
      </div>
    </div>

    <div
      v-if="claimsToConfirmSelected.length || (someoneGave && description)"
      class="flex justify-center mt-4"
    >
      <button
        @click="record()"
        class="block text-center text-md font-bold bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md w-56"
      >
        Sign & Send
      </button>
    </div>
    <div v-else class="flex justify-center mt-4">
      <button
        class="block text-center text-md font-bold bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md w-56"
      >
        Choose What To Confirm
      </button>
    </div>
  </section>
</template>

<script lang="ts">
import axios from "axios";
import { DateTime } from "luxon";
import * as R from "ramda";
import { Component, Vue } from "vue-facing-decorator";
import { Router } from "vue-router";

import QuickNav from "@/components/QuickNav.vue";
import TopMessage from "@/components/TopMessage.vue";
import { NotificationIface } from "@/constants/app";
import { accountsDB, db } from "@/db/index";
import { Contact } from "@/db/tables/contacts";
import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings";
import {
  BVC_MEETUPS_PROJECT_CLAIM_ID,
  claimSpecialDescription,
  containsHiddenDid,
  createAndSubmitConfirmation,
  createAndSubmitGive,
  GenericCredWrapper,
  GenericVerifiableCredential,
  getHeaders,
  ErrorResult,
} from "@/libs/endorserServer";

@Component({
  methods: { claimSpecialDescription },
  components: {
    QuickNav,
    TopMessage,
  },
})
export default class QuickActionBvcBeginView extends Vue {
  $notify!: (notification: NotificationIface, timeout?: number) => void;

  activeDid = "";
  allContacts: Array<Contact> = [];
  allMyDids: Array<string> = [];
  apiServer = "";
  claimCountByUser = 0;
  claimCountWithHidden = 0;
  claimsToConfirm: GenericCredWrapper<GenericVerifiableCredential>[] = [];
  claimsToConfirmSelected: string[] = [];
  description = "breakfast";
  loadingConfirms = true;
  someoneGave = false;

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

  async mounted() {
    this.loadingConfirms = true;
    let currentOrPreviousSat = DateTime.now().setZone("America/Denver");
    if (currentOrPreviousSat.weekday < 6) {
      // it's not Saturday or Sunday,
      // so move back one week before setting to the Saturday
      currentOrPreviousSat = currentOrPreviousSat.minus({ week: 1 });
    }
    const eventStartDateObj = currentOrPreviousSat
      .set({ weekday: 6 })
      .set({ hour: 9 })
      .startOf("hour");

    // Hack, but full ISO pushes the length to 340 which crashes verifyJWT!
    const todayOrPreviousStartDate =
      eventStartDateObj.toISO({
        suppressMilliseconds: true,
      }) || "";

    await accountsDB.open();
    const allAccounts = await accountsDB.accounts.toArray();
    this.allMyDids = allAccounts.map((acc) => acc.did);
    const headers = await getHeaders(this.activeDid);
    try {
      const response = await fetch(
        this.apiServer +
          "/api/claim/?" +
          "issuedAt_greaterThanOrEqualTo=" +
          encodeURIComponent(todayOrPreviousStartDate) +
          "&excludeConfirmations=true",
        { headers },
      );

      if (!response.ok) {
        console.error("Bad response", response);
        throw new Error("Bad response when retrieving claims.");
      }
      await response.json().then((data) => {
        const dataByOthers = R.reject(
          (claim: GenericCredWrapper<GenericVerifiableCredential>) =>
            claim.issuer === this.activeDid,
          data,
        );
        const dataByOthersWithoutHidden = R.reject(
          containsHiddenDid,
          dataByOthers,
        );
        this.claimsToConfirm = dataByOthersWithoutHidden;
        this.claimCountByUser = data.length - dataByOthers.length;
        this.claimCountWithHidden =
          dataByOthers.length - dataByOthersWithoutHidden.length;
      });
    } catch (error) {
      console.error("Error:", error);
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error",
          text: "There was an error retrieving today's claims to confirm.",
        },
        5000,
      );
    }
    this.loadingConfirms = false;
  }

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

  async record() {
    try {
      this.$notify({ group: "alert", type: "toast", title: "Sent..." }, 1000);

      // in parallel, make a confirmation for each selected claim and send them all to the server
      const confirmResults = await Promise.allSettled(
        this.claimsToConfirmSelected.map(async (jwtId) => {
          const record = this.claimsToConfirm.find(
            (claim) => claim.id === jwtId,
          );
          if (!record) {
            return { type: "error", error: "Record not found." };
          }
          return createAndSubmitConfirmation(
            this.activeDid,
            record.claim as GenericVerifiableCredential,
            record.id,
            record.handleId,
            this.apiServer,
            axios,
          );
        }),
      );
      // check for any rejected confirmations
      const confirmsSucceeded = confirmResults.filter(
        (result) =>
          result.status === "fulfilled" && result.value.type === "success",
      );
      if (confirmsSucceeded.length < this.claimsToConfirmSelected.length) {
        console.error("Error sending confirmations:", confirmResults);
        const howMany = confirmsSucceeded.length === 0 ? "all" : "some";
        this.$notify(
          {
            group: "alert",
            type: "danger",
            title: "Error",
            text: `There was an error sending ${howMany} of the confirmations.`,
          },
          5000,
        );
      }

      // now send the give for the description
      let giveSucceeded = false;
      if (this.someoneGave) {
        const giveResult = await createAndSubmitGive(
          axios,
          this.apiServer,
          this.activeDid,
          undefined,
          this.activeDid,
          this.description,
          undefined,
          undefined,
          BVC_MEETUPS_PROJECT_CLAIM_ID,
        );
        giveSucceeded = giveResult.type === "success";
        if (!giveSucceeded) {
          console.error("Error sending give:", giveResult);
          this.$notify(
            {
              group: "alert",
              type: "danger",
              title: "Error",
              text:
                (giveResult as ErrorResult)?.error?.userMessage ||
                "There was an error sending that give.",
            },
            5000,
          );
        }
      }

      if (confirmsSucceeded.length > 0 || giveSucceeded) {
        const confirms =
          confirmsSucceeded.length === 1 ? "confirmation" : "confirmations";
        const actions =
          confirmsSucceeded.length > 0 && giveSucceeded
            ? `Your ${confirms} and that give have been recorded.`
            : giveSucceeded
              ? "That give has been recorded."
              : "Your " +
                confirms +
                " " +
                (confirmsSucceeded.length === 1 ? "has" : "have") +
                " been recorded.";
        this.$notify(
          {
            group: "alert",
            type: "success",
            title: "Success",
            text: actions,
          },
          3000,
        );
      }

      // eslint-disable-next-line @typescript-eslint/no-explicit-any
    } catch (error: any) {
      console.error("Error sending claims.", error);
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error",
          text: error.userMessage || "There was an error sending claims.",
        },
        5000,
      );
    }
  }
}
</script>
