<template>
  <!-- CONTENT -->
  <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
    <!-- Breadcrumb -->
    <div class="mb-8">
      <!-- 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 pt-4">
        <span v-if="uploading"> Uploading... </span>
        <span v-else-if="blob"> Look Good? </span>
        <span v-else> Say "Cheese"! </span>
      </h1>
    </div>

    <div v-if="uploading" class="flex justify-center">
      <fa icon="spinner" class="fa-spin fa-3x text-center block" />
    </div>
    <div v-else-if="blob">
      <img :src="URL.createObjectURL(blob)" class="w-full" />
      <div class="flex justify-around mt-2">
        <button
          @click="uploadImage"
          class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-2 rounded-full"
        >
          <span>Upload</span>
        </button>
        <button
          @click="retryImage"
          class="bg-slate-500 hover:bg-slate-700 text-white font-bold py-2 px-2 rounded-full"
        >
          <span>Retry</span>
        </button>
      </div>
    </div>
    <div v-else>
      <!--
        Camera "resolution" doesn't change how it shows on screen but rather stretches the result, eg the following which just stretches it vertically:
        :resolution="{ width: 375, height: 812 }"
      -->
      <camera facingMode="environment" autoplay ref="camera">
        <div class="absolute bottom-0 w-full flex justify-center pb-4">
          <!-- Button -->
          <button
            @click="takeImage"
            class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-2 rounded-full"
          >
            <fa icon="camera" class="fa-fw"></fa>
          </button>
        </div>
      </camera>
    </div>
  </section>
</template>

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

import { DEFAULT_IMAGE_API_SERVER, NotificationIface } from "@/constants/app";
import { getIdentity } from "@/libs/util";
import { db } from "@/db/index";
import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings";
import { accessToken } from "@/libs/crypto";

@Component({ components: { Camera } })
export default class GiftedPhoto extends Vue {
  $notify!: (notification: NotificationIface, timeout?: number) => void;

  activeDid = "";
  blob: Blob | null = null;
  uploading = false;

  URL = window.URL || window.webkitURL;

  async mounted() {
    try {
      await db.open();
      const settings = (await db.settings.get(MASTER_SETTINGS_KEY)) as Settings;
      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,
      );
    }
  }

  async takeImage(/* payload: MouseEvent */) {
    const cameraComponent = this.$refs.camera as InstanceType<typeof Camera>;
    this.blob = await cameraComponent?.snapshot();
    if (!this.blob) {
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error",
          text: "There was an error taking the picture. Please try again.",
        },
        5000,
      );
      return;
    }
  }

  async retryImage() {
    this.blob = null;
  }

  async uploadImage() {
    this.uploading = true;
    const identifier = await getIdentity(this.activeDid);
    const token = await accessToken(identifier);
    const headers = {
      Authorization: "Bearer " + token,
    };
    const formData = new FormData();
    if (!this.blob) {
      // yeah, this should never happen, but it helps with subsequent type checking
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error",
          text: "There was an error finding the picture. Please try again.",
        },
        5000,
      );
      this.uploading = false;
      return;
    }
    formData.append("image", this.blob, "snapshot.jpg");
    try {
      const response = await axios.post(
        DEFAULT_IMAGE_API_SERVER + "/image",
        formData,
        { headers },
      );
      this.uploading = false;

      this.$notify(
        {
          group: "alert",
          type: "success",
          title: "Stored",
          text: "Your picture has been stored.",
        },
        3000,
      );
      localStorage.setItem("imageUrl", response.data.url as string);
      this.$router.back();
    } catch (error) {
      console.error("Error uploading the image", error);
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error",
          text: "There was an error saving the picture. Please try again.",
        },
        5000,
      );
      this.uploading = false;
      this.blob = null;
    }
  }
}
</script>
