/** * OfferDialog.vue - Dialog component for creating and submitting offers * *
Features: * - Offer creation with description and amount * - Unit code selection
(HUR, etc.) * - Expiration date handling * - Recipient and project targeting * -
Real-time validation and submission * - Comprehensive error handling and user
feedback * - Navigation to detailed offer configuration * * @author Matthew
Raymer */
<template>
  <div v-if="visible" class="dialog-overlay">
    <div class="dialog">
      <h1 class="text-xl font-bold text-center mb-4">Offer Help</h1>
      <input
        v-model="description"
        type="text"
        data-testId="inputDescription"
        class="block w-full rounded border border-slate-400 mb-2 px-3 py-2"
        placeholder="Description of what is offered"
      />
      <div class="flex mb-4">
        <AmountInput
          :value="parseFloat(amountInput) || 0"
          :on-update-value="handleAmountUpdate"
          data-testId="inputOfferAmount"
        />

        <select
          v-model="amountUnitCode"
          class="flex-1 rounded border border-slate-400 ms-2 px-3 py-2"
        >
          <option
            v-for="(displayName, code) in unitOptions"
            :key="code"
            :value="code"
          >
            {{ displayName }}
          </option>
        </select>
      </div>
      <div class="mt-4 flex justify-center">
        <span>
          <router-link :to="offerDetailsRoute" class="text-blue-500">
            Conditions & more options...
          </router-link>
        </span>
      </div>
      <p class="text-center mt-6 mb-2 italic">
        Sign & Send to publish to the world
      </p>
      <div class="grid grid-cols-1 sm:grid-cols-2 gap-2">
        <button :class="primaryButtonClasses" @click="confirm">
          Sign &amp; Send
        </button>
        <button :class="secondaryButtonClasses" @click="cancel">Cancel</button>
      </div>
    </div>
  </div>
</template>

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

import { NotificationIface } from "../constants/app";
import { createAndSubmitOffer } from "../libs/endorserServer";
import * as libsUtil from "../libs/util";
import { logger } from "../utils/logger";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
import { showSeedPhraseReminder } from "@/utils/seedPhraseReminder";
import {
  NOTIFY_OFFER_SETTINGS_ERROR,
  NOTIFY_OFFER_RECORDING,
  NOTIFY_OFFER_IDENTITY_REQUIRED,
  NOTIFY_OFFER_DESCRIPTION_REQUIRED,
  NOTIFY_OFFER_CREATION_ERROR,
  NOTIFY_OFFER_SUCCESS,
  NOTIFY_OFFER_SUBMISSION_ERROR,
  NOTIFY_OFFER_ERROR_NEGATIVE_AMOUNT,
} from "@/constants/notifications";
import AmountInput from "./AmountInput.vue";

@Component({
  mixins: [PlatformServiceMixin],
  components: {
    AmountInput,
  },
})
export default class OfferDialog extends Vue {
  @Prop projectId?: string;
  @Prop projectName?: string;

  // Vue notification system
  $notify!: (notification: NotificationIface, timeout?: number) => void;

  // Notification system
  notify!: ReturnType<typeof createNotifyHelpers>;

  // Component state
  activeDid = "";
  apiServer = "";
  amountInput = "0";
  amountUnitCode = "HUR";
  description = "";
  expirationDateInput = "";
  recipientDid? = "";
  recipientName? = "";
  visible = false;

  libsUtil = libsUtil;

  // =================================================
  // COMPUTED PROPERTIES - Template Logic Streamlining
  // =================================================

  /**
   * CSS classes for the primary action button (Sign & Send)
   * Reduces template complexity for gradient button styling
   */
  get primaryButtonClasses(): string {
    return "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";
  }

  /**
   * CSS classes for the secondary action button (Cancel)
   * Reduces template complexity for gradient button styling
   */
  get secondaryButtonClasses(): string {
    return "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";
  }

  /**
   * Computed property to get unit options for the select dropdown
   */
  get unitOptions() {
    return this.libsUtil.UNIT_SHORT;
  }

  /**
   * Router configuration object for offer details navigation
   * Consolidates complex query parameter object from template
   */
  get offerDetailsRoute(): object {
    return {
      name: "offer-details",
      query: {
        amountInput: this.amountInput,
        description: this.description,
        offererDid: this.activeDid,
        projectId: this.projectId,
        projectName: this.projectName,
        recipientDid: this.recipientDid,
        recipientName: this.recipientName,
        unitCode: this.amountUnitCode,
      },
    };
  }

