<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="cancel()"
      >
        <fa icon="chevron-left" class="fa-fw"></fa>
      </h1>
    </div>

    <!-- Heading -->
    <h1 class="text-4xl text-center font-light px-4 mb-4">What Was Given</h1>

    <h1 class="text-xl font-bold text-center mb-4">
      {{ message }} {{ giverName || "somebody not named" }}
    </h1>
    <textarea
      class="block w-full rounded border border-slate-400 mb-2 px-3 py-2"
      placeholder="What was received"
      v-model="description"
    />
    <div class="flex flex-row justify-center">
      <span
        class="rounded-l border border-r-0 border-slate-400 bg-slate-200 text-center text-blue-500 px-2 py-2 w-20"
        @click="changeUnitCode()"
      >
        {{ libsUtil.UNIT_SHORT[unitCode] }}
      </span>
      <div
        class="border border-r-0 border-slate-400 bg-slate-200 px-4 py-2"
        @click="amountInput === '0' ? null : decrement()"
      >
        <fa icon="chevron-left" />
      </div>
      <input
        type="number"
        class="border border-r-0 border-slate-400 px-2 py-2 text-center w-20"
        v-model="amountInput"
      />
      <div
        class="rounded-r border border-slate-400 bg-slate-200 px-4 py-2"
        @click="increment()"
      >
        <fa icon="chevron-right" />
      </div>
    </div>

    <div class="flex justify-center mt-4">
      <span v-if="imageUrl" class="flex justify-between">
        <a :href="imageUrl" target="_blank" class="text-blue-500 ml-4">
          <img :src="imageUrl" class="h-24 rounded-xl" />
        </a>
        <fa
          icon="trash-can"
          @click="confirmDeleteImage"
          class="text-red-500 fa-fw ml-8 mt-10"
        />
      </span>
      <span v-else>
        <fa
          icon="camera"
          class="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-2 rounded-md"
          @click="openPhotoDialog"
        />
      </span>
    </div>
    <GiftedPhotoDialog ref="photoDialog" />

    <div v-if="projectId" class="mt-4">
      <fa
        icon="check"
        class="bg-slate-500 text-white h-5 w-5 px-0.5 py-0.5 mr-2 rounded"
      />
      <label class="text-sm">This is given to a project</label>
    </div>

    <div v-if="!projectId" class="mt-4">
      <input type="checkbox" class="h-6 w-6 mr-2" v-model="givenToUser" />
      <label class="text-sm">Given to you</label>
    </div>

    <div class="mt-4">
      <input type="checkbox" class="h-6 w-6 mr-2" v-model="isTrade" />
      <label class="text-sm">Trade (not a gift)</label>
    </div>

    <p class="text-center mb-2 mt-6 italic">
      Sign & Send to publish to the world
    </p>
    <button
      class="block w-full text-center text-lg font-bold uppercase 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 mb-2"
      @click="confirm"
    >
      Sign &amp; Send
    </button>
    <button
      class="block w-full text-center text-md 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 px-1.5 py-2 rounded-md"
      @click="cancel"
    >
      Cancel
    </button>
  </section>
</template>

<script lang="ts">
import { Component, Vue } from "vue-facing-decorator";

