<script setup>
import { ref, computed, watch, onUnmounted } from "vue";
import {
  Drawer,
  DrawerContent,
  DrawerHeader,
  DrawerTitle,
  DrawerFooter,
  DrawerClose,
} from "@/Components/ui/drawer";
import { ScrollArea } from "@/Components/ui/scroll-area";
import { Button } from "@/Components/ui/button";
import { Badge } from "@/Components/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;
  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 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);
};

// Touch pinch-to-zoom
let lastTouchDist = null;
let lastTouchMidX = null;
let lastTouchMidY = null;

const getTouchDist = (touches) => {
  const dx = touches[0].clientX - touches[1].clientX;
  const dy = touches[0].clientY - touches[1].clientY;
  return Math.hypot(dx, dy);
};

const handleTouchStart = (e) => {
  if (e.touches.length === 2) {
    lastTouchDist = getTouchDist(e.touches);
    const rect = containerRef.value.getBoundingClientRect();
    lastTouchMidX = (e.touches[0].clientX + e.touches[1].clientX) / 2 - rect.left;
    lastTouchMidY = (e.touches[0].clientY + e.touches[1].clientY) / 2 - rect.top;
    e.preventDefault();
  } else if (e.touches.length === 1) {
    isDragging.value = true;
    hasMoved.value = false;
    dragStartX.value = e.touches[0].clientX;
    dragStartY.value = e.touches[0].clientY;
    dragStartTX.value = translateX.value;
    dragStartTY.value = translateY.value;
  }
};

const handleTouchMove = (e) => {
  if (e.touches.length === 2 && lastTouchDist !== null) {
    e.preventDefault();
    const dist = getTouchDist(e.touches);
    const factor = dist / lastTouchDist;
    const rect = containerRef.value.getBoundingClientRect();
    const midX = (e.touches[0].clientX + e.touches[1].clientX) / 2 - rect.left;
    const midY = (e.touches[0].clientY + e.touches[1].clientY) / 2 - rect.top;
    zoomAt(midX, midY, factor);
    lastTouchDist = dist;
    lastTouchMidX = midX;
    lastTouchMidY = midY;
  } else if (e.touches.length === 1 && isDragging.value) {
    const dx = e.touches[0].clientX - dragStartX.value;
    const dy = e.touches[0].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 handleTouchEnd = () => {
  lastTouchDist = null;
  isDragging.value = false;
  setTimeout(() => { hasMoved.value = false; }, 0);
};

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";
  if (["doc", "docx"].includes(ext) || mime.includes("word") || mime.includes("msword"))
    return "docx";
  if (["jpg", "jpeg", "png", "gif", "webp", "heic", "heif"].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 {
    textContent.value = "Napaka pri nalaganju vsebine.";
  } finally {
    loading.value = false;
  }
};

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;
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await axios.head(props.src, { validateStatus: () => true });
      if (response.status >= 200 && response.status < 300) {
        docxPreviewUrl.value = props.src;
        previewGenerating.value = false;
        return;
      } else if (response.status === 202) {
        await new Promise((resolve) => setTimeout(resolve, retryDelay));
      } else {
        previewError.value = "Napaka pri nalaganju predogleda.";
        previewGenerating.value = false;
        return;
      }
    } catch {
      previewError.value = "Napaka pri nalaganju predogleda.";
      previewGenerating.value = false;
      return;
    }
  }
  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();
    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>
  <Drawer :open="show" @update:open="(val) => !val && emit('close')">
    <DrawerContent class="flex flex-col h-[95vh]">
      <DrawerHeader class="border-b px-4 py-3 shrink-0">
        <DrawerTitle class="truncate pr-4">{{ title }}</DrawerTitle>
        <div class="mt-1">
          <Badge>{{ fileExtension }}</Badge>
        </div>
      </DrawerHeader>

      <!-- Viewer area: flex-1 + min-h-0 works because parent has fixed h-[95vh] -->
      <div class="flex-1 min-h-0 overflow-hidden">
        <!-- PDF Viewer -->
        <template v-if="viewerType === 'pdf' && src">
          <iframe :src="src" class="w-full h-full" type="application/pdf" />
        </template>

        <!-- DOCX Viewer (converted to PDF by backend) -->
        <template v-else-if="viewerType === 'docx'">
          <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>
          <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="src" target="_blank" variant="outline">
              Prenesi datoteko
            </Button>
          </div>
          <iframe
            v-else-if="docxPreviewUrl"
            :src="docxPreviewUrl"
            class="w-full h-full"
            type="application/pdf"
          />
        </template>

        <!-- Image Viewer with touch pinch-to-zoom -->
        <template v-else-if="viewerType === 'image' && src">
          <div
            ref="containerRef"
            class="relative h-full overflow-hidden select-none"
            :class="imageCursorClass"
            @mousedown="handleMouseDown"
            @wheel.prevent="handleWheel"
            @touchstart.prevent="handleTouchStart"
            @touchmove.prevent="handleTouchMove"
            @touchend="handleTouchEnd"
          >
            <img
              ref="imageRef"
              :src="src"
              :alt="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"
            />
            <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>
            <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>
            <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"
            >
              Ščipni za poveč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>
          <ScrollArea v-else class="h-full">
            <pre
              class="p-4 bg-gray-50 dark:bg-gray-900 text-sm whitespace-pre-wrap wrap-break-word"
              >{{ textContent }}</pre
            >
          </ScrollArea>
        </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="src" target="_blank" variant="outline">
              Prenesi datoteko
            </Button>
          </div>
        </template>

        <div v-else class="flex items-center justify-center h-full text-sm text-gray-500">
          Ni dokumenta za prikaz.
        </div>
      </div>

      <DrawerFooter class="border-t shrink-0 px-4 py-3">
        <DrawerClose as-child>
          <Button variant="outline" class="w-full" @click="emit('close')">Zapri</Button>
        </DrawerClose>
      </DrawerFooter>
    </DrawerContent>
  </Drawer>
</template>