  // =================================================
  // COMPONENT METHODS
  // =================================================

  /**
   * Vue lifecycle hook - Initialize notification helpers
   */
  mounted() {
    this.notify = createNotifyHelpers(this.$notify);
  }

  /**
   * Open the dialog and load account settings
   * @param recipientDid - Optional recipient DID
   * @param recipientName - Optional recipient name
   */
  async open(recipientDid?: string, recipientName?: string) {
    try {
      this.recipientDid = recipientDid;
      this.recipientName = recipientName;

      const settings = await this.$accountSettings();
      this.apiServer = settings.apiServer || "";
      this.activeDid = settings.activeDid || "";

      // eslint-disable-next-line @typescript-eslint/no-explicit-any
    } catch (err: any) {
      logger.error("Error retrieving settings from database:", err);
      this.notify.error(
        err.message || NOTIFY_OFFER_SETTINGS_ERROR.message,
        TIMEOUTS.MODAL,
      );
    }

    this.visible = true;
  }

  /**
   * Close the dialog without changing values
   */
  close() {
    // close the dialog but don't change values (since it might be submitting info)
    this.visible = false;
  }

  /**
   * Handle amount updates from AmountInput component
   * @param value - New amount value
   */
  handleAmountUpdate(value: number) {
    this.amountInput = value.toString();
  }

  /**
   * Cancel the dialog and clear values
   */
  cancel() {
    this.close();
    this.eraseValues();
  }

  /**
   * Clear form values
   */
  eraseValues() {
    this.description = "";
    this.amountInput = "0";
    this.amountUnitCode = "HUR";
  }

  /**
   * Confirm and submit the offer
   */
  async confirm() {
    if (!this.activeDid) {
      this.notify.error(NOTIFY_OFFER_IDENTITY_REQUIRED.message, TIMEOUTS.LONG);
      return;
    }

    if (parseFloat(this.amountInput) < 0) {
      this.notify.error(
        NOTIFY_OFFER_ERROR_NEGATIVE_AMOUNT.message,
        TIMEOUTS.SHORT,
      );
      return;
    }

    if (!this.description && !parseFloat(this.amountInput)) {
      const message = NOTIFY_OFFER_DESCRIPTION_REQUIRED.message.replace(
        "{unit}",
        this.libsUtil.UNIT_LONG[this.amountUnitCode],
      );
      this.notify.error(message, TIMEOUTS.SHORT);
      return;
    }

    this.close();
    this.notify.toast(NOTIFY_OFFER_RECORDING.text, undefined, TIMEOUTS.BRIEF);

    // this is asynchronous, but we don't need to wait for it to complete
    this.recordOffer(
      this.description,
      parseFloat(this.amountInput),
      this.amountUnitCode,
      this.expirationDateInput,
    ).then(() => {
      this.description = "";
      this.amountInput = "0";
    });
  }

  /**
   * Record an offer with the given parameters
   * @param description - Offer description (may be empty)
   * @param amount - Offer amount (may be 0)
   * @param unitCode - Unit code (defaults to "HUR")
   * @param expirationDateInput - Optional expiration date
   */
  public async recordOffer(
    description: string,
    amount: number,
    unitCode: string = "HUR",
    expirationDateInput?: string,
  ) {
    try {
      const result = await createAndSubmitOffer(
        this.axios,
        this.apiServer,
        this.activeDid,
        description,
        amount,
        unitCode,
        "",
        expirationDateInput,
        this.recipientDid,
        this.projectId,
      );

      if (!result.success) {
        const errorMessage = result.error;
        logger.error("Error with offer creation result:", result);
        this.notify.error(
          errorMessage || NOTIFY_OFFER_CREATION_ERROR.message,
          TIMEOUTS.MODAL,
        );
      } else {
        this.notify.success(NOTIFY_OFFER_SUCCESS.message, TIMEOUTS.VERY_LONG);

        // Show seed phrase backup reminder if needed
        try {
          const settings = await this.$accountSettings();
          showSeedPhraseReminder(!!settings.hasBackedUpSeed, this.$notify);
        } catch (error) {
          logger.error("Error checking seed backup status:", error);
        }
      }
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
    } catch (error: any) {
      logger.error("Error with offer recordation caught:", error);
      const message =
        error.userMessage ||
        error.response?.data?.error?.message ||
        NOTIFY_OFFER_SUBMISSION_ERROR.message;
      this.notify.error(message, TIMEOUTS.MODAL);
    }
  }
}
</script>
