<template>
  <div>
    <v-skeleton-loader v-if="loading" type="list-item-two-line" width="100%" />
    <template v-else>
      <v-divider class="mb-4" />
      <InputLabel title="Pass Type (Optional)" />
      <v-row no-gutters class="ga-3 mt-1">
        <v-col>
          <v-card
            :variant="passType === 'QR' ? 'tonal' : 'outlined'"
            :color="passType === 'QR' ? 'primary' : undefined"
            class="pa-3 d-flex flex-column align-center ga-2 cursor-pointer position-relative"
            rounded="lg"
            @click="passType = passType === 'QR' ? null : 'QR'"
          >
            <v-icon v-if="passType === 'QR'" size="18" color="primary" class="selected-check">mdi-check-circle</v-icon>
            <v-icon size="32">mdi-qrcode</v-icon>
            <span class="text-subtitle-2 font-weight-bold">QR Pass</span>
          </v-card>
        </v-col>
        <v-col v-if="nfcEnabled">
          <v-card
            :variant="passType === 'NFC' ? 'tonal' : 'outlined'"
            :color="passType === 'NFC' ? 'primary' : undefined"
            class="pa-3 d-flex flex-column align-center ga-2 cursor-pointer position-relative"
            rounded="lg"
            @click="passType = passType === 'NFC' ? null : 'NFC'"
          >
            <v-icon v-if="passType === 'NFC'" size="18" color="primary" class="selected-check">mdi-check-circle</v-icon>
            <v-icon size="32">mdi-nfc</v-icon>
            <span class="text-subtitle-2 font-weight-bold">NFC</span>
          </v-card>
        </v-col>
      </v-row>

      <!-- QR Pass section -->
      <template v-if="passType === 'QR'">
        <v-alert
          v-if="!hasPrinter"
          type="warning"
          variant="tonal"
          density="compact"
          class="mt-3"
          icon="mdi-printer-alert"
        >
          No printer is configured in the settings. QR Pass may not print.
        </v-alert>
        <template v-else>
          <div class="d-flex align-center justify-space-between mt-3">
            <InputLabel title="Quantity" />
            <span v-if="availableCount !== null" class="text-caption text-grey">
              {{ availableCount }} available
            </span>
            <v-progress-circular v-else-if="cardsLoading" size="14" width="2" indeterminate color="grey" />
          </div>
          <v-text-field
            v-model.number="quantity"
            type="number"
            density="comfortable"
            :min="1"
            :max="availableCount ?? undefined"
            hide-details
          />
        </template>
      </template>

      <!-- NFC Pass section -->
      <template v-if="passType === 'NFC'">
        <div class="mt-3">
          <InputLabel title="Select Cards" />
          <v-select
            v-model="selectedCards"
            :items="cardItems"
            :loading="cardsLoading"
            item-title="cardNo"
            item-value="_id"
            density="comfortable"
            multiple
            chips
            closable-chips
            hide-details
            placeholder="Select cards..."
            return-object
          >
          </v-select>
        </div>

        <div class="mt-3">
          <InputLabel title="Quantity" />
          <v-text-field
            :model-value="selectedCards.length"
            type="number"
            density="comfortable"
            disabled
            hide-details
          />
        </div>

        <v-row no-gutters class="ga-3 mt-3">
          <v-col>
            <v-btn
              :color="isBarcodeScanningActive ? 'success' : 'primary'"
              variant="outlined"
              block
              prepend-icon="mdi-barcode-scan"
              @click="toggleBarcodeScanning"
            >
              <v-icon v-if="isBarcodeScanningActive" start>mdi-check-circle</v-icon>
              {{ isBarcodeScanningActive ? 'Scanning...' : 'Scan Via BarCode' }}
            </v-btn>
          </v-col>
          <v-col>
            <v-btn
              variant="outlined"
              color="primary"
              block
              prepend-icon="mdi-camera"
              @click="openCameraDialog"
            >
              Scan with Camera
            </v-btn>
          </v-col>
        </v-row>

        <v-alert
          v-if="isBarcodeScanningActive"
          type="info"
          variant="tonal"
          density="compact"
          class="mt-3 text-center"
        >
          <v-icon class="mr-2 animate-pulse">mdi-radar</v-icon>
          Ready to scan NFC cards. Please scan a card with your barcode scanner.
        </v-alert>
      </template>
    </template>
    <v-divider class="mt-6" />

    <!-- Hidden canvas used for frame-by-frame barcode detection -->
    <canvas ref="canvasRef" style="display: none" />

    <!-- Camera scanner dialog -->
    <v-dialog
      v-model="showCameraDialog"
      width="700"
      max-width="700"
      persistent
      transition="dialog-bottom-transition"
      @after-enter="startCamera"
      @after-leave="stopCamera"
    >
      <v-container class="d-flex justify-center" style="max-height: 90vh">
        <v-card elevation="2" class="d-flex flex-column align-center pa-2 w-100">
          <v-toolbar color="transparent">
            <v-card-title class="text-h5">Scan Card</v-card-title>
            <v-spacer />
            <v-btn icon="mdi-close" color="grey-darken-1" @click="closeCameraDialog" />
          </v-toolbar>

          <div class="position-relative w-100" style="aspect-ratio: 4/3; background: #000">
            <video
              ref="videoRef"
              autoplay
              playsinline
              muted
              class="w-100 h-100"
              style="object-fit: cover"
            />
            <!-- scan overlay -->
            <div class="scan-overlay">
              <div class="scan-box" />
            </div>
          </div>

          <div v-if="cameraError" class="pa-3 text-center text-error text-caption">
            {{ cameraError }}
          </div>

          <v-card-actions class="justify-center">
            <v-btn icon="mdi-camera-switch" color="primary" @click="switchCamera" />
          </v-card-actions>
        </v-card>
      </v-container>
    </v-dialog>

    <Snackbar v-model="snackbar" :text="snackbarText" :color="snackbarColor" />
  </div>
</template>

<script setup lang="ts">
const props = defineProps({
  settings: {
    type: Object as PropType<Record<string, any> | null>,
    default: null,
  },
  loading: {
    type: Boolean,
    default: false,
  },
  modelValue: {
    type: String as PropType<string | null>,
    default: null,
  },
  quantity: {
    type: Number as PropType<number | null>,
    default: null,
  },
  cards: {
    type: Array as PropType<any[]>,
    default: () => [],
  },
  siteId: {
    type: String as PropType<string | null>,
    default: null,
  },
  unitId: {
    type: String as PropType<string | null>,
    default: null,
  },
});

const emit = defineEmits(["update:modelValue", "update:quantity", "update:cards"]);

const { getAvailableContractorCards, generateQrVms } = useAccessManagement();

const nfcEnabled = computed(() => props.settings?.data?.settings?.nfcPass ?? false);
const printer = computed(() => props.settings?.data?.settings?.printer ?? { vendorId: null, productId: null });
const hasPrinter = computed(() => !!printer.value.vendorId && !!printer.value.productId);

const passType = computed({
  get: () => props.modelValue,
  set: (val) => emit("update:modelValue", val),
});

const quantity = computed({
  get: () => props.quantity,
  set: (val) => emit("update:quantity", val),
});

const selectedCards = computed({
  get: () => props.cards,
  set: (val) => {
    emit("update:cards", val);
    emit("update:quantity", val.length || null);
  },
});

// ─── Snackbar ─────────────────────────────────────────────────────
const snackbar = ref(false);
const snackbarText = ref("");
const snackbarColor = ref("error");

function showSnackbar(text: string, color = "error") {
  snackbarText.value = text;
  snackbarColor.value = color;
  snackbar.value = true;
}

// ─── Card fetching ────────────────────────────────────────────────
const cardItems = ref<any[]>([]);
const cardsLoading = ref(false);
const availableCount = ref<number | null>(null);

async function fetchCards(type: "NFC" | "QRCODE" = "NFC") {
  if (!props.siteId || !props.unitId) return;
  cardsLoading.value = true;
  try {
    const res = await getAvailableContractorCards({
      type,
      siteId: props.siteId,
      unitId: props.unitId,
    });
    cardItems.value = res?.data?.[0]?.items ?? [];
    availableCount.value = (res?.data?.[2] as any)?.count ?? cardItems.value.length;

    if (type === "QRCODE" && props.settings?.data?._id && availableCount.value !== null && availableCount.value < 50) {
      await generateQrVms({
        site: props.settings.data.site,
        unitId: props.unitId,
        quantity: 50,
      });
      // Re-fetch to get the updated count after generation
      const refreshed = await getAvailableContractorCards({
        type,
        siteId: props.siteId,
        unitId: props.unitId,
      });
      cardItems.value = refreshed?.data?.[0]?.items ?? [];
      availableCount.value = (refreshed?.data?.[2] as any)?.count ?? cardItems.value.length;
    }
  } catch (err: any) {
    const msg = err?.data?.message ?? err?.message ?? String(err);
    showSnackbar(`Entry pass service error: ${msg}`);
    availableCount.value = null;
    cardItems.value = [];
  } finally {
    cardsLoading.value = false;
  }
}

function addCard(card: any) {
  const already = selectedCards.value.some((c) => c._id === card._id);
  if (!already) selectedCards.value = [...selectedCards.value, card];
}

// ─── Barcode scanning ─────────────────────────────────────────────
const isBarcodeScanningActive = ref(false);
const barcodeBuffer = ref("");
const barcodeTimeout = ref<ReturnType<typeof setTimeout> | null>(null);

const toggleBarcodeScanning = () => {
  isBarcodeScanningActive.value = !isBarcodeScanningActive.value;
  if (!isBarcodeScanningActive.value) {
    barcodeBuffer.value = "";
    if (barcodeTimeout.value) clearTimeout(barcodeTimeout.value);
  }
};

const validateScannedBarcode = (scannedCode: string) => {
  const card = cardItems.value.find((c) => c.cardNo === scannedCode);
  if (card) addCard(card);
  barcodeBuffer.value = "";
};

const handleBarcodeInput = (event: KeyboardEvent) => {
  if (!isBarcodeScanningActive.value || passType.value !== "NFC") return;

  if (event.key === "Enter") {
    event.preventDefault();
    if (barcodeTimeout.value) clearTimeout(barcodeTimeout.value);
    if (barcodeBuffer.value.trim()) validateScannedBarcode(barcodeBuffer.value.trim());
    return;
  }

  if (event.key.length === 1) {
    barcodeBuffer.value += event.key;
    if (barcodeTimeout.value) clearTimeout(barcodeTimeout.value);
    barcodeTimeout.value = setTimeout(() => { barcodeBuffer.value = ""; }, 200);
  }
};

// ─── Camera scanner ───────────────────────────────────────────────
const videoRef = ref<HTMLVideoElement | null>(null);
const canvasRef = ref<HTMLCanvasElement | null>(null);
const showCameraDialog = ref(false);
const cameraError = ref("");
const cameraFacingMode = ref<"environment" | "user">("environment");
let mediaStream: MediaStream | null = null;
let scanIntervalId: ReturnType<typeof setInterval> | null = null;
let barcodeDetector: any = null;

const openCameraDialog = () => {
  cameraError.value = "";
  showCameraDialog.value = true;
};

const closeCameraDialog = () => {
  showCameraDialog.value = false;
};

const startCamera = async () => {
  cameraError.value = "";

  if (!("BarcodeDetector" in window)) {
    cameraError.value = "BarcodeDetector is not supported in this browser. Use Chrome or Edge.";
    return;
  }

  try {
    mediaStream = await navigator.mediaDevices.getUserMedia({
      video: { facingMode: cameraFacingMode.value, width: { ideal: 1280 }, height: { ideal: 720 } },
    });

    if (videoRef.value) {
      videoRef.value.srcObject = mediaStream;
      await videoRef.value.play();
    }

    barcodeDetector = new (window as any).BarcodeDetector({
      formats: ["qr_code", "code_128", "code_39", "ean_13", "ean_8"],
    });

    // Wait a tick for the video to have real frame data before scanning
    await new Promise((resolve) => setTimeout(resolve, 500));
    startScanLoop();
  } catch (err: any) {
    cameraError.value = `Camera error: ${err.message}`;
  }
};

const startScanLoop = () => {
  if (scanIntervalId !== null) clearInterval(scanIntervalId);
  scanIntervalId = setInterval(scanFrame, 300);
};

const scanFrame = async () => {
  const video = videoRef.value;
  const canvas = canvasRef.value;
  if (!video || !canvas || !barcodeDetector || !showCameraDialog.value) return;
  if (video.readyState < 2 || video.videoWidth === 0) return;

  const ctx = canvas.getContext("2d");
  if (!ctx) return;

  canvas.width = video.videoWidth;
  canvas.height = video.videoHeight;
  ctx.drawImage(video, 0, 0, canvas.width, canvas.height);

  try {
    const barcodes = await barcodeDetector.detect(canvas);
    for (const barcode of barcodes) {
      const card = cardItems.value.find((c) => c.cardNo === barcode.rawValue);
      if (card) {
        addCard(card);
        closeCameraDialog();
        return;
      } else if (barcode.rawValue) {
        showSnackbar(`Card "${barcode.rawValue}" not found in available cards.`, "warning");
        return;
      }
    }
  } catch {
    // silent — detector throws on blank frames
  }
};

const stopCamera = () => {
  if (scanIntervalId !== null) { clearInterval(scanIntervalId); scanIntervalId = null; }
  if (mediaStream) { mediaStream.getTracks().forEach((t) => t.stop()); mediaStream = null; }
  if (videoRef.value) videoRef.value.srcObject = null;
  barcodeDetector = null;
};

const switchCamera = async () => {
  stopCamera();
  cameraFacingMode.value = cameraFacingMode.value === "environment" ? "user" : "environment";
  await startCamera();
};

// ─── Watchers & lifecycle ─────────────────────────────────────────
watch(
  () => passType.value,
  (val) => {
    if (val === "NFC") {
      fetchCards("NFC");
      window.addEventListener("keydown", handleBarcodeInput);
    } else if (val === "QR") {
      availableCount.value = null;
      fetchCards("QRCODE");
    } else {
      availableCount.value = null;
      isBarcodeScanningActive.value = false;
      barcodeBuffer.value = "";
      if (barcodeTimeout.value) clearTimeout(barcodeTimeout.value);
      window.removeEventListener("keydown", handleBarcodeInput);
    }
  }
);

onUnmounted(() => {
  window.removeEventListener("keydown", handleBarcodeInput);
  if (barcodeTimeout.value) clearTimeout(barcodeTimeout.value);
  stopCamera();
});
</script>

<style scoped>
.selected-check {
  position: absolute;
  top: 6px;
  right: 6px;
}

.animate-pulse {
  animation: pulse 1.5s ease-in-out infinite;
}

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.3; }
}

.scan-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

.scan-box {
  width: 220px;
  height: 220px;
  border: 3px solid rgba(255, 255, 255, 0.85);
  border-radius: 12px;
  box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.45);
}
</style>
