<template>
  <!-- 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">
        <!-- Cancel -->
        <router-link
          :to="{ name: 'project' }"
          class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
          ><fa icon="chevron-left" class="fa-fw"></fa
        ></router-link>
        [New/Edit] Plan
      </h1>
    </div>

    <!-- Project Details -->
    <!-- Image - (see design model) Empty -->

    <div>
      {{ errorMessage }}
    </div>

    <input
      type="text"
      placeholder="Project Name"
      class="block w-full rounded border border-slate-400 mb-4 px-3 py-2"
      v-model="projectName"
    />

    <textarea
      placeholder="Description"
      class="block w-full rounded border border-slate-400 mb-4 px-3 py-2"
      rows="5"
      v-model="description"
      maxlength="500"
    ></textarea>
    <div class="text-xs text-slate-500 italic -mt-3 mb-4">
      {{ description.length }}/500 max. characters
    </div>

    <div class="mt-8">
      <button
        :disabled="isHiddenSave"
        class="block w-full text-center text-lg font-bold uppercase bg-blue-600 text-white px-2 py-3 rounded-md mb-2"
        @click="onSaveProjectClick()"
      >
        <!-- SHOW if in idle state -->
        <span :class="{ hidden: isHiddenSave }">Save Project</span>

        <!-- SHOW if in saving state; DISABLE button while in saving state -->
        <span :class="{ hidden: isHiddenSpinner }">
          <!-- icon no worky? -->
          <i class="fa-solid fa-spinner fa-spin-pulse"></i>
          Saving&hellip;</span
        >
      </button>
      <button
        type="button"
        class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md"
        @click="onCancelClick()"
      >
        Cancel
      </button>
    </div>
    <AlertMessage
      :alertTitle="alertTitle"
      :alertMessage="alertMessage"
    ></AlertMessage>
  </section>
</template>

<script lang="ts">
import { AxiosError } from "axios";
import * as didJwt from "did-jwt";
import * as R from "ramda";
import { Component, Vue } from "vue-facing-decorator";

import { accountsDB, db } from "@/db";
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
import { accessToken, SimpleSigner } from "@/libs/crypto";
import { useAppStore } from "@/store/app";
import { IIdentifier } from "@veramo/core";
import AlertMessage from "@/components/AlertMessage";

interface VerifiableCredential {
  "@context": string;
  "@type": string;
  name: string;
  description: string;
  identifier?: string;
}

@Component({
  components: { AlertMessage },
})
export default class NewEditProjectView extends Vue {
  activeDid = "";
  apiServer = "";
  projectName = "";
  description = "";
  errorMessage = "";

  projectId = localStorage.getItem("projectId") || "";
  isHiddenSave = false;
  isHiddenSpinner = true;

  // 'created' hook runs when the Vue instance is first created
  async created() {
    await db.open();
    const settings = await db.settings.get(MASTER_SETTINGS_KEY);
    this.activeDid = settings?.activeDid || "";
    this.apiServer = settings?.apiServer || "";

    if (this.projectId) {
      await accountsDB.open();
      const num_accounts = await accountsDB.accounts.count();
      if (num_accounts === 0) {
        console.error("Error: no account was found.");
      } else {
        const accounts = await accountsDB.accounts.toArray();
        const account = R.find((acc) => acc.did === this.activeDid, accounts);
        const identity = JSON.parse(account?.identity || "null");
        if (!identity) {
          throw new Error("No identity found.");
        }
        this.LoadProject(identity);
      }
    }
  }

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

    try {
      const resp = await this.axios.get(url, { headers });
      if (resp.status === 200) {
        const claim = resp.data.claim;
        this.projectName = claim.name;
        this.description = claim.description;
      }
    } catch (error) {
      console.error("Got error retrieving that project", error);
    }
  }

  private async SaveProject(identity: IIdentifier) {
    // Make a claim
    const vcClaim: VerifiableCredential = {
      "@context": "https://schema.org",
      "@type": "PlanAction",
      name: this.projectName,
      description: this.description,
      identifier: this.projectId || undefined,
    };
    if (this.projectId) {
      vcClaim.identifier = this.projectId;
    }
    // Make a payload for the claim
    const vcPayload = {
      vc: {
        "@context": ["https://www.w3.org/2018/credentials/v1"],
        type: ["VerifiableCredential"],
        credentialSubject: vcClaim,
      },
    };
    // create a signature using private key of identity
    if (identity.keys[0].privateKeyHex != null) {
      const privateKeyHex: string = identity.keys[0].privateKeyHex;
      const signer = await SimpleSigner(privateKeyHex);
      const alg = undefined;
      // create a JWT for the request
      const vcJwt: string = await didJwt.createJWT(vcPayload, {
        alg: alg,
        issuer: identity.did,
        signer: signer,
      });

      // Make the xhr request payload

      const payload = JSON.stringify({ jwtEncoded: vcJwt });
      const url = this.apiServer + "/api/v2/claim";
      const token = await accessToken(identity);
      const headers = {
        "Content-Type": "application/json",
        Authorization: "Bearer " + token,
      };

      try {
        const resp = await this.axios.post(url, payload, { headers });
        // handleId is new in server v release-1.6.0; remove fullIri when that
        // version shows up here: https://endorser.ch:3000/api-docs/
        if (resp.data?.success?.handleId || resp.data?.success?.fullIri) {
          this.errorMessage = "";
          this.alertTitle = "";
          this.alertMessage = "";
          // handleId is new in server v release-1.6.0; remove fullIri when that
          // version shows up here: https://endorser.ch:3000/api-docs/
          useAppStore().setProjectId(
            resp.data.success.handleId || resp.data.success.fullIri
          );
          setTimeout(
            function (that: Vue) {
              const route = {
                name: "project",
              };
              that.$router.push(route);
            },
            2000,
            this
          );
        }
      } catch (error) {
        let userMessage = "There was an error saving the project.";
        const serverError = error as AxiosError;
        if (serverError) {
          if (Object.prototype.hasOwnProperty.call(serverError, "message")) {
            console.log(serverError);
            this.alertTitle = "User Message";
            userMessage = serverError.response.data.error.message; // This is info for the user.
            this.alertMessage = userMessage;
          } else {
            this.alertTitle = "Server Message";
            this.alertMessage = JSON.stringify(serverError.toJSON());
          }
        } else {
          console.error(
            "Here's the full error trying to save the claim:",
            error
          );
          this.alertTitle = "Claim Error";
          this.alertMessage = error as string;
        }
        // Now set that error for the user to see.
        this.errorMessage = userMessage;
      }
    }
  }

  public async onSaveProjectClick() {
    this.isHiddenSave = true;
    this.isHiddenSpinner = false;
    await accountsDB.open();
    const num_accounts = await accountsDB.accounts.count();
    if (num_accounts === 0) {
      console.error("Error: there is no account.");
    } else {
      const accounts = await accountsDB.accounts.toArray();
      const account = R.find((acc) => acc.did === this.activeDid, accounts);
      const identity = JSON.parse(account?.identity || "null");
      if (!identity) {
        throw new Error("No identity found.");
      }
      this.SaveProject(identity);
    }
  }

  public onCancelClick() {
    this.$router.back();
  }

  // This same popup code is in many files.
  alertTitle = "";
  alertMessage = "";
}
</script>
