<template>
  <!-- CONTENT -->
  <section id="Content" class="relativew-[100vw] h-[100vh]">
    <div
      class="absolute inset-x-0 bottom-0 bg-black/50 p-6 pb-[calc(env(safe-area-inset-bottom)+1.5rem)]"
    >
      <p class="text-center text-white mb-3">
        Point your camera at a TimeSafari contact QR code to scan it
        automatically.
      </p>

      <p v-if="error" class="text-center text-rose-300 mb-3">{{ error }}</p>

      <div class="flex justify-center items-center">
        <button
          class="text-center text-slate-600 leading-none bg-white p-2 rounded-full drop-shadow-lg"
          @click="handleBack"
        >
          <font-awesome icon="xmark" class="size-6"></font-awesome>
        </button>
      </div>
    </div>
  </section>
</template>

<script lang="ts">
import { Component, Vue } from "vue-facing-decorator";
import { Router } from "vue-router";
import { logger } from "../utils/logger";
import { QRScannerFactory } from "../services/QRScanner/QRScannerFactory";
import QuickNav from "../components/QuickNav.vue";
import { NotificationIface } from "../constants/app";
import { db } from "../db/index";
import { Contact } from "../db/tables/contacts";
import { getContactJwtFromJwtUrl } from "../libs/crypto";
import { decodeEndorserJwt } from "../libs/crypto/vc";
import { retrieveSettingsForActiveAccount } from "../db/index";
import { setVisibilityUtil } from "../libs/endorserServer";

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

@Component({
  components: {
    QuickNav,
  },
})
export default class ContactQRScan extends Vue {
  $notify!: (notification: NotificationIface, timeout?: number) => void;
  $router!: Router;

  isScanning = false;
  error: string | null = null;
  activeDid = "";
  apiServer = "";

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

  async created() {
    try {
      const settings = await retrieveSettingsForActiveAccount();
      this.activeDid = settings.activeDid || "";
      this.apiServer = settings.apiServer || "";
    } catch (error) {
      logger.error("Error initializing component:", {
        error: error instanceof Error ? error.message : String(error),
        stack: error instanceof Error ? error.stack : undefined,
      });
      this.$notify({
        group: "alert",
        type: "danger",
        title: "Initialization Error",
        text: "Failed to initialize QR scanner. Please try again.",
      });
    }
  }

  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();

      // 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(
          {
            group: "alert",
            type: "warning",
            title: "HTTPS Required",
            text: "Camera access requires a secure (HTTPS) connection",
          },
          5000,
        );
        return;
      }

      // Check permissions first
      if (!(await scanner.checkPermissions())) {
        const granted = await scanner.requestPermissions();
        if (!granted) {
          this.error = "Camera permission denied";
          this.isScanning = false;
          // Show notification for better visibility
          this.$notify(
            {
              group: "alert",
              type: "warning",
              title: "Camera Access Required",
              text: "Camera permission denied",
            },
            5000,
          );
          return;
        }
      }

      // Add scan listener
      scanner.addListener({
        onScan: this.onScanDetect,
        onError: this.onScanError,
      });

      // 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.info("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.info("Ignoring duplicate scan:", rawValue);
        return;
      }

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

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

      // Extract JWT
      const jwt = getContactJwtFromJwtUrl(rawValue);
      if (!jwt) {
        logger.warn("Invalid QR code format - no JWT found in URL");
        this.$notify({
          group: "alert",
          type: "danger",
          title: "Invalid QR Code",
          text: "This QR code does not contain valid contact information. Please scan a TimeSafari contact QR code.",
        });
        return;
      }

      // Process JWT and contact info
      logger.info("Decoding JWT payload from QR code");
      const decodedJwt = await decodeEndorserJwt(jwt);
      if (!decodedJwt?.payload?.own) {
        logger.warn("Invalid JWT payload - missing 'own' field");
        this.$notify({
          group: "alert",
          type: "danger",
          title: "Invalid Contact Info",
          text: "The contact information is incomplete or invalid.",
        });
        return;
      }

      const contactInfo = decodedJwt.payload.own;
      if (!contactInfo.did) {
        logger.warn("Invalid contact info - missing DID");
        this.$notify({
          group: "alert",
          type: "danger",
          title: "Invalid Contact",
          text: "The contact DID is missing.",
        });
        return;
      }

      // Create contact object
      const contact = {
        did: contactInfo.did,
        name: contactInfo.name || "",
        email: contactInfo.email || "",
        phone: contactInfo.phone || "",
        company: contactInfo.company || "",
        title: contactInfo.title || "",
        notes: contactInfo.notes || "",
      };

      // Add contact and stop scanning
      logger.info("Adding new contact to database:", {
        did: contact.did,
        name: contact.name,
      });
      await this.addNewContact(contact);
      await this.stopScanning();
      this.$router.back(); // Return to previous view after successful scan
    } 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({
        group: "alert",
        type: "danger",
        title: "Error",
        text:
          error instanceof Error
            ? error.message
            : "Could not process QR code. Please try again.",
      });
    }
  }

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

  async setVisibility(contact: Contact, visibility: boolean) {
    const result = await setVisibilityUtil(
      this.activeDid,
      this.apiServer,
      this.axios,
      db,
      contact,
      visibility,
    );
    if (result.error) {
      this.$notify({
        group: "alert",
        type: "danger",
        title: "Error Setting Visibility",
        text: result.error as string,
      });
    } else if (!result.success) {
      logger.warn("Unexpected result from setting visibility:", result);
    }
  }

  async addNewContact(contact: Contact) {
    try {
      logger.info("Opening database connection for new contact");
      await db.open();

      // Check if contact already exists
      const existingContacts = await db.contacts.toArray();
      const existingContact = existingContacts.find(
        (c) => c.did === contact.did,
      );

      if (existingContact) {
        logger.info("Contact already exists", { did: contact.did });
        this.$notify(
          {
            group: "alert",
            type: "warning",
            title: "Contact Exists",
            text: "This contact has already been added to your list.",
          },
          3000,
        );
        return;
      }

      // Add new contact
      await db.contacts.add(contact);

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

      this.$notify(
        {
          group: "alert",
          type: "success",
          title: "Contact Added",
          text: this.activeDid
            ? "They were added, and your activity is visible to them."
            : "They were added.",
        },
        3000,
      );
    } 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(
        {
          group: "alert",
          type: "danger",
          title: "Contact Error",
          text: "Could not save contact. Check if it already exists.",
        },
        5000,
      );
    }
  }

  // Lifecycle hooks
  mounted() {
    this.isMounted = true;
    document.addEventListener("pause", this.handleAppPause);
    document.addEventListener("resume", this.handleAppResume);
    this.startScanning(); // Automatically start scanning when view is mounted
  }

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

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

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

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

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

  async handleBack() {
    await this.cleanupScanner();
    this.$router.back();
  }
}
</script>

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