<template>
  <!-- CONTENT -->
  <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
    <div class="mb-2">
      <h1 class="text-2xl text-center font-semibold relative px-7">
        <!-- Back -->
        <a
          class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
          @click="handleBack"
        >
          <font-awesome icon="chevron-left" class="fa-fw" />
        </a>

        <!-- Quick Help -->
        <a
          class="text-2xl text-center text-blue-500 px-2 py-1 absolute -right-2 -top-1"
          @click="toastQRCodeHelp()"
        >
          <font-awesome icon="circle-question" class="fa-fw" />
        </a>

        Share Contact Info
      </h1>
    </div>

    <div v-if="!givenName" :class="nameWarningClasses">
      <p class="mb-2">
        <b>Note:</b> your identity currently does <b>not</b> include a name.
      </p>
      <button :class="setNameButtonClasses" @click="openUserNameDialog">
        Set Your Name
      </button>
    </div>

    <UserNameDialog ref="userNameDialog" />

    <div
      v-if="activeDid && activeDid.startsWith(ETHR_DID_PREFIX)"
      :class="qrCodeContainerClasses"
      @click="onCopyUrlToClipboard()"
    >
      <!--
        Play with display options: https://qr-code-styling.com/
        See docs: https://www.npmjs.com/package/qr-code-generator-vue3
      -->
      <QRCodeVue3
        :value="qrValue"
        :width="606"
        :height="606"
        :corners-square-options="{ type: 'square' }"
        :dots-options="{ type: 'square', color: '#000' }"
      />
    </div>
    <div v-else-if="activeDid" class="text-center my-4">
      <!-- Not an ETHR DID so force them to paste it. (Passkey Peer DIDs are too big.) -->
      <span class="text-blue-500" @click="onCopyDidToClipboard()">
        Click here to copy your DID to your clipboard.
      </span>
      <span>
        Then give it to them so they can paste it in their list of People.
      </span>
    </div>
    <div v-else class="text-center my-4">
      You have no identitifiers yet, so
      <router-link
        :to="{ name: 'start' }"
        class="bg-blue-500 text-white px-1.5 py-1 rounded-md"
      >
        create your identifier.
      </router-link>
      <br />
      If you don't do that first, these contacts won't see your activity.
    </div>

    <div class="text-center mt-6">
      <div v-if="isScanning" :class="scannerContainerClasses">
        <!-- Status Message -->
        <div :class="statusMessageClasses">
          <div
            v-if="cameraState === 'initializing'"
            class="flex items-center justify-center space-x-2"
          >
            <svg
              class="animate-spin h-5 w-5 text-white"
              xmlns="http://www.w3.org/2000/svg"
              fill="none"
              viewBox="0 0 24 24"
            >
              <circle
                class="opacity-25"
                cx="12"
                cy="12"
                r="10"
                stroke="currentColor"
                stroke-width="4"
              ></circle>
              <path
                class="opacity-75"
                fill="currentColor"
                d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 
                3.042 1.135 5.824 3 7.938l3-2.647z"
              ></path>
            </svg>
            <span>{{ cameraStateMessage || "Initializing camera..." }}</span>
          </div>
          <p
            v-else-if="cameraState === 'active'"
            class="flex items-center justify-center space-x-2"
          >
            <span
              class="inline-block w-2 h-2 bg-green-500 rounded-full animate-pulse"
            ></span>
            <span>Position QR code in the frame</span>
          </p>
          <p v-else-if="error" class="text-red-400">Error: {{ error }}</p>
          <p v-else class="flex items-center justify-center space-x-2">
            <span :class="cameraStatusIndicatorClasses"></span>
            <span>{{ cameraStateMessage || "Ready to scan" }}</span>
          </p>
        </div>

        <qrcode-stream
          v-if="useQRReader"
          :camera="preferredCamera"
          class="qr-scanner"
          @decode="onDecode"
          @init="onInit"
          @detect="onDetect"
          @error="onError"
          @camera-on="onCameraOn"
          @camera-off="onCameraOff"
        />
      </div>
    </div>
  </section>
