/** * Data Export Section Component * * Provides UI and functionality for
exporting user data and backing up identifier seeds. * Includes buttons for seed
backup and database export, with platform-specific download instructions. * *
Features: * - Platform-specific export handling (web vs. native) * - Proper
resource cleanup for blob URLs * - Robust error handling with user-friendly
messages * - Conditional UI based on platform capabilities * * @component *
@displayName DataExportSection * @example * ```vue *
<DataExportSection :active-did="currentDid" />
* ``` * * @author Matthew Raymer * @since 2025-01-25 * @version 1.1.0 */

<template>
  <div id="sectionDataExport" :class="containerClasses">
    <div :class="titleClasses">Data Export</div>
    <router-link
      v-if="activeDid"
      :to="{ name: 'seed-backup' }"
      :class="backupButtonClasses"
    >
      <!-- Notification dot - show while the user has not yet backed up their seed phrase -->
      <font-awesome
        v-if="showRedNotificationDot"
        icon="circle"
        class="absolute -right-[8px] -top-[8px] text-rose-500 text-[14px] border border-white rounded-full"
      ></font-awesome>
      Backup Identifier Seed
    </router-link>

    <button
      :disabled="isExporting"
      :class="exportButtonClasses"
      @click="exportDatabase()"
    >
      {{ isExporting ? "Exporting..." : "Download Contacts" }}
    </button>

    <div
      v-if="capabilities.needsFileHandlingInstructions"
      :class="instructionsContainerClasses"
    >
      <p>
        After the export, you can save the file in your preferred storage
        location.
      </p>
      <ul>
        <li v-if="capabilities.isIOS" :class="listItemClasses">
          On iOS: You will be prompted to choose a location to save your backup
          file.
        </li>
        <li
          v-if="capabilities.isMobile && !capabilities.isIOS"
          :class="listItemClasses"
        >
          On Android: You will be prompted to choose a location to save your
          backup file.
        </li>
      </ul>
    </div>
  </div>
</template>

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

import { AppString, NotificationIface } from "../constants/app";
import { Contact } from "../db/tables/contacts";

import { logger } from "../utils/logger";
import { contactsToExportJson } from "../libs/util";
import { createNotifyHelpers } from "@/utils/notify";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";

/**
 * @vue-component
 * Data Export Section Component
 * Handles database export and seed backup functionality with platform-specific behavior
 *
 * Features:
 * - Automatic date stamping of backup files (YYYY-MM-DD format)
 * - Platform-specific export handling with proper abstraction
 * - Robust error handling and user notifications
 * - Template streamlined with computed CSS properties
 */
@Component({
  mixins: [PlatformServiceMixin],
})
export default class DataExportSection extends Vue {
  /**
   * Notification function injected by Vue
   * Used to show success/error messages to the user
   */
  $notify!: (notification: NotificationIface, timeout?: number) => void;

  /**
   * Active DID (Decentralized Identifier) of the user
   * Controls visibility of seed backup option
   * @required
   */
  @Prop({ required: true }) readonly activeDid!: string;

  /**
   * Flag indicating if export is currently in progress
   * Used to show loading state and prevent multiple simultaneous exports
   */
  isExporting = false;

  /**
   * Flag indicating if the user has backed up their seed phrase
   * Used to control the visibility of the notification dot
   */
  showRedNotificationDot = false;

  /**
   * Notification helper for consistent notification patterns
   * Created as a getter to ensure $notify is available when called
   */
  notify!: ReturnType<typeof createNotifyHelpers>;

  /**
   * NOTE: PlatformServiceMixin provides both concise helpers (e.g. $contacts, capabilities)
   * and the full platformService instance for advanced/native features (e.g. writeAndShareFile).
   * Always use 'this.platformService' as a property (never as a function).
   */
  declare readonly platformService: import("@/services/PlatformService").PlatformService;

  /**
   * CSS classes for the main container
   */
  get containerClasses(): string {
    return "bg-slate-100 rounded-md overflow-hidden px-4 py-4 mt-8 mb-8";
  }

  /**
   * CSS classes for the section title
   */
  get titleClasses(): string {
    return "mb-2 font-bold";
  }

  /**
   * CSS classes for the backup button (router link)
   */
  get backupButtonClasses(): string {
    return "block relative w-full text-center text-md 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-1.5 py-2 rounded-md mb-2 mt-2";
  }

  /**
   * CSS classes for the export button
   */
  get exportButtonClasses(): string {
    return "block w-full text-center text-md 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-1.5 py-2 rounded-md disabled:opacity-50 disabled:cursor-not-allowed";
  }

  /**
   * CSS classes for the instructions container
   */
  get instructionsContainerClasses(): string {
    return "mt-4";
  }

  /**
   * CSS classes for list items in instructions
   */
  get listItemClasses(): string {
    return "list-disc list-outside ml-4";
  }

  /**
   * Computed property for the export file name
   * Includes today's date for easy identification of backup files
   */
  private get fileName(): string {
    const today = new Date();
    const dateString = today.toISOString().split("T")[0]; // YYYY-MM-DD format
    return `${AppString.APP_NAME_NO_SPACES}-backup-contacts-${dateString}.json`;
  }

  /**
   * Exports the database to a JSON file
   * Uses the platform service to handle platform-specific export logic
   * Shows success/error notifications to user
   *
   * @throws {Error} If export fails
   */
  public async exportDatabase(): Promise<void> {
    // Note that similar code is in ContactsView.vue exportContactData()

    if (this.isExporting) {
      return; // Prevent multiple simultaneous exports
    }

    try {
      this.isExporting = true;

      // Fetch contacts from database using mixin's cached method
      const allContacts = await this.$contacts();

      // Convert contacts to export format
      const processedContacts: Contact[] = allContacts.map((contact) => {
        // first remove the contactMethods field, mostly to cast to a clear type (that will end up with JSON objects)
        const exContact: Contact = R.omit(["contactMethods"], contact);
        // now add contactMethods as a true array of ContactMethod objects
        exContact.contactMethods = contact.contactMethods
          ? typeof contact.contactMethods === "string" &&
            contact.contactMethods.trim() !== ""
            ? JSON.parse(contact.contactMethods)
            : []
          : [];
        return exContact;
      });

      const exportData = contactsToExportJson(processedContacts);
      const jsonStr = JSON.stringify(exportData, null, 2);

      // Use platform service to handle export (no platform-specific logic here!)
      await this.platformService.writeAndShareFile(this.fileName, jsonStr);

      this.notify.success(
        "Contact export completed successfully. Check your downloads or share dialog.",
      );
    } catch (error) {
      logger.error("Export Error:", error);
      this.notify.error(
        `There was an error exporting the data: ${error instanceof Error ? error.message : "Unknown error"}`,
      );
    } finally {
      this.isExporting = false;
    }
  }

  created() {
    this.notify = createNotifyHelpers(this.$notify);
    this.loadSeedBackupStatus();
  }

  /**
   * Loads the seed backup status from account settings
   * Updates the hasBackedUpSeed flag to control notification dot visibility
   */
  private async loadSeedBackupStatus(): Promise<void> {
    try {
      const settings = await this.$accountSettings();
      this.showRedNotificationDot =
        !!settings.isRegistered && !settings.hasBackedUpSeed;
    } catch (err: unknown) {
      logger.error("Failed to load seed backup status:", err);
      // Default to false (show notification dot) if we can't load the setting
      this.showRedNotificationDot = false;
    }
  }
}
</script>
