<!--
  SharedPhotoView.vue - External Image Sharing Handler
  
  Handles images shared to TimeSafari from external applications via deep linking.
  Provides options to use shared images for gifts or profile pictures, with
  integrated image upload and processing capabilities.
  
  Key Features:
  - Process externally shared images from device
  - Upload images to image server with authentication
  - Convert images to gifts (GiveAction) or profile pictures
  - Temporary storage management for shared image data
  - Comprehensive error handling for upload failures
  - Integration with PhotoDialog for profile image cropping
  
  Image Flow:
  1. External app shares image → TimeSafari deep link
  2. Image stored temporarily in database as base64
  3. User chooses: Record Gift, Save as Profile, or Cancel
  4. Image uploaded to server with JWT authentication
  5. Temporary storage cleaned up
  
  Navigation Paths:
  - External Share → SharedPhotoView
  - Record Gift → GiftedDetailsView (with image URL)
  - Save Profile → PhotoDialog → AccountView
  - Cancel → HomeView
  
  Migration Status: ✅ Complete Enhanced Triple Migration Pattern
  - Phase 1: Database Migration (PlatformServiceMixin) ✅
  - Phase 2: SQL Abstraction ($getTemp, $deleteTemp, $accountSettings, $updateSettings) ✅
  - Phase 3: Notification Migration (3 constants, helper methods) ✅
  - Phase 4: Template Streamlining (Simple template) ✅
  
  Author: Matthew Raymer
  Last Updated: 2025-07-07
-->
<template>
  <QuickNav />
  <!-- CONTENT -->
  <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
    <!-- Heading -->
    <h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8">
      Image
    </h1>
    <div v-if="imageBlob">
      <div v-if="uploading" class="text-center mb-4">
        <font-awesome icon="spinner" class="fa-spin-pulse" />
      </div>
      <div v-else>
        <div class="text-center mb-4">Choose how to use this image</div>
        <div class="grid grid-cols-1 sm:grid-cols-3 gap-4">
          <button
            class="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"
            @click="recordGift"
          >
            <font-awesome icon="gift" class="fa-fw" />
            Record a Gift
          </button>
          <button
            class="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"
            @click="recordProfile"
          >
            <font-awesome icon="circle-user" class="fa-fw" />
            Save as Profile Image
          </button>
          <button
            class="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"
            @click="cancel"
          >
            <font-awesome icon="ban" class="fa-fw" />
            Cancel
          </button>
        </div>
        <PhotoDialog ref="photoDialog" />
      </div>

      <div class="flex justify-center">
        <img
          :src="URL.createObjectURL(imageBlob)"
          alt="Shared Image"
          class="rounded mt-4"
        />
      </div>
    </div>
    <div v-else class="text-center mb-4">
      <p>No image found.</p>
      <p class="mt-4">
        If you shared an image, the cause is usually that you do not have the
        recent version of this app, or that the app has not refreshed the
        service code underneath. To fix this, first make sure you have latest
        version by comparing your version at the bottom of "Help" with the
        version at the bottom of https://timesafari.app/help in a browser. After
        that, it may eventually work, but you can speed up the process by
        clearing your data cache (in the browser on mobile, even if you
        installed it) and/or reinstalling the app (after backing up all your
        data, of course).
      </p>
    </div>
  </section>
</template>

<script lang="ts">
import axios from "axios";
import { Component, Vue } from "vue-facing-decorator";
import {
  RouteLocationNormalizedLoaded,
  RouteLocationRaw,
  Router,
} from "vue-router";

import PhotoDialog from "../components/PhotoDialog.vue";
import QuickNav from "../components/QuickNav.vue";
import {
  DEFAULT_IMAGE_API_SERVER,
  IMAGE_TYPE_PROFILE,
  NotificationIface,
} from "../constants/app";
import { accessToken } from "../libs/crypto";
import { base64ToBlob, SHARED_PHOTO_BASE64_KEY } from "../libs/util";
import { logger } from "../utils/logger";

import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
import {
  NOTIFY_SHARED_PHOTO_LOAD_ERROR,
  NOTIFY_SHARED_PHOTO_SAVE_ERROR,
} from "@/constants/notifications";
@Component({
  components: { PhotoDialog, QuickNav },
  mixins: [PlatformServiceMixin],
})
export default class SharedPhotoView extends Vue {
  $notify!: (notification: NotificationIface, timeout?: number) => void;
  $router!: Router;
  $route!: RouteLocationNormalizedLoaded;

  notify!: ReturnType<typeof createNotifyHelpers>;

  /**
   * Active user's DID for authentication and image ownership
   */
  activeDid: string | undefined = undefined;

  /**
   * Blob data of the shared image for processing and upload
   */
  imageBlob: Blob | undefined = undefined;

  /**
   * Original filename of the shared image from external app
   */
  imageFileName: string | undefined = undefined;

  /**
   * Upload status indicator for UI feedback
   */
  uploading = false;

  /**
   * Browser URL API for creating object URLs from blobs
   */
  URL = window.URL || window.webkitURL;

