/** * 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 Management</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..." : "Export 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>

    <!-- Import Contacts -->
    <div id="sectionImportContactsSettings" class="mt-4">
      <h2 class="text-slate-500 text-sm font-bold">Import Contacts</h2>

      <div class="mt-2">
        <input
          type="file"
          class="w-full bg-white rounded-md pe-2 file:border-0 file:bg-gradient-to-b file:from-blue-400 file:to-blue-700 file:shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] file:text-white file:px-3 file:py-2 file:me-2 file:rounded-s-md"
          @change="uploadImportFile"
        />
        <transition
          enter-active-class="transform ease-out duration-300 transition"
          enter-from-class="translate-y-2 opacity-0 sm:translate-y-4"
          enter-to-class="translate-y-0 opacity-100 sm:translate-y-0"
          leave-active-class="transition ease-in duration-500"
          leave-from-class="opacity-100"
          leave-to-class="opacity-0"
        >
          <div v-if="showContactImport()" class="mt-2">
            <!-- Bulk import has an error
            <div class="flex justify-center">
              <button
                class="block 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-6"
                @click="confirmSubmitImportFile()"
              >
                Overwrite Settings & Contacts
                <br />
                (which doesn't include Identifier Data)
              </button>
            </div>
            -->
            <button
              class="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"
              @click="checkContactImports()"
            >
              Import Contacts
            </button>
          </div>
        </transition>
      </div>
    </div>
  </div>
</template>

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

import { NotificationIface } from "../constants/app";

import { logger } from "../utils/logger";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { ACCOUNT_VIEW_CONSTANTS } from "@/constants/accountView";
import { ImportContent } from "@/interfaces/accountView";

/**
 * @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;

  /**
   * Router instance injected by Vue
   * Used for navigation
   */
  $router!: Router;

  /**
   * 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;

  /**
   * Reference to the selected import file
   * Used to store the file selected by the user for import
   */
  private inputImportFileName: Blob | undefined;

  /**
   * 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";
  }

  /**
   * Exports the database to a JSON file
   * Exports contacts and contact labels tables
   * 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> {
    if (this.isExporting) {
      return; // Prevent multiple simultaneous exports
    }

    try {
      this.isExporting = true;

      // Prepare export data using shared utility function
      await this.$saveContactExport();

      this.notify.success(
        "Contact export completed successfully. Check downloads or the 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;
    }
  }

  /**
   * Handles file selection for contact import
   * Stores the selected file for later processing
   */
  async uploadImportFile(event: Event): Promise<void> {
    this.inputImportFileName = (event.target as HTMLInputElement).files?.[0];
  }

  /**
   * Checks if a contact import file has been selected
   * Used to conditionally show the import button
   */
  showContactImport(): boolean {
    return !!this.inputImportFileName;
  }

  /**
   * Processes the selected import file and navigates to the contact import view
   * Parses the JSON file and extracts contact data for import
   */
  async checkContactImports(): Promise<void> {
    if (!this.inputImportFileName) {
      return;
    }

    const reader = new FileReader();
    reader.onload = (event) => {
      const fileContent: string = (event.target?.result as string) || "{}";
      try {
        const contents: ImportContent = JSON.parse(fileContent);
        const contactTableRows: Array<Contact> = (
          contents.data?.data as [{ tableName: string; rows: Array<Contact> }]
        )?.find((table) => table.tableName === "contacts")
          ?.rows as Array<Contact>;
        const contactRows = contactTableRows.map(
          // @ts-expect-error for omitting this field that is found in the Dexie format
          (contact) => R.omit(["$types"], contact) as Contact,
        );
        this.$router.push({
          name: "contact-import",
          query: { contacts: JSON.stringify(contactRows) },
        });
      } catch (error) {
        logger.error("Error checking contact imports:", error);
        this.notify.error(
          ACCOUNT_VIEW_CONSTANTS.ERRORS.IMPORT_ERROR,
          TIMEOUTS.STANDARD,
        );
      }
    };
    reader.readAsText(this.inputImportFileName);
  }
}
</script>
