<script setup>
import { ref, computed, watch, onUnmounted } from "vue";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
} from "@/Components/ui/dialog";
import { Button } from "@/Components/ui/button";
import { Badge } from "../ui/badge";
import { Loader2, RotateCcwIcon } from "lucide-vue-next";
import axios from "axios";

const props = defineProps({
  show: { type: Boolean, default: false },
  src: { type: String, default: "" },
  title: { type: String, default: "Dokument" },
  mimeType: { type: String, default: "" },
  filename: { type: String, default: "" },
});
const emit = defineEmits(["close"]);

const textContent = ref("");
const loading = ref(false);
const previewGenerating = ref(false);
const previewError = ref("");

// Image viewer – zoom & pan state
const containerRef = ref(null);
const imageRef = ref(null);
const imageScale = ref(1);
const translateX = ref(0);
const translateY = ref(0);
const fitScale = ref(1);
const isDragging = ref(false);
const hasMoved = ref(false);
const dragStartX = ref(0);
const dragStartY = ref(0);
const dragStartTX = ref(0);
const dragStartTY = ref(0);

const MAX_SCALE = 8;

const imageCursorClass = computed(() => {
  if (isDragging.value && hasMoved.value) return "cursor-grabbing";
  if (imageScale.value > fitScale.value + 0.01) return "cursor-grab";
  return "cursor-default";
});

const initImageView = () => {
  const container = containerRef.value;
  const img = imageRef.value;
  if (!container || !img) return;
  const cW = container.clientWidth;
  const cH = container.clientHeight;
  const iW = img.naturalWidth || cW;
  const iH = img.naturalHeight || cH;
  const fs = Math.min(1, cW / iW, cH / iH);
  fitScale.value = fs;
  imageScale.value = fs;
  translateX.value = (cW - iW * fs) / 2;
  translateY.value = (cH - iH * fs) / 2;
};

const resetImageView = () => {
  initImageView();
};

const clampTranslate = (tx, ty, scale) => {
  const container = containerRef.value;
  const img = imageRef.value;
  if (!container || !img) return { tx, ty };
  const cW = container.clientWidth;
  const cH = container.clientHeight;
  const iW = img.naturalWidth * scale;
  const iH = img.naturalHeight * scale;
  // When image fills the container: clamp so image edges stay within container.
  // When image is smaller than container: keep it centered.
  const minX = iW >= cW ? cW - iW : (cW - iW) / 2;
  const maxX = iW >= cW ? 0 : (cW - iW) / 2;
  const minY = iH >= cH ? cH - iH : (cH - iH) / 2;
  const maxY = iH >= cH ? 0 : (cH - iH) / 2;
  return {
    tx: Math.min(maxX, Math.max(minX, tx)),
    ty: Math.min(maxY, Math.max(minY, ty)),
  };
};

const zoomAt = (mx, my, factor) => {
  const img = imageRef.value;
  const iW = img?.naturalWidth ?? 1;
  const iH = img?.naturalHeight ?? 1;
  const raw = imageScale.value * factor;
  const newScale = Math.min(MAX_SCALE, Math.max(fitScale.value, raw));
  if (newScale === imageScale.value) return;
  let tx = mx - ((mx - translateX.value) / imageScale.value) * newScale;
  let ty = my - ((my - translateY.value) / imageScale.value) * newScale;
  const clamped = clampTranslate(tx, ty, newScale);
  translateX.value = clamped.tx;
  translateY.value = clamped.ty;
  imageScale.value = newScale;
};

const mousePos = (e) => {
  const rect = containerRef.value.getBoundingClientRect();
  return { mx: e.clientX - rect.left, my: e.clientY - rect.top };
};

const handleImageLoad = () => {
  initImageView();
};

const handleWheel = (e) => {
  e.preventDefault();
  const { mx, my } = mousePos(e);
  zoomAt(mx, my, e.deltaY < 0 ? 1.2 : 1 / 1.2);
};

const onMouseMove = (e) => {
  if (!isDragging.value) return;
  const dx = e.clientX - dragStartX.value;
  const dy = e.clientY - dragStartY.value;
  if (!hasMoved.value && (Math.abs(dx) > 3 || Math.abs(dy) > 3)) {
    hasMoved.value = true;
  }
  if (hasMoved.value) {
    const clamped = clampTranslate(
      dragStartTX.value + dx,
      dragStartTY.value + dy,
      imageScale.value
    );
    translateX.value = clamped.tx;
    translateY.value = clamped.ty;
  }
};

const onMouseUp = () => {
  isDragging.value = false;
  setTimeout(() => {
    hasMoved.value = false;
  }, 0);
  window.removeEventListener("mousemove", onMouseMove);
  window.removeEventListener("mouseup", onMouseUp);
};

const handleMouseDown = (e) => {
  if (e.button !== 0) return;
  isDragging.value = true;
  hasMoved.value = false;
  dragStartX.value = e.clientX;
  dragStartY.value = e.clientY;
  dragStartTX.value = translateX.value;
  dragStartTY.value = translateY.value;
  window.addEventListener("mousemove", onMouseMove);
  window.addEventListener("mouseup", onMouseUp);
};

onUnmounted(() => {
  window.removeEventListener("mousemove", onMouseMove);
  window.removeEventListener("mouseup", onMouseUp);
});

const fileExtension = computed(() => {
  if (props.filename) {
    return props.filename.split(".").pop()?.toLowerCase() || "";
  }
  return "";
});

const viewerType = computed(() => {
  const ext = fileExtension.value;
  const mime = props.mimeType.toLowerCase();

  if (ext === "pdf" || mime === "application/pdf") return "pdf";
  // DOCX/DOC files are converted to PDF by backend - treat as PDF viewer
  if (["doc", "docx"].includes(ext) || mime.includes("word") || mime.includes("msword"))
    return "docx";
  if (["jpg", "jpeg", "png", "gif", "webp"].includes(ext) || mime.startsWith("image/"))
    return "image";
  if (["txt", "csv", "xml"].includes(ext) || mime.startsWith("text/")) return "text";

  return "unsupported";
});

const loadTextContent = async () => {
  if (!props.src || viewerType.value !== "text") return;
  loading.value = true;
  try {
    const response = await axios.get(props.src);
    textContent.value = response.data;
  } catch (e) {
    textContent.value = "Napaka pri nalaganju vsebine.";
  } finally {
    loading.value = false;
  }
};

// For DOCX files, the backend converts to PDF. If the preview isn't ready yet (202 status),
// we poll until it's available.
const docxPreviewUrl = ref("");
const loadDocxPreview = async () => {
  if (!props.src || viewerType.value !== "docx") return;

  previewGenerating.value = true;
  previewError.value = "";
  docxPreviewUrl.value = "";

  const maxRetries = 15;
  const retryDelay = 2000; // 2 seconds between retries

  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await axios.head(props.src, { validateStatus: () => true });

      if (response.status >= 200 && response.status < 300) {
        // Preview is ready
        docxPreviewUrl.value = props.src;
        previewGenerating.value = false;
        return;
      } else if (response.status === 202) {
        // Preview is being generated, wait and retry
        await new Promise((resolve) => setTimeout(resolve, retryDelay));
      } else {
        // Other error
        previewError.value = "Napaka pri nalaganju predogleda.";
        previewGenerating.value = false;
        return;
      }
    } catch (e) {
      previewError.value = "Napaka pri nalaganju predogleda.";
      previewGenerating.value = false;
      return;
    }
  }

  // Max retries reached
  previewError.value = "Predogled ni na voljo. Prosimo poskusite znova kasneje.";
  previewGenerating.value = false;
};

watch(
  () => [props.show, props.src],
  ([show]) => {
    if (show && viewerType.value === "text") {
      loadTextContent();
    }
    if (show && viewerType.value === "docx") {
      loadDocxPreview();
    }
    // Reset states when dialog closes
    if (!show) {
      previewGenerating.value = false;
      previewError.value = "";
      docxPreviewUrl.value = "";
      imageScale.value = 1;
      translateX.value = 0;
      translateY.value = 0;
      fitScale.value = 1;
    }
  },
  { immediate: true }
);
</script>

<template>
  <Dialog :open="show" @update:open="(open) => !open && $emit('close')">
    <DialogContent class="max-w-full xl:max-w-7xl">
      <DialogHeader>
        <DialogTitle>
          {{ title }}
        </DialogTitle>
        <DialogDescription>
          <Badge>
            {{ fileExtension }}
          </Badge>
        </DialogDescription>
      </DialogHeader>

      <div class="h-[70vh] overflow-auto">
        <!-- PDF Viewer (browser native) -->
        <template v-if="viewerType === 'pdf' && props.src">
          <iframe
            :src="props.src"
            class="w-full h-full rounded border"
            type="application/pdf"
          />
        </template>

        <!-- DOCX Viewer (converted to PDF by backend) -->
        <template v-else-if="viewerType === 'docx'">
          <!-- Loading/generating state -->
          <div
            v-if="previewGenerating"
            class="flex flex-col items-center justify-center h-full gap-4"
          >
            <Loader2 class="h-8 w-8 animate-spin text-indigo-600" />
            <span class="text-gray-500">Priprava predogleda dokumenta...</span>
          </div>
          <!-- Error state -->
          <div
            v-else-if="previewError"
            class="flex flex-col items-center justify-center h-full gap-4 text-gray-500"
          >
            <span>{{ previewError }}</span>
            <Button as="a" :href="props.src" target="_blank" variant="outline">
              Prenesi datoteko
            </Button>
          </div>
          <!-- Preview ready -->
          <iframe
            v-else-if="docxPreviewUrl"
            :src="docxPreviewUrl"
            class="w-full h-full rounded border"
            type="application/pdf"
          />
        </template>

        <!-- Image Viewer -->
        <template v-else-if="viewerType === 'image' && props.src">
          <div
            ref="containerRef"
            class="relative h-full overflow-hidden select-none"
            :class="imageCursorClass"
            @mousedown="handleMouseDown"
            @wheel.prevent="handleWheel"
          >
            <img
              ref="imageRef"
              :src="props.src"
              :alt="props.title"
              draggable="false"
              class="absolute top-0 left-0 max-w-none"
              :style="{
                transformOrigin: '0 0',
                transform: `translate(${translateX}px, ${translateY}px) scale(${imageScale})`,
                transition: isDragging ? 'none' : 'transform 0.12s ease',
              }"
              @load="handleImageLoad"
            />
            <!-- Zoom level badge -->
            <div
              class="absolute bottom-2 right-2 bg-black/50 text-white text-xs px-2 py-1 rounded pointer-events-none"
            >
              {{ Math.round(imageScale * 100) }}%
            </div>
            <!-- Reset button -->
            <Button
              v-if="imageScale > fitScale + 0.01"
              size="icon-sm"
              variant="secondary"
              class="absolute top-2 right-2 opacity-70 hover:opacity-100"
              title="Ponastavi pogled"
              @click.stop="resetImageView"
            >
              <RotateCcwIcon class="h-3 w-3" />
            </Button>
            <!-- Hint -->
            <div
              v-if="imageScale <= fitScale + 0.01"
              class="absolute bottom-2 left-1/2 -translate-x-1/2 bg-black/40 text-white text-xs px-2 py-1 rounded pointer-events-none select-none"
            >
              Kolesce za povečavo / pomanjšavo · Povleči za premik
            </div>
          </div>
        </template>

        <!-- Text/CSV/XML Viewer -->
        <template v-else-if="viewerType === 'text'">
          <div v-if="loading" class="flex items-center justify-center h-full">
            <div class="animate-pulse text-gray-500">Nalaganje...</div>
          </div>
          <pre
            v-else
            class="p-4 bg-gray-50 dark:bg-gray-900 rounded border text-sm overflow-auto h-full whitespace-pre-wrap wrap-break-word"
            >{{ textContent }}</pre
          >
        </template>

        <!-- Unsupported -->
        <template v-else-if="viewerType === 'unsupported'">
          <div
            class="flex flex-col items-center justify-center h-full gap-4 text-gray-500"
          >
            <span>Predogled ni na voljo za to vrsto datoteke.</span>
            <Button as="a" :href="props.src" target="_blank" variant="outline">
              Prenesi datoteko
            </Button>
          </div>
        </template>

        <!-- No source -->
        <div v-else class="text-sm text-gray-500">Ni dokumenta za prikaz.</div>
      </div>

      <div class="flex justify-end mt-4">
        <Button type="button" variant="outline" @click="$emit('close')">Zapri</Button>
      </div>
    </DialogContent>
  </Dialog>
</template>