  /**
   * Vue lifecycle hook - Initialize shared image processing
   *
   * Loads the shared image data from temporary storage, retrieves user settings,
   * and prepares the component for image processing. Cleans up temporary storage
   * after successful image loading.
   *
   * @async
   */
  async mounted() {
    this.notify = createNotifyHelpers(this.$notify);

    try {
      const settings = await this.$accountSettings();
      this.activeDid = settings.activeDid;

      const temp = await this.$getTemp(SHARED_PHOTO_BASE64_KEY);
      const imageB64 = temp?.blobB64 as string;
      if (temp) {
        this.imageBlob = base64ToBlob(imageB64);

        // clear the temp image
        await this.$deleteTemp(SHARED_PHOTO_BASE64_KEY);

        this.imageFileName = this.$route.query["fileName"] as string;
      } else {
        logger.error("No appropriate image found in temp storage.", temp);
      }
    } catch (err: unknown) {
      logger.error("Got an error loading an identifier:", err);
      this.notify.error(
        NOTIFY_SHARED_PHOTO_LOAD_ERROR.message,
        TIMEOUTS.STANDARD,
      );
    }
  }

  /**
   * Process shared image as a gift record
   *
   * Uploads the shared image to the server as a GiveAction and navigates
   * to the gift details view with the uploaded image URL for gift recording.
   *
   * @async
   */
  async recordGift() {
    await this.sendToImageServer("GiveAction").then((url) => {
      if (url) {
        const route = {
          name: "gifted-details",
          // this might be wrong since "name" goes with params, but it works so test well when you change it
          query: {
            destinationPathAfter: "/",
            hideBackButton: "true",
            imageUrl: url,
            recipientDid: this.activeDid,
          },
        } as RouteLocationRaw;
        this.$router.push(route);
      }
    });
  }

  /**
   * Process shared image as profile picture
   *
   * Opens the PhotoDialog component for image cropping and profile picture
   * processing. Updates user settings with the new profile image URL and
   * navigates to the account view upon completion.
   */
  recordProfile() {
    (this.$refs.photoDialog as PhotoDialog).open(
      async (imgUrl) => {
        await this.$updateSettings({ profileImageUrl: imgUrl });
        this.$router.push({ name: "account" });
      },
      IMAGE_TYPE_PROFILE,
      true,
      this.imageBlob,
      this.imageFileName,
    );
  }

  /**
   * Cancel image processing and return to home
   *
   * Clears the shared image data and navigates back to the home view,
   * effectively canceling the image sharing operation.
   *
   * @async
   */
  async cancel() {
    this.imageBlob = undefined;
    this.imageFileName = undefined;
    this.$router.push({ name: "home" });
  }

  /**
   * Upload image to the image server with comprehensive error handling
   *
   * Sends the shared image to the configured image server with JWT authentication.
   * Provides detailed error handling for various failure scenarios including
   * authentication, file size, format, and server errors.
   *
   * @param imageType - The type of image claim (e.g., "GiveAction")
   * @returns Promise<string | undefined> - The uploaded image URL or undefined on failure
   * @async
   */
  async sendToImageServer(imageType: string) {
    this.uploading = true;

    let result;
    try {
      // send the image to the server
      const token = await accessToken(this.activeDid);
      const headers = {
        Authorization: "Bearer " + token,
        // axios fills in Content-Type of multipart/form-data
      };
      const formData = new FormData();
      formData.append(
        "image",
        this.imageBlob as Blob,
        this.imageFileName as string,
      );
      formData.append("claimType", imageType);

      if (
        window.location.hostname === "localhost" &&
        !DEFAULT_IMAGE_API_SERVER.includes("localhost")
      ) {
        logger.log(
          "Using shared image API server, so only users on that server can play with images.",
        );
      }
      const response = await axios.post(
        DEFAULT_IMAGE_API_SERVER + "/image",
        formData,
        { headers },
      );
      if (response?.data?.url) {
        this.imageBlob = undefined;
        this.imageFileName = undefined;
        result = response.data.url as string;
      } else {
        logger.error("Problem uploading the image", response.data);
        const errorMessage =
          NOTIFY_SHARED_PHOTO_SAVE_ERROR.message +
          " " +
          (response?.data?.message || "");
        this.notify.error(errorMessage, TIMEOUTS.LONG);
      }

      this.uploading = false;
    } catch (error) {
      // Log the raw error first
      logger.error("Raw error object:", JSON.stringify(error, null, 2));

      let errorMessage = "There was an error saving the picture.";

      if (axios.isAxiosError(error)) {
        const status = error.response?.status;
        const statusText = error.response?.statusText;
        const data = error.response?.data;

        // Log detailed error information
        logger.error("Upload error details:", {
          status,
          statusText,
          data: JSON.stringify(data, null, 2),
          message: error.message,
          config: {
            url: error.config?.url,
            method: error.config?.method,
            headers: error.config?.headers,
          },
        });

        if (status === 401) {
          errorMessage = "Authentication failed. Please try logging in again.";
        } else if (status === 413) {
          errorMessage = "Image file is too large. Please try a smaller image.";
        } else if (status === 415) {
          errorMessage =
            "Unsupported image format. Please try a different image.";
        } else if (status && status >= 500) {
          errorMessage = "Server error. Please try again later.";
        } else if (data?.message) {
          errorMessage = data.message;
        }
      } else if (error instanceof Error) {
        // Log non-Axios error with full details
        logger.error("Non-Axios error details:", {
          name: error.name,
          message: error.message,
          stack: error.stack,
          error: JSON.stringify(error, Object.getOwnPropertyNames(error), 2),
        });
      } else {
        // Log any other type of error
        logger.error("Unknown error type:", {
          error: JSON.stringify(error, null, 2),
          type: typeof error,
        });
      }

      this.notify.error(errorMessage, TIMEOUTS.LONG);
      this.uploading = false;
    }
    return result;
  }
}
</script>