</template>

<script lang="ts">
import { AxiosError } from "axios";
import { Buffer } from "buffer/";
import QRCodeVue3 from "qr-code-generator-vue3";
import { Component, Vue } from "vue-facing-decorator";
import { copyToClipboard } from "../services/ClipboardService";

import { QrcodeStream } from "vue-qrcode-reader";

import QuickNav from "../components/QuickNav.vue";
import UserNameDialog from "../components/UserNameDialog.vue";
import { NotificationIface } from "../constants/app";
import { Contact } from "../db/tables/contacts";
import { getContactJwtFromJwtUrl } from "../libs/crypto";
import {
  CONTACT_CSV_HEADER,
  CONTACT_IMPORT_CONFIRM_URL_PATH_TIME_SAFARI,
  register,
  setVisibilityUtil,
  generateEndorserJwtUrlForAccount,
} from "../libs/endorserServer";
import { decodeEndorserJwt, ETHR_DID_PREFIX } from "../libs/crypto/vc";
import * as libsUtil from "../libs/util";
import { Router } from "vue-router";
import { logger } from "../utils/logger";
import { QRScannerFactory } from "@/services/QRScanner/QRScannerFactory";
import { CameraState } from "@/services/QRScanner/types";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { createNotifyHelpers } from "@/utils/notify";
import { showSeedPhraseReminder } from "@/utils/seedPhraseReminder";
import {
  NOTIFY_QR_INITIALIZATION_ERROR,
  NOTIFY_QR_CAMERA_IN_USE,
  NOTIFY_QR_CAMERA_ACCESS_REQUIRED,
  NOTIFY_QR_NO_CAMERA,
  NOTIFY_QR_HTTPS_REQUIRED,
  NOTIFY_QR_CONTACT_EXISTS,
  NOTIFY_QR_CONTACT_ERROR,
  NOTIFY_QR_REGISTRATION_SUBMITTED,
  NOTIFY_QR_REGISTRATION_ERROR,
  NOTIFY_QR_URL_COPIED,
  NOTIFY_QR_CODE_HELP,
  NOTIFY_QR_DID_COPIED,
  NOTIFY_QR_INVALID_QR_CODE,
  NOTIFY_QR_INVALID_CONTACT_INFO,
  NOTIFY_QR_MISSING_DID,
  NOTIFY_QR_UNKNOWN_CONTACT_TYPE,
  NOTIFY_QR_PROCESSING_ERROR,
  createQRContactAddedMessage,
  createQRRegistrationSuccessMessage,
  QR_TIMEOUT_STANDARD,
  QR_TIMEOUT_LONG,
} from "@/constants/notifications";
import { Account } from "@/db/tables/accounts";

interface QRScanResult {
  rawValue?: string;
  barcode?: string;
}

interface IUserNameDialog {
  open: (callback: (name: string) => void) => void;
}

@Component({
  components: {
    QRCodeVue3,
    QuickNav,
    UserNameDialog,
    QrcodeStream,
  },
  mixins: [PlatformServiceMixin],
})
export default class ContactQRScanShow extends Vue {
  $notify!: (notification: NotificationIface, timeout?: number) => void;
  $router!: Router;

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

  activeDid = "";
  apiServer = "";

  givenName = "";
  hideRegisterPromptOnNewContact = false;
  isRegistered = false;
  qrValue = "";
  isScanning = false;
  profileImageUrl = "";
  error: string | null = null;

  // QR Scanner properties
  isInitializing = true;
  initializationStatus = "Initializing camera...";
  useQRReader = __USE_QR_READER__;
  preferredCamera: "user" | "environment" = "environment";
  cameraState: CameraState = "off";
  cameraStateMessage?: string;

  ETHR_DID_PREFIX = ETHR_DID_PREFIX;

  // Add new properties to track scanning state
  private lastScannedValue: string = "";
  private lastScanTime: number = 0;
  private readonly SCAN_DEBOUNCE_MS = 2000; // Prevent duplicate scans within 2 seconds

  // Add cleanup tracking
  private isCleaningUp = false;
  private isMounted = false;

  // Add property to track if we're on desktop
  private isDesktop = false;
  private isFrontCamera = false;

  // Computed properties for template classes
  get nameWarningClasses(): string {
    return "bg-amber-200 text-amber-900 border-amber-500 border-dashed border text-center rounded-md overflow-hidden px-4 py-3 my-4";
  }

  get setNameButtonClasses(): string {
    return "inline-block text-md 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-4 py-2 rounded-md";
  }

  get qrCodeContainerClasses(): string {
    return "block w-[90vw] max-w-[calc((100vh-max(env(safe-area-inset-top),var(--safe-area-inset-top,0px))-max(env(safe-area-inset-bottom),var(--safe-area-inset-bottom,0px)))*0.4)] mx-auto my-4";
  }

  get scannerContainerClasses(): string {
    return "relative aspect-square overflow-hidden bg-slate-800 w-[90vw] max-w-[calc((100vh-max(env(safe-area-inset-top),var(--safe-area-inset-top,0px))-max(env(safe-area-inset-bottom),var(--safe-area-inset-bottom,0px)))*0.4)] mx-auto";
  }

  get statusMessageClasses(): string {
    return "absolute top-0 left-0 right-0 bg-black bg-opacity-50 text-white text-sm text-center py-2 z-10";
  }

  get cameraStatusIndicatorClasses(): Record<string, boolean> {
    return {
      "inline-block w-2 h-2 rounded-full": true,
      "bg-green-500": this.cameraState === "ready",
      "bg-yellow-500": this.cameraState === "in_use",
      "bg-red-500":
        this.cameraState === "error" ||
        this.cameraState === "permission_denied" ||
        this.cameraState === "not_found",
      "bg-blue-500": this.cameraState === "off",
    };
  }

