<template>
  <teleport to="body">
    <modal v-if="modalOpen" @close="modalOpen = false">
      <template v-slot:header>
        <h2>{{ $t("signature_confirmation") }}</h2>
      </template>
      <template v-slot:body>
        <div class="signature-modal-body text-center" v-if="loading">
          <p>{{ $t("electronic_signature_in_progress") }}</p>
          <div class="loading">
            <i
              class="fa fa-circle-o-notch fa-spin fa-3x"
              :title="$t('loading')"
            ></i>
          </div>
        </div>
        <div class="signature-modal-body text-center" v-else>
          <p>{{ $t("you_are_going_to_sign") }}</p>
          <p>{{ $t("are_you_sure") }}</p>
        </div>
      </template>
      <template v-slot:footer>
        <button class="btn btn-action" @click.prevent="confirmSign">
          {{ $t("yes") }}
        </button>
      </template>
    </modal>
  </teleport>
  <div class="col-12 m-auto">
    <div class="row justify-content-center border-bottom pdf-tools">
      <div v-if="pageCount > 1" class="col-4 text-center turn-page">
        <button
          class="btn btn-light btn-sm"
          :disabled="page <= 1"
          @click="turnPage(-1)"
        >
          ❮
        </button>
        <span>page {{ page }} / {{ pageCount }}</span>
        <button
          class="btn btn-light btn-sm"
          :disabled="page >= pageCount"
          @click="turnPage(1)"
        >
          ❯
        </button>
      </div>
      <div v-if="signature.zones.length > 1" class="col-4 col-xl-3 text-end">
        <button
          :disabled="userSignatureZone === null || userSignatureZone?.index < 1"
          class="btn btn-light btn-sm"
          @click="turnSignature(-1)"
        >
          {{ $t("last_sign_zone") }}
        </button>
      </div>
      <div v-if="signature.zones.length > 1" class="col-4 col-xl-3 text-start">
        <button
          :disabled="userSignatureZone?.index >= signature.zones.length - 1"
          class="btn btn-light btn-sm"
          @click="turnSignature(1)"
        >
          {{ $t("next_sign_zone") }}
        </button>
      </div>
    </div>
  </div>
  <div class="col-xs-12 col-md-12 col-lg-9 m-auto my-5 text-center">
    <canvas class="m-auto" id="canvas"></canvas>
  </div>

  <div
    class="col-xs-12 col-md-12 col-lg-9 m-auto p-4"
    id="action-buttons"
    v-if="signedState !== 'signed'"
  >
    <div class="row mb-3">
      <div class="col-12 d-flex justify-content-end">
        <div class="col-4 col-xl-3 gap-2 d-grid">
          <button
            v-if="adding"
            class="btn btn-misc btn-cancel me-2 btn-sm"
            @click="removeNewZone()"
          >
            {{ $t("remove_sign_zone") }}
          </button>
        </div>
        <div class="col-4 gap-2 d-grid">
          <button
            class="btn btn-create btn-sm"
            :class="{ active: canvasEvent === 'add' }"
            @click="toggleAddZone()"
          >
            {{ $t("add_sign_zone") }}
          </button>
        </div>
      </div>
    </div>

    <div class="row">
      <div class="col-4">
        <button
          class="btn btn-action me-2"
          :disabled="!userSignatureZone"
          @click="sign"
        >
          {{ $t("sign") }}
        </button>
      </div>
      <div class="col-8 d-flex justify-content-end">
        <button
          class="btn btn-misc me-2"
          :hidden="!userSignatureZone"
          @click="undoSign"
          v-if="signature.zones.length > 1"
        >
          {{ $t("choose_another_signature") }}
        </button>
        <button
          class="btn btn-misc me-2"
          :hidden="!userSignatureZone"
          @click="undoSign"
          v-else
        >
          {{ $t("cancel") }}
        </button>
        <a class="btn btn-delete" :href="getReturnPath()">
          {{ $t("cancel_signing") }}
        </a>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, Ref, reactive } from "vue";
import { useToast } from "vue-toast-notification";
import "vue-toast-notification/dist/theme-sugar.css";
import {
  CanvasEvent,
  Signature,
  SignatureZone,
  SignedState,
} from "../../types";
import { makeFetch } from "../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
import * as pdfjsLib from "pdfjs-dist";
import {
  PDFDocumentProxy,
  PDFPageProxy,
} from "pdfjs-dist/types/src/display/api";