import { DEFAULT_IMAGE_API_SERVER, NotificationIface } from "@/constants/app";
import QuickNav from "@/components/QuickNav.vue";
import TopMessage from "@/components/TopMessage.vue";
import { db } from "@/db/index";
import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings";
import { createAndSubmitGive } from "@/libs/endorserServer";
import * as libsUtil from "@/libs/util";
import { accessToken } from "@/libs/crypto";
import GiftedDialog from "@/components/GiftedDialog.vue";
import GiftedPhotoDialog from "@/components/GiftedPhotoDialog.vue";

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

  activeDid = "";
  apiServer = "";

  amountInput = "0";
  description = "";
  givenToUser = false;
  giverDid: string | undefined;
  giverName = "";
  imageUrl = "";
  isTrade = false;
  message = "";
  offerId = "";
  projectId = "";
  unitCode = "HUR";

  libsUtil = libsUtil;

  async mounted() {
    this.amountInput = this.$route.query.amountInput as string;
    this.description = this.$route.query.description as string;
    this.giverDid = this.$route.query.giverDid as string;
    this.giverName = this.$route.query.giverName as string;
    this.message = this.$route.query.message as string;
    this.offerId = this.$route.query.offerId as string;
    this.projectId = this.$route.query.projectId as string;
    this.unitCode = this.$route.query.unitCode as string;

    this.imageUrl = localStorage.getItem("imageUrl") || "";

    this.givenToUser = !this.projectId;

    try {
      await db.open();
      const settings = (await db.settings.get(MASTER_SETTINGS_KEY)) as Settings;
      this.apiServer = settings?.apiServer || "";
      this.activeDid = settings?.activeDid || "";

      // eslint-disable-next-line @typescript-eslint/no-explicit-any
    } catch (err: any) {
      console.error("Error retrieving settings from database:", err);
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error",
          text: err.message || "There was an error retrieving your settings.",
        },
        -1,
      );
    }
  }

  changeUnitCode() {
    const units = Object.keys(this.libsUtil.UNIT_SHORT);
    const index = units.indexOf(this.unitCode);
    this.unitCode = units[(index + 1) % units.length];
  }

  increment() {
    this.amountInput = `${(parseFloat(this.amountInput) || 0) + 1}`;
  }

  decrement() {
    this.amountInput = `${Math.max(
      0,
      (parseFloat(this.amountInput) || 1) - 1,
    )}`;
  }

  cancel() {
    this.deleteImage(); // not awaiting, so they'll go back immediately
    this.$router.back();
  }

  openPhotoDialog() {
    (this.$refs.photoDialog as GiftedPhotoDialog).open((imgUrl) => {
      this.imageUrl = imgUrl;
    });
  }

  confirmDeleteImage() {
    this.$notify(
      {
        group: "modal",
        type: "confirm",
        title: "Are you sure you want to delete the image?",
        text: "",
        onYes: this.deleteImage,
      },
      -1,
    );
  }

  async deleteImage() {
    if (!this.imageUrl) {
      return;
    }
    try {
      const identity = await libsUtil.getIdentity(this.activeDid);
      const token = await accessToken(identity);
      const response = await this.axios.delete(
        DEFAULT_IMAGE_API_SERVER +
          "/image/" +
          encodeURIComponent(this.imageUrl),
        {
          headers: {
            Authorization: `Bearer ${token}`,
          },
        },
      );
      if (response.status === 204) {
        // don't bother with a notification
        // (either they'll simply continue or they're canceling and going back)
      } else {
        console.error("Non-success deleting image:", response);
        this.$notify(
          {
            group: "alert",
            type: "danger",
            title: "Error",
            text: "There was a problem deleting the image.",
          },
          5000,
        );
        // keep the imageUrl in localStorage so the user can try again if they want
        return;
      }

      localStorage.removeItem("imageUrl");
      this.imageUrl = "";
    } catch (error) {
      console.error("Error deleting image:", error);
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      if ((error as any).response.status === 404) {
        console.log("The image was already deleted:", error);

        localStorage.removeItem("imageUrl");
        this.imageUrl = "";

        // it already doesn't exist so we won't say anything to the user
      } else {
        this.$notify(
          {
            group: "alert",
            type: "danger",
            title: "Error",
            text: "There was an error deleting the image.",
          },
          5000,
        );
      }
    }
  }

  async confirm() {
    if (!this.activeDid) {
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error",
          text: "You must select an identifier before you can record a give.",
        },
        2000,
      );
      return;
    }
    if (parseFloat(this.amountInput) < 0) {
      this.$notify(
        {
          group: "alert",
          type: "danger",
          text: "You may not send a negative number.",
          title: "",
        },
        2000,
      );
      return;
    }
    if (!this.description && !parseFloat(this.amountInput)) {
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error",
          text: `You must enter a description or some number of ${
            this.libsUtil.UNIT_LONG[this.unitCode]
          }.`,
        },
        2000,
      );
      return;
    }

    this.$notify(
      {
        group: "alert",
        type: "toast",
        text: "Recording the give...",
        title: "",
      },
      1000,
    );

    // this is asynchronous, but we don't need to wait for it to complete
    await this.recordGive();
  }

  /**
   *
   * @param giverDid may be null
   * @param description may be an empty string
   * @param amountInput may be 0
   * @param unitCode may be omitted, defaults to "HUR"
   */
  public async recordGive() {
    try {
      const identity = await libsUtil.getIdentity(this.activeDid);
      const result = await createAndSubmitGive(
        this.axios,
        this.apiServer,
        identity,
        this.giverDid,
        this.givenToUser ? this.activeDid : undefined,
        this.description,
        parseFloat(this.amountInput),
        this.unitCode,
        this.projectId,
        this.offerId,
        this.isTrade,
        this.imageUrl,
      );

      if (
        result.type === "error" ||
        this.isGiveCreationError(result.response)
      ) {
        const errorMessage = this.getGiveCreationErrorMessage(result);
        console.error("Error with give creation result:", result);
        this.$notify(
          {
            group: "alert",
            type: "danger",
            title: "Error",
            text: errorMessage || "There was an error creating the give.",
          },
          -1,
        );
      } else {
        this.$notify(
          {
            group: "alert",
            type: "success",
            title: "Success",
            text: `That ${this.isTrade ? "trade" : "gift"} was recorded.`,
          },
          5000,
        );
        localStorage.removeItem("imageUrl");
        this.$router.back();
      }
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
    } catch (error: any) {
      console.error("Error with give recordation caught:", error);
      const message =
        error.userMessage ||
        error.response?.data?.error?.message ||
        "There was an error recording the give.";
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error",
          text: message,
        },
        -1,
      );
    }
  }

  // Helper functions for readability

  /**
   * @param result response "data" from the server
   * @returns true if the result indicates an error
   */
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  isGiveCreationError(result: any) {
    return result.status !== 201 || result.data?.error;
  }

  /**
   * @param result direct response eg. ErrorResult or SuccessResult (potentially with embedded "data")
   * @returns best guess at an error message
   */
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  getGiveCreationErrorMessage(result: any) {
    return (
      result.error?.userMessage ||
      result.error?.error ||
      result.response?.data?.error?.message
    );
  }
}
</script>