  async created() {
    this.notify = createNotifyHelpers(this.$notify);

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

      // Get activeDid from active_identity table (single source of truth)
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      const activeIdentity = await (this as any).$getActiveIdentity();
      this.activeDid = activeIdentity.activeDid || "";

      this.apiServer = settings.apiServer || "";
      this.givenName = settings.firstName || "";
      this.hideRegisterPromptOnNewContact =
        !!settings.hideRegisterPromptOnNewContact;
      this.isRegistered = !!settings.isRegistered;
      this.profileImageUrl = settings.profileImageUrl || "";

      const account = await libsUtil.retrieveAccountMetadata(this.activeDid);
      if (account) {
        const name =
          (settings.firstName || "") +
          (settings.lastName ? ` ${settings.lastName}` : "");
        const publicKeyBase64 = Buffer.from(
          account.publicKeyHex,
          "hex",
        ).toString("base64");
        this.qrValue =
          CONTACT_CSV_HEADER +
          "\n" +
          `"${name}",${account.did},${publicKeyBase64},false,${this.isRegistered}`;
      }
    } catch (error) {
      logger.error("Error initializing component:", {
        error: error instanceof Error ? error.message : String(error),
        stack: error instanceof Error ? error.stack : undefined,
      });
      this.notify.error(NOTIFY_QR_INITIALIZATION_ERROR.message);
    }
  }

  async handleBack(): Promise<void> {
    await this.cleanupScanner();

    // 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);
    }

    this.$router.back();
  }

  async startScanning() {
    if (this.isCleaningUp) {
      logger.debug("Cannot start scanning during cleanup");
      return;
    }

    try {
      this.error = null;
      this.isScanning = true;
      this.lastScannedValue = "";
      this.lastScanTime = 0;

      const scanner = QRScannerFactory.getInstance();

      // Add camera state listener
      scanner.addCameraStateListener({
        onStateChange: (state, message) => {
          this.cameraState = state;
          this.cameraStateMessage = message;

          // Update UI based on camera state
          switch (state) {
            case "in_use":
              this.error = "Camera is in use by another application";
              this.isScanning = false;
              this.notify.warning(
                NOTIFY_QR_CAMERA_IN_USE.message,
                QR_TIMEOUT_LONG,
              );
              break;
            case "permission_denied":
              this.error = "Camera permission denied";
              this.isScanning = false;
              this.notify.warning(
                NOTIFY_QR_CAMERA_ACCESS_REQUIRED.message,
                QR_TIMEOUT_LONG,
              );
              break;
            case "not_found":
              this.error = "No camera found";
              this.isScanning = false;
              this.notify.warning(NOTIFY_QR_NO_CAMERA.message, QR_TIMEOUT_LONG);
              break;
            case "error":
              this.error = this.cameraStateMessage || "Camera error";
              this.isScanning = false;
              break;
          }
        },
      });

      // Check if scanning is supported first
      if (!(await scanner.isSupported())) {
        this.error =
          "Camera access requires HTTPS. Please use a secure connection.";
        this.isScanning = false;
        this.notify.warning(NOTIFY_QR_HTTPS_REQUIRED.message, QR_TIMEOUT_LONG);
        return;
      }

      // Start scanning
      await scanner.startScan();
    } catch (error) {
      this.error = error instanceof Error ? error.message : String(error);
      this.isScanning = false;
      logger.error("Error starting scan:", {
        error: error instanceof Error ? error.message : String(error),
        stack: error instanceof Error ? error.stack : undefined,
      });
    }
  }

  async stopScanning() {
    try {
      const scanner = QRScannerFactory.getInstance();
      await scanner.stopScan();
    } catch (error) {
      logger.error("Error stopping scan:", {
        error: error instanceof Error ? error.message : String(error),
        stack: error instanceof Error ? error.stack : undefined,
      });
    } finally {
      this.isScanning = false;
      this.lastScannedValue = "";
      this.lastScanTime = 0;
    }
  }

  async cleanupScanner() {
    if (this.isCleaningUp) {
      return;
    }

    this.isCleaningUp = true;
    try {
      logger.debug("Cleaning up QR scanner resources");
      await this.stopScanning();
      await QRScannerFactory.cleanup();
    } catch (error) {
      logger.error("Error during scanner cleanup:", {
        error: error instanceof Error ? error.message : String(error),
        stack: error instanceof Error ? error.stack : undefined,
      });
    } finally {
      this.isCleaningUp = false;
    }
  }

  /**
   * Handle QR code scan result with debouncing to prevent duplicate scans
   */
  async onScanDetect(result: string | QRScanResult) {
    try {
      // Extract raw value from different possible formats
      const rawValue =
        typeof result === "string"
          ? result
          : result?.rawValue || result?.barcode;
      if (!rawValue) {
        logger.warn("Invalid scan result - no value found:", result);
        return;
      }

      // Debounce duplicate scans
      const now = Date.now();
      if (
        rawValue === this.lastScannedValue &&
        now - this.lastScanTime < this.SCAN_DEBOUNCE_MS
      ) {
        logger.debug("Ignoring duplicate scan:", rawValue);
        return;
      }

      // Update scan tracking
      this.lastScannedValue = rawValue;
      this.lastScanTime = now;

      logger.debug("Processing QR code scan result:", rawValue);

      let contact: Contact;
      if (rawValue.includes(CONTACT_IMPORT_CONFIRM_URL_PATH_TIME_SAFARI)) {
        const jwt = getContactJwtFromJwtUrl(rawValue);
        if (!jwt) {
          logger.warn("Invalid QR code format - no JWT found in URL");
          this.notify.error(NOTIFY_QR_INVALID_QR_CODE.message);
          return;
        }
        logger.debug("Decoding JWT payload from QR code");
        const decodedJwt = await decodeEndorserJwt(jwt);

        // Process JWT and contact info
        if (!decodedJwt?.payload?.own) {
          logger.warn("Invalid JWT payload - missing 'own' field");
          this.notify.error(NOTIFY_QR_INVALID_CONTACT_INFO.message);
          return;
        }

        const contactInfo = decodedJwt.payload.own;
        const did = contactInfo.did || decodedJwt.payload.iss;
        if (!did) {
          logger.warn("Invalid contact info - missing DID");
          this.notify.error(NOTIFY_QR_MISSING_DID.message);
          return;
        }

        // Create contact object
        contact = {
          did: did,
          name: contactInfo.name || "",
          publicKeyBase64: contactInfo.publicKeyBase64 || "",
          seesMe: contactInfo.seesMe || false,
          registered: contactInfo.registered || false,
        };
      } else if (rawValue.startsWith(CONTACT_CSV_HEADER)) {
        const lines = rawValue.split(/\n/);
        contact = libsUtil.csvLineToContact(lines[1]);
      } else {
        this.notify.error(NOTIFY_QR_UNKNOWN_CONTACT_TYPE.message);
        return;
      }

      // Add contact but keep scanning
      logger.debug("Adding new contact to database:", {
        did: contact.did,
        name: contact.name,
      });
      await this.addNewContact(contact);
    } catch (error) {
      logger.error("Error processing contact QR code:", {
        error: error instanceof Error ? error.message : String(error),
        stack: error instanceof Error ? error.stack : undefined,
      });
      this.notify.error(
        error instanceof Error
          ? error.message
          : NOTIFY_QR_PROCESSING_ERROR.message,
      );
    }
  }

  async setVisibility(contact: Contact, visibility: boolean) {
    const result = await setVisibilityUtil(
      this.activeDid,
      this.apiServer,
      this.axios,
      contact,
      visibility,
    );
    if (result.error) {
      this.notify.error(result.error as string, QR_TIMEOUT_LONG);
    } else if (!result.success) {
      logger.warn("Unexpected result from setting visibility:", result);
    }
  }

  async register(contact: Contact) {
    logger.debug("Submitting contact registration", {
      did: contact.did,
      name: contact.name,
    });
    this.notify.toast("Submitted", NOTIFY_QR_REGISTRATION_SUBMITTED.message);

    try {
      const regResult = await register(
        this.activeDid,
        this.apiServer,
        this.axios,
        contact,
      );
      if (regResult.success) {
        contact.registered = true;
        await this.$updateContact(contact.did, { registered: true });
        logger.debug("Contact registration successful", { did: contact.did });

        this.notify.success(
          createQRRegistrationSuccessMessage(contact.name || ""),
          QR_TIMEOUT_LONG,
        );
      } else {
        this.notify.error(
          (regResult.error as string) || NOTIFY_QR_REGISTRATION_ERROR.message,
          QR_TIMEOUT_LONG,
        );
      }
    } catch (error) {
      logger.error("Error registering contact:", {
        did: contact.did,
        error: error instanceof Error ? error.message : String(error),
        stack: error instanceof Error ? error.stack : undefined,
      });
      let userMessage = "There was an error.";
      const serverError = error as AxiosError;
      if (serverError) {
        if (
          serverError.response?.data &&
          typeof serverError.response.data === "object" &&
          "message" in serverError.response.data
        ) {
          userMessage = (serverError.response.data as { message: string })
            .message;
        } else if (serverError.message) {
          userMessage = serverError.message; // Info for the user
        } else {
          userMessage = JSON.stringify(serverError.toJSON());
        }
      } else {
        userMessage = error as string;
      }
      // Now set that error for the user to see.
      this.notify.error(userMessage, QR_TIMEOUT_LONG);
    }
  }

  onScanError(error: Error) {
    this.error = error.message;
    logger.error("QR code scan error:", {
      error: error.message,
      stack: error.stack,
    });
  }

  async onCopyUrlToClipboard() {
    try {
      // Generate URL for sharing
      const account = (await libsUtil.retrieveFullyDecryptedAccount(
        this.activeDid,
      )) as Account;
      const jwtUrl = await generateEndorserJwtUrlForAccount(
        account,
        this.isRegistered,
        this.givenName,
        this.profileImageUrl,
        true,
      );

      // Copy the URL to clipboard
      await copyToClipboard(jwtUrl);
      this.notify.toast(
        NOTIFY_QR_URL_COPIED.title,
        NOTIFY_QR_URL_COPIED.message,
      );
    } catch (error) {
      this.$logAndConsole(`Error copying URL to clipboard: ${error}`, true);
      this.notify.error("Failed to copy URL to clipboard.");
    }
  }

  toastQRCodeHelp() {
    this.notify.info(NOTIFY_QR_CODE_HELP.message, QR_TIMEOUT_LONG);
  }

  async onCopyDidToClipboard() {
    //this.onScanDetect([{ rawValue: this.qrValue }]); // good for testing
    try {
      await copyToClipboard(this.activeDid);
      this.notify.info(NOTIFY_QR_DID_COPIED.message, QR_TIMEOUT_LONG);
    } catch (error) {
      this.$logAndConsole(`Error copying DID to clipboard: ${error}`, true);
      this.notify.error("Failed to copy DID to clipboard.");
    }
  }

  openUserNameDialog() {
    (this.$refs.userNameDialog as IUserNameDialog).open((name: string) => {
      this.givenName = name;
    });
  }

  // Lifecycle hooks
  mounted() {
    this.isMounted = true;
    this.isDesktop = this.detectDesktopBrowser();
    document.addEventListener("pause", this.handleAppPause);
    document.addEventListener("resume", this.handleAppResume);
    // Start scanning automatically when view is loaded
    this.startScanning();

    // Apply mirroring after a short delay to ensure video element is ready
    setTimeout(() => {
      const videoElement = document.querySelector(
        ".qr-scanner video",
      ) as HTMLVideoElement;
      if (videoElement) {
        videoElement.style.transform = "scaleX(-1)";
      }
    }, 1000);
  }

  beforeDestroy() {
    this.isMounted = false;
    document.removeEventListener("pause", this.handleAppPause);
    document.removeEventListener("resume", this.handleAppResume);
    this.cleanupScanner();
  }

  async handleAppPause() {
    if (!this.isMounted) return;

    logger.debug("App paused, stopping scanner");
    await this.stopScanning();
  }

  handleAppResume() {
    if (!this.isMounted) return;

    logger.debug("App resumed, scanner can be restarted by user");
    this.isScanning = false;
  }

  async addNewContact(contact: Contact) {
    try {
      logger.debug("Opening database connection for new contact");

      // Check if contact already exists
      const existingContact = await this.$getContact(contact.did);

      if (existingContact) {
        logger.info("Contact already exists", { did: contact.did });
        this.notify.warning(NOTIFY_QR_CONTACT_EXISTS.message, QR_TIMEOUT_LONG);
        return;
      }

      // Add new contact
      // @ts-expect-error because we're just using the value to store to the DB
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      contact.contactMethods = JSON.stringify(
        (
          this as {
            _parseJsonField: (
              value: unknown,
              defaultValue: unknown[],
            ) => unknown[];
          }
        )._parseJsonField(contact.contactMethods, []),
      );
      await this.$insertContact(contact);

      if (this.activeDid) {
        logger.debug("Setting contact visibility", { did: contact.did });
        await this.setVisibility(contact, true);
        contact.seesMe = true;
      }

      this.notify.success(
        createQRContactAddedMessage(!!this.activeDid),
        QR_TIMEOUT_STANDARD,
      );

      if (
        this.isRegistered &&
        !this.hideRegisterPromptOnNewContact &&
        !contact.registered
      ) {
        setTimeout(() => {
          this.$notify(
            {
              group: "modal",
              type: "confirm",
              title: "Register",
              text: "Do you want to register them?",
              onCancel: async (stopAsking?: boolean) => {
                await this.handleRegistrationPromptResponse(stopAsking);
              },
              onNo: async (stopAsking?: boolean) => {
                await this.handleRegistrationPromptResponse(stopAsking);
              },
              onYes: async () => {
                await this.register(contact);
              },
              promptToStopAsking: true,
            },
            -1,
          );
        }, 500);
      }
    } catch (error) {
      logger.error("Error saving contact to database:", {
        did: contact.did,
        error: error instanceof Error ? error.message : String(error),
        stack: error instanceof Error ? error.stack : undefined,
      });
      this.notify.error(NOTIFY_QR_CONTACT_ERROR.message, QR_TIMEOUT_LONG);
    }
  }

  async onInit(promise: Promise<void>): Promise<void> {
    logger.log("[QRScanner] onInit called");
    try {
      await promise;
      this.isInitializing = false;
      this.cameraState = "ready";
    } catch (error) {
      const wrappedError =
        error instanceof Error ? error : new Error(String(error));
      this.error = wrappedError.message;
      this.cameraState = "error";
      this.isInitializing = false;
      logger.error("Error during QR scanner initialization:", {
        error: wrappedError.message,
        stack: wrappedError.stack,
      });
    }
  }

  onCameraOn(): void {
    this.cameraState = "active";
    this.isInitializing = false;
    this.isFrontCamera = this.preferredCamera === "user";
    this.applyCameraMirroring();
  }

  onCameraOff(): void {
    this.cameraState = "off";
  }

  onDetect(result: unknown): void {
    this.isScanning = true;
    this.cameraState = "active";
    try {
      let rawValue: string | undefined;
      if (
        Array.isArray(result) &&
        result.length > 0 &&
        "rawValue" in result[0]
      ) {
        rawValue = result[0].rawValue;
      } else if (result && typeof result === "object" && "rawValue" in result) {
        rawValue = (result as { rawValue: string }).rawValue;
      }
      if (rawValue) {
        this.isInitializing = false;
        this.initializationStatus = "QR code captured!";
        this.onScanDetect(rawValue);
      }
    } catch (error) {
      this.handleError(error);
    } finally {
      this.cameraState = "active";
    }
  }

  onDecode(result: string): void {
    try {
      this.isInitializing = false;
      this.initializationStatus = "QR code captured!";
      this.onScanDetect(result);
    } catch (error) {
      this.handleError(error);
    }
  }

  toggleCamera(): void {
    this.preferredCamera =
      this.preferredCamera === "user" ? "environment" : "user";
    this.isFrontCamera = this.preferredCamera === "user";
    this.applyCameraMirroring();
  }

  private handleError(error: unknown): void {
    const wrappedError =
      error instanceof Error ? error : new Error(String(error));
    this.error = wrappedError.message;
    this.cameraState = "error";
  }

  onError(error: Error): void {
    this.error = error.message;
    this.cameraState = "error";
    logger.error("QR code scan error:", {
      error: error.message,
      stack: error.stack,
    });
  }

  // Add method to detect desktop browser
  private detectDesktopBrowser(): boolean {
    const userAgent = navigator.userAgent.toLowerCase();
    return !/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(
      userAgent,
    );
  }

  // Add method to apply camera mirroring
  private applyCameraMirroring(): void {
    const videoElement = document.querySelector(
      ".qr-scanner video",
    ) as HTMLVideoElement;
    if (videoElement) {
      // Mirror if it's desktop or front camera on mobile
      const shouldMirror =
        this.isDesktop || (this.isFrontCamera && !this.isDesktop);
      videoElement.style.transform = shouldMirror ? "scaleX(-1)" : "none";
    }
  }

  private async handleRegistrationPromptResponse(
    stopAsking?: boolean,
  ): Promise<void> {
    if (stopAsking) {
      await this.$saveSettings({
        hideRegisterPromptOnNewContact: stopAsking,
      });
      this.hideRegisterPromptOnNewContact = stopAsking;
    }
  }
}
</script>

<style scoped>
.aspect-square {
  aspect-ratio: 1 / 1;
}

/* Update styles for camera mirroring */
:deep(.qr-scanner) {
  position: relative;
}

/* Remove the default mirroring from CSS since we're handling it in JavaScript */
:deep(.qr-scanner video) {
  transform: none;
}

/* Ensure the canvas for QR detection is not mirrored */
:deep(.qr-scanner canvas) {
  transform: none;
}
</style>