// @ts-ignore
import * as PdfWorker from "pdfjs-dist/build/pdf.worker.mjs";
console.log(PdfWorker); // incredible but this is needed

// import { PdfWorker } from 'pdfjs-dist/build/pdf.worker.mjs'
// pdfjsLib.GlobalWorkerOptions.workerSrc = PdfWorker;

import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
import { download_and_decrypt_doc } from "../StoredObjectButton/helpers";

pdfjsLib.GlobalWorkerOptions.workerSrc = "pdfjs-dist/build/pdf.worker.mjs";

const modalOpen: Ref<boolean> = ref(false);
const loading: Ref<boolean> = ref(false);
const adding: Ref<boolean> = ref(false);
const canvasEvent: Ref<CanvasEvent> = ref("select");
const signedState: Ref<SignedState> = ref("pending");
const page: Ref<number> = ref(1);
const pageCount: Ref<number> = ref(0);
let userSignatureZone: Ref<null | SignatureZone> = ref(null);
let pdf = {} as PDFDocumentProxy;

declare global {
  interface Window {
    signature: Signature;
  }
}

const $toast = useToast();

const signature = window.signature;

console.log(signature);

const mountPdf = async (url: string) => {
  const loadingTask = pdfjsLib.getDocument(url);
  pdf = await loadingTask.promise;
  pageCount.value = pdf.numPages;
  await setPage(1);
};

const getRenderContext = (pdfPage: PDFPageProxy) => {
  const scale = 1;
  const viewport = pdfPage.getViewport({ scale });
  const canvas = document.querySelectorAll("canvas")[0] as HTMLCanvasElement;
  const context = canvas.getContext("2d") as CanvasRenderingContext2D;
  canvas.height = viewport.height;
  canvas.width = viewport.width;

  return {
    canvasContext: context,
    viewport: viewport,
  };
};

const setPage = async (page: number) => {
  const pdfPage = await pdf.getPage(page);
  const renderContext = getRenderContext(pdfPage);
  await pdfPage.render(renderContext);
};

async function downloadAndOpen(): Promise<Blob> {
  let raw;
  try {
    raw = await download_and_decrypt_doc(
      signature.storedObject,
      signature.storedObject.currentVersion
    );
  } catch (e) {
    console.error("error while downloading and decrypting document", e);
    throw e;
  }
  await mountPdf(URL.createObjectURL(raw));
  initPdf();
  return raw;
}

const initPdf = () => {
  const canvas = document.querySelectorAll("canvas")[0] as HTMLCanvasElement;
  canvas.addEventListener("pointerup", canvasClick, false);
  setTimeout(() => addZones(page.value), 800);
};

const scaleXToCanvas = (x: number, canvasWidth: number, PDFWidth: number) =>
  Math.round((x * canvasWidth) / PDFWidth);

const scaleYToCanvas = (h: number, canvasHeight: number, PDFHeight: number) =>
  Math.round((h * canvasHeight) / PDFHeight);

const hitSignature = (
  zone: SignatureZone,
  xy: number[],
  canvasWidth: number,
  canvasHeight: number
) =>
  scaleXToCanvas(zone.x, canvasWidth, zone.PDFPage.width) < xy[0] &&
  xy[0] <
    scaleXToCanvas(zone.x + zone.width, canvasWidth, zone.PDFPage.width) &&
  zone.PDFPage.height -
    scaleYToCanvas(zone.y, canvasHeight, zone.PDFPage.height) <
    xy[1] &&
  xy[1] <
    scaleYToCanvas(zone.height - zone.y, canvasHeight, zone.PDFPage.height) +
      zone.PDFPage.height;

const selectZone = (z: SignatureZone, canvas: HTMLCanvasElement) => {
  userSignatureZone.value = z;
  const ctx = canvas.getContext("2d");
  if (ctx) {
    setPage(page.value);
    setTimeout(() => drawZone(z, ctx, canvas.width, canvas.height), 200);
  }
};

const selectZoneOnCanvas = (e: PointerEvent, canvas: HTMLCanvasElement) =>
  signature.zones
    .filter((z) => z.PDFPage.index + 1 === page.value)
    .map((z) => {
      if (
        hitSignature(z, [e.offsetX, e.offsetY], canvas.width, canvas.height)
      ) {
        if (userSignatureZone.value === null) {
          selectZone(z, canvas);
        } else {
          if (userSignatureZone.value.index === z.index) {
            sign();
          }
        }
      }
    });

const canvasClick = (e: PointerEvent) => {
  const canvas = document.querySelectorAll("canvas")[0] as HTMLCanvasElement;
  canvasEvent.value === "select"
    ? selectZoneOnCanvas(e, canvas)
    : addZoneOnCanvas(e, canvas);
};

const turnPage = async (upOrDown: number) => {
  userSignatureZone.value = null;
  page.value = page.value + upOrDown;
  await setPage(page.value);
  setTimeout(() => addZones(page.value), 200);
};

const turnSignature = async (upOrDown: number) => {
  let zoneIndex = userSignatureZone.value?.index ?? -1;
  if (zoneIndex < -1) {
    zoneIndex = -1;
  }
  if (zoneIndex < signature.zones.length) {
    zoneIndex = zoneIndex + upOrDown;
  } else {
    zoneIndex = 0;
  }
  let currentZone = signature.zones[zoneIndex];
  if (currentZone) {
    page.value = currentZone.PDFPage.index + 1;
    userSignatureZone.value = currentZone;
    const canvas = document.querySelectorAll("canvas")[0];
    selectZone(currentZone, canvas);
  }
};

const drawZone = (
  zone: SignatureZone,
  ctx: CanvasRenderingContext2D,
  canvasWidth: number,
  canvasHeight: number
) => {
  const unselectedBlue = "#007bff";
  const selectedBlue = "#034286";
  ctx.strokeStyle =
    userSignatureZone.value?.index === zone.index
      ? selectedBlue
      : unselectedBlue;
  ctx.lineWidth = 2;
  ctx.lineJoin = "bevel";
  ctx.strokeRect(
    scaleXToCanvas(zone.x, canvasWidth, zone.PDFPage.width),
    zone.PDFPage.height -
      scaleYToCanvas(zone.y, canvasHeight, zone.PDFPage.height),
    scaleXToCanvas(zone.width, canvasWidth, zone.PDFPage.width),
    scaleYToCanvas(zone.height, canvasHeight, zone.PDFPage.height)
  );
  ctx.font = "bold 16px serif";
  ctx.textAlign = "center";
  ctx.fillStyle = "black";
  const xText =
    scaleXToCanvas(zone.x, canvasWidth, zone.PDFPage.width) +
    scaleXToCanvas(zone.width, canvasWidth, zone.PDFPage.width) / 2;
  const yText =
    zone.PDFPage.height -
    scaleYToCanvas(zone.y, canvasHeight, zone.PDFPage.height) +
    scaleYToCanvas(zone.height, canvasHeight, zone.PDFPage.height) / 2;
  if (userSignatureZone.value?.index === zone.index) {
    ctx.fillStyle = selectedBlue;
    ctx.fillText("Signer ici", xText, yText);
  } else {
    ctx.fillStyle = unselectedBlue;
    ctx.fillText("Choisir cette", xText, yText - 12);
    ctx.fillText("zone de signature", xText, yText + 12);
    // ctx.strokeStyle = "#c6c6c6"; // halo
    // ctx.strokeText("Choisir cette", xText, yText - 12);
    // ctx.strokeText("zone de signature", xText, yText + 12);
  }
};

const addZones = (page: number) => {
  const canvas = document.querySelectorAll("canvas")[0];
  const ctx = canvas.getContext("2d");
  if (ctx) {
    signature.zones
      .filter((z) => z.PDFPage.index + 1 === page)
      .map((z) => drawZone(z, ctx, canvas.width, canvas.height));
  }
};

const checkSignature = () => {
  const url = `/api/1.0/document/workflow/${signature.id}/check-signature`;
  return makeFetch("GET", url)
    .then((r) => {
      signedState.value = r as SignedState;
      checkForReady();
    })
    .catch((error) => {
      signedState.value = "error";
      console.log("Error while checking the signature", error);
      $toast.error(
        `Erreur lors de la vérification de la signature: ${error.txt}`
      );
    });
};

const maxTryForReady = 60; //2 minutes for trying to sign
let tryForReady = 0;

const stopTrySigning = () => {
  loading.value = false;
  modalOpen.value = false;
};

const checkForReady = () => {
  if (tryForReady > maxTryForReady) {
    stopTrySigning();
    tryForReady = 0;
    console.log("Reached the maximum number of tentative to try signing");
    $toast.error(
      "Le nombre maximum de tentatives pour essayer de signer est atteint"
    );
  }
  if (signedState.value === "rejected") {
    stopTrySigning();
    console.log("Signature rejected by the server");
    $toast.error("Signature rejetée par le serveur");
  }
  if (signedState.value === "canceled") {
    stopTrySigning();
    console.log("Signature canceled");
    $toast.error("Signature annulée");
  }
  if (signedState.value === "pending") {
    tryForReady = tryForReady + 1;
    setTimeout(() => checkSignature(), 2000);
  } else {
    stopTrySigning();
    if (signedState.value === "signed") {
      userSignatureZone.value = null;
      downloadAndOpen();
    }
  }
};

const sign = () => (modalOpen.value = true);

const confirmSign = () => {
  loading.value = true;
  const url = `/api/1.0/document/workflow/${signature.id}/signature-request`;
  const body = {
    storedObject: signature.storedObject,
    zone: userSignatureZone.value,
  };
  makeFetch("POST", url, body)
    .then((r) => {
      checkForReady();
    })
    .catch((error) => {
      console.log("Error while posting the signature", error);
      stopTrySigning();
      $toast.error(
        `Erreur lors de la soumission de la signature: ${error.txt}`
      );
    });
};

const undoSign = async () => {
  signature.zones = signature.zones.filter((z) => z.index !== null);
  await setPage(page.value);
  setTimeout(() => addZones(page.value), 200);
  userSignatureZone.value = null;
  adding.value = false;
};

const toggleAddZone = () => {
  canvasEvent.value === "select"
    ? (canvasEvent.value = "add")
    : (canvasEvent.value = "select");
};

const addZoneOnCanvas = (e: PointerEvent, canvas: HTMLCanvasElement) => {
  const BOX_WIDTH = 180;
  const BOX_HEIGHT = 90;
  const PDFPageHeight = canvas.height;
  const PDFPageWidth = canvas.width;

  const x = e.offsetX;
  const y = e.offsetY;
  const newZone: SignatureZone = {
    index: null,
    x:
      scaleXToCanvas(x, canvas.width, PDFPageWidth) -
      scaleXToCanvas(BOX_WIDTH / 2, canvas.width, PDFPageWidth),
    y:
      PDFPageHeight -
      scaleYToCanvas(y, canvas.height, PDFPageHeight) +
      scaleYToCanvas(BOX_HEIGHT / 2, canvas.height, PDFPageHeight),
    width: BOX_WIDTH,
    height: BOX_HEIGHT,
    PDFPage: {
      index: page.value - 1,
      width: PDFPageWidth,
      height: PDFPageHeight,
    },
  };
  signature.zones.push(newZone);

  setTimeout(() => addZones(page.value), 200);
  canvasEvent.value = "select";
  adding.value = true;
};

const removeNewZone = async () => {
  signature.zones = signature.zones.filter((z) => z.index !== null);
  userSignatureZone.value = null;
  await setPage(page.value);
  setTimeout(() => addZones(page.value), 200);
  canvasEvent.value = "select";
  adding.value = false;
};

const getReturnPath = () =>
  window.location.search
    ? window.location.search.split("?returnPath=")[1] ??
      window.location.pathname
    : window.location.pathname;

downloadAndOpen();
</script>

<style scoped lang="scss">
#canvas {
  box-shadow: 0 20px 20px rgba(0, 0, 0, 0.2);
}
div#action-buttons {
  position: sticky;
  bottom: 0px;
  background-color: white;
  z-index: 100;
}
div.pdf-tools {
  background-color: #f3f3f3;
  @media (min-width: 1200px) {
    background: none;
    border: none !important;
  }
}
div.turn-page {
  span {
    font-size: 0.8rem;
    margin: 0 0.4rem;
  }
}
div.signature-modal-body {
  height: 8rem;
}
</style>

