<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()"
      >
        <font-awesome icon="chevron-left" class="fa-fw"></font-awesome>
      </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">
        <font-awesome icon="spinner" class="fa-spin-pulse" />
      </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
          v-for="record in claimsToConfirm"
          :key="record.id"
          class="border-b border-slate-300 py-2"
        >
          <div class="grid grid-cols-12">
            <span class="col-span-11 justify-self-start">
              <span>
                <input
                  type="checkbox"
                  :checked="claimsToConfirmSelected.includes(record.id)"
                  class="mr-2 h-6 w-6"
                  @click="
                    claimsToConfirmSelected.includes(record.id)
                      ? claimsToConfirmSelected.splice(
                          claimsToConfirmSelected.indexOf(record.id),
                          1,
                        )
                      : claimsToConfirmSelected.push(record.id)
                  "
                />
              </span>
              {{
                claimSpecialDescription(
                  record,
                  activeDid,
                  allMyDids,
                  allContacts,
                )
              }}
              <a @click="onClickLoadClaim(record.id)">
                <font-awesome
                  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
        <font-awesome 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 v-model="someoneGave" type="checkbox" class="h-6 w-6" />
        <span class="pb-2 pl-2 pr-2">The group provided</span>
        <span v-if="someoneGave">
          <input
            v-model="description"
            type="text"
            size="20"
            class="border border-slate-400 h-6 px-2"
          />
          <br />
          (Everyone likes personalized messages! 😁 ... and for a pic:
          <input v-model="supplyGiftDetails" type="checkbox" />)
        </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
        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"
        @click="record()"
      >
        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, USE_DEXIE_DB } from "../constants/app";
import * as databaseUtil from "../db/databaseUtil";
import {
  accountsDBPromise,
  db,
  retrieveSettingsForActiveAccount,
} from "../db/index";
import { Contact } from "../db/tables/contacts";
import {
  GenericCredWrapper,
  GenericVerifiableCredential,
  CreateAndSubmitClaimResult,
} from "../interfaces";
import {
  BVC_MEETUPS_PROJECT_CLAIM_ID,
  claimSpecialDescription,
  containsHiddenDid,
  createAndSubmitConfirmation,
  createAndSubmitGive,
  getHeaders,
} from "../libs/endorserServer";
import { logger } from "../utils/logger";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
import { retrieveAllAccountsMetadata } from "@/libs/util";
@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;
  supplyGiftDetails = false;

  async created() {
    this.loadingConfirms = true;

    let settings = await databaseUtil.retrieveSettingsForActiveAccount();
    if (USE_DEXIE_DB) {
      settings = await retrieveSettingsForActiveAccount();
    }
    this.apiServer = settings.apiServer || "";
    this.activeDid = settings.activeDid || "";

    const platformService = PlatformServiceFactory.getInstance();
    const contactQueryResult = await platformService.dbQuery(
      "SELECT * FROM contacts",
    );
    this.allContacts = databaseUtil.mapQueryResultToValues(
      contactQueryResult,
    ) as unknown as Contact[];
    if (USE_DEXIE_DB) {
      await db.open();
      this.allContacts = await db.contacts.toArray();
    }

    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,
      }) || "";

    this.allMyDids = (await retrieveAllAccountsMetadata()).map(
      (account) => account.did,
    );
    if (USE_DEXIE_DB) {
      const accountsDB = await accountsDBPromise;
      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) {
        logger.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) {
      logger.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 {
      if (this.claimsToConfirmSelected.length > 0) {
        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: PromiseSettledResult<CreateAndSubmitClaimResult>[] =
        await Promise.allSettled(
          this.claimsToConfirmSelected.map(async (jwtId) => {
            const record = this.claimsToConfirm.find(
              (claim) => claim.id === jwtId,
            );
            if (!record) {
              return { success: false, 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(
        // 'fulfilled' is the status in a successful PromiseFulfilledResult
        (result) => result.status === "fulfilled" && result.value.success,
      );
      if (confirmsSucceeded.length < this.claimsToConfirmSelected.length) {
        logger.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 && !this.supplyGiftDetails) {
        const giveResult = await createAndSubmitGive(
          axios,
          this.apiServer,
          this.activeDid,
          undefined,
          this.activeDid,
          this.description,
          undefined,
          undefined,
          undefined,
          undefined,
          false,
          undefined,
          BVC_MEETUPS_PROJECT_CLAIM_ID,
        );
        giveSucceeded = giveResult.success;
        if (!giveSucceeded) {
          logger.error("Error sending give:", giveResult);
          this.$notify(
            {
              group: "alert",
              type: "danger",
              title: "Error",
              text:
                (giveResult as CreateAndSubmitClaimResult)?.error ||
                "There was an error sending that give.",
            },
            5000,
          );
        }
      }
      if (this.someoneGave && this.supplyGiftDetails) {
        // we'll give a success message for the confirmations and go to the gifted details page
        if (confirmsSucceeded.length > 0) {
          const actions =
            confirmsSucceeded.length === 1
              ? `Your confirmation has been recorded.`
              : `Your confirmations have been recorded.`;
          this.$notify(
            {
              group: "alert",
              type: "success",
              title: "Success",
              text: actions,
            },
            3000,
          );
        }
        (this.$router as Router).push({
          name: "gifted-details",
          query: {
            description: this.description,
            destinationPathAfter: "/",
            providerProjectId: BVC_MEETUPS_PROJECT_CLAIM_ID,
            recipientDid: this.activeDid,
          },
        });
      } else {
        // just go ahead and print a message for all the activity
        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,
          );
          (this.$router as Router).push({ path: "/" });
        } else {
          // errors should have already shown
        }
      }

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