<script setup lang="ts">
import { invoke } from "@tauri-apps/api/core";
import {
  GlobalWorkerOptions,
  getDocument,
  type PDFDocumentLoadingTask,
  type PDFDocumentProxy,
  type RenderTask,
} from "pdfjs-dist";
import { computed, nextTick, onBeforeUnmount, ref, shallowRef, watch } from "vue";

const props = defineProps<{
  scanUrl: string;
  title: string;
}>();

const show = defineModel<boolean>("show", { required: true });

GlobalWorkerOptions.workerSrc = new URL(
  "pdfjs-dist/build/pdf.worker.min.mjs",
  import.meta.url,
).toString();

const canvasRef = ref<HTMLCanvasElement | null>(null);
const pdfDocument = shallowRef<PDFDocumentProxy | null>(null);
const loading = ref(false);
const error = ref("");
const pageNumber = ref(1);
const pageCount = ref(0);
const scale = ref(1.1);

let loadingTask: PDFDocumentLoadingTask | null = null;
let renderTask: RenderTask | null = null;
const pageInfo = computed(() => `${pageNumber.value} / ${pageCount.value}`);
const canGoBack = computed(() => pageNumber.value > 1);
const canGoForward = computed(() => pageNumber.value < pageCount.value);

async function loadDocument() {
  resetDocument();

  loading.value = true;
  error.value = "";

  try {
    const bytes = await invoke<number[]>("load_remote_file", {
      url: props.scanUrl,
    });

    if (!bytes.length) {
      throw new Error("Файл пустой");
    }

    const task = getDocument({ data: new Uint8Array(bytes) });
    loadingTask = task;

    const document = await task.promise;
    if (loadingTask !== task) {
      await document.destroy();
      return;
    }

    pdfDocument.value = document;
    pageCount.value = document.numPages;
    pageNumber.value = 1;
    scale.value = 1.1;
    loading.value = false;
    await nextTick();
    await renderPage();
  } catch (err) {
    error.value = err instanceof Error ? err.message : "Не удалось открыть PDF";
    loading.value = false;
    if (loadingTask) {
      loadingTask = null;
    }
  }
}

async function renderPage() {
  const document = pdfDocument.value;
  const canvas = canvasRef.value;

  if (!document || !canvas) {
    return;
  }

  renderTask?.cancel();

  const page = await document.getPage(pageNumber.value);
  const viewport = page.getViewport({ scale: scale.value });
  const context = canvas.getContext("2d");

  if (!context) {
    error.value = "Не удалось создать canvas context";
    return;
  }

  const devicePixelRatio = window.devicePixelRatio || 1;
  canvas.width = Math.floor(viewport.width * devicePixelRatio);
  canvas.height = Math.floor(viewport.height * devicePixelRatio);
  canvas.style.width = `${viewport.width}px`;
  canvas.style.height = `${viewport.height}px`;

  context.setTransform(devicePixelRatio, 0, 0, devicePixelRatio, 0, 0);

  renderTask = page.render({ canvas: canvas, canvasContext: context, viewport });

  try {
    await renderTask.promise;
  } catch (err) {
    if (!(err instanceof Error) || err.name !== "RenderingCancelledException") {
      error.value = err instanceof Error ? err.message : "Не удалось отрисовать PDF";
    }
  }
}

function resetDocument() {
  renderTask?.cancel();
  renderTask = null;
  loadingTask?.destroy().catch(() => undefined);
  loadingTask = null;
  pdfDocument.value?.destroy().catch(() => undefined);
  pdfDocument.value = null;
  pageCount.value = 0;
  pageNumber.value = 1;
  loading.value = false;
  error.value = "";
}

async function goBack() {
  if (canGoBack.value) {
    pageNumber.value -= 1;
    await nextTick();
    await renderPage();
  }
}

async function goForward() {
  if (canGoForward.value) {
    pageNumber.value += 1;
    await nextTick();
    await renderPage();
  }
}

async function zoomIn() {
  scale.value = Math.min(scale.value + 0.2, 3);
  await nextTick();
  await renderPage();
}

async function zoomOut() {
  scale.value = Math.max(scale.value - 0.2, 0.5);
  await nextTick();
  await renderPage();
}

watch([show, () => props.scanUrl], async ([visible]) => {
  if (visible) {
    await nextTick();
    await loadDocument();
  } else {
    resetDocument();
  }
});

onBeforeUnmount(() => {
  resetDocument();
});
</script>

<template>
  <van-popup v-model:show="show" round position="bottom" class="pdf-preview">
    <div class="pdf-preview__header">
      <div class="pdf-preview__title">{{ title }}</div>
      <van-button size="small" plain @click="show = false">Закрыть</van-button>
    </div>

    <div class="pdf-preview__toolbar">
      <van-button size="small" plain :disabled="!canGoBack" @click="goBack">
        Назад
      </van-button>
      <div class="pdf-preview__page-info">{{ pageInfo }}</div>
      <van-button size="small" plain :disabled="!canGoForward" @click="goForward">
        Вперед
      </van-button>
      <van-button size="small" plain @click="zoomOut">-</van-button>
      <van-button size="small" plain @click="zoomIn">+</van-button>
      </div>

    <div class="pdf-preview__body">
      <van-loading v-if="loading">Загрузка PDF...</van-loading>
      <van-empty v-else-if="error" :description="error" />
      <div v-else class="pdf-preview__canvas-wrap">
        <canvas ref="canvasRef" class="pdf-preview__canvas" />
      </div>
    </div>
  </van-popup>
</template>

<style scoped>
.pdf-preview {
  display: flex;
  flex-direction: column;
  height: 92vh;
  background: #f5f7fb;
}

.pdf-preview__header,
.pdf-preview__toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 16px;
  background: #fff;
}

.pdf-preview__header {
  justify-content: space-between;
  border-bottom: 1px solid #eef0f4;
}

.pdf-preview__title {
  min-width: 0;
  font-size: 16px;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pdf-preview__toolbar {
  justify-content: center;
  flex-wrap: wrap;
  border-bottom: 1px solid #eef0f4;
}

.pdf-preview__page-info {
  min-width: 72px;
  text-align: center;
  color: #636b74;
  font-size: 13px;
}

.pdf-preview__body {
  flex: 1;
  min-height: 0;
  overflow: auto;
  padding: 16px;
}

.pdf-preview__canvas-wrap {
  display: flex;
  justify-content: center;
}

.pdf-preview__canvas {
  max-width: 100%;
  height: auto;
  background: #fff;
  box-shadow: 0 8px 28px rgba(15, 23, 42, 0.16);
}
</style>
