<template>
  <QuickNav />
  <!-- CONTENT -->
  <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
    <!-- Breadcrumb -->
    <div id="ViewBreadcrumb" class="mb-8">
      <h1 class="text-lg text-center font-light relative px-7">
        <!-- Back -->
        <button
          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" />
        </button>
        Raw Claim
      </h1>
    </div>

    <div class="flex">
      <textarea v-model="claimStr" rows="20" class="border-2 w-full"></textarea>
    </div>
    <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"
      @click="submitClaim()"
    >
      Sign &amp; Send
    </button>
  </section>
</template>

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

import QuickNav from "../components/QuickNav.vue";
import { NotificationIface } from "../constants/app";
import { logConsoleAndDb, retrieveSettingsForActiveAccount } from "../db/index";
import * as serverUtil from "../libs/endorserServer";
import * as libsUtil from "../libs/util";
import { errorStringForLog } from "../libs/endorserServer";
import { Router, RouteLocationNormalizedLoaded } from "vue-router";
import { logger } from "../utils/logger";

/**
 * View component for adding or editing raw claim data
 * Allows direct JSON editing of claim data with validation and submission
 */
@Component({
  components: { QuickNav },
})
export default class ClaimAddRawView extends Vue {
  $notify!: (notification: NotificationIface, timeout?: number) => void;
  $route!: RouteLocationNormalizedLoaded;
  $router!: Router;
  axios!: AxiosInstance;

  accountIdentityStr: string = "null";
  activeDid = "";
  apiServer = "";
  claimStr = "";

  /**
   * Lifecycle hook that initializes the view
   * Workflow:
   * 1. Retrieves active DID and API server from settings
   * 2. Checks for existing claim data from query params:
   *    - If "claim" param exists: Parses and formats JSON
   *    - If "claimJwtId" param exists: Fetches claim data from API
   * 3. Populates textarea with formatted claim data
   */
  async mounted() {
    await this.initializeSettings();
    await this.loadClaimData();
  }

  /**
   * Initialize settings from active account
   */
  private async initializeSettings() {
    const settings = await retrieveSettingsForActiveAccount();
    this.activeDid = settings.activeDid || "";
    this.apiServer = settings.apiServer || "";
  }

  /**
   * Load claim data from query parameters or API
   */
  private async loadClaimData() {
    // Try loading from direct claim parameter
    if (await this.loadClaimFromQueryParam()) return;

    // Try loading from claim JWT ID
    await this.loadClaimFromJwtId();
  }

  /**
   * Attempt to load claim from query parameter
   * @returns true if claim was loaded successfully
   */
  private async loadClaimFromQueryParam(): Promise<boolean> {
    this.claimStr = (this.$route.query["claim"] as string) || "";
    if (!this.claimStr) return false;

    try {
      const veriClaim = JSON.parse(this.claimStr);
      this.claimStr = JSON.stringify(veriClaim, null, 2);
      return true;
    } catch (e) {
      // ignore parse error
      return false;
    }
  }

  /**
   * Load claim data from JWT ID via API
   */
  private async loadClaimFromJwtId() {
    const claimJwtId = (this.$route.query["claimJwtId"] as string) || "";
    if (!claimJwtId) return;

    const urlPath = libsUtil.isGlobalUri(claimJwtId)
      ? "/api/claim/byHandle/"
      : "/api/claim/";
    const url = this.apiServer + urlPath + encodeURIComponent(claimJwtId);

    try {
      const response = await this.fetchClaimData(url);
      this.formatClaimResponse(response, claimJwtId);
    } catch (error: unknown) {
      this.handleClaimError(error);
    }
  }

  /**
   * Fetch claim data from API
   */
  private async fetchClaimData(url: string) {
    const headers = await serverUtil.getHeaders(this.activeDid);
    return await this.axios.get(url, { headers });
  }

  /**
   * Format successful claim response data
   */
  private formatClaimResponse(response: unknown, claimJwtId: string) {
    if (response.status === 200) {
      const claim = response.data?.claim;
      claim.lastClaimId = serverUtil.stripEndorserPrefix(claimJwtId);
      this.claimStr = JSON.stringify(claim, null, 2);
    } else {
      throw {
        message: "Got an error loading that claim.",
        response: {
          status: response.status,
          statusText: response.statusText,
        },
      };
    }
  }

  /**
   * Handle error loading claim data
   */
  private handleClaimError(error: unknown) {
    logConsoleAndDb(
      "Error retrieving claim: " + errorStringForLog(error),
      true,
    );
    this.$notify(
      {
        group: "alert",
        type: "danger",
        title: "Error",
        text: "Got an error retrieving claim data.",
      },
      3000,
    );
  }

  /**
   * Submits the edited claim data
   * Workflow:
   * 1. Parses JSON from textarea
   * 2. Sends to server via createAndSubmitClaim
   * 3. Shows success/error notification
   * @throws Will show error notification if submission fails
   */
  async submitClaim() {
    const fullClaim = JSON.parse(this.claimStr);
    const result = await serverUtil.createAndSubmitClaim(
      fullClaim,
      this.activeDid,
      this.apiServer,
      this.axios,
    );
    if (result.type === "success") {
      this.$notify(
        {
          group: "alert",
          type: "success",
          title: "Success",
          text: "Claim submitted.",
        },
        5000,
      );
    } else {
      logger.error("Got error submitting the claim:", result);
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error",
          text: "There was a problem submitting the claim.",
        },
        5000,
      );
    }
  }
}
</script>
