import { layout } from './stores/layout.svelte.js';
import type { PDFPageProxy } from 'pdfjs-dist';

let pdfPage: PDFPageProxy | null = null;
let pdfjsLib: typeof import('pdfjs-dist') | null = null;
let pdfNatWidth = 0;
let pdfNatHeight = 0;

// High-res raster rendered once at load time
let hiResCanvas: HTMLCanvasElement | null = null;

// Callback to pass the image to Canvas component
let onImageUpdate: ((img: HTMLCanvasElement | null, natW: number, natH: number) => void) | null = null;

export function setImageUpdateCallback(cb: (img: HTMLCanvasElement | null, natW: number, natH: number) => void) {
  onImageUpdate = cb;
  if (hiResCanvas) {
    cb(hiResCanvas, pdfNatWidth, pdfNatHeight);
  }
}

// --- IndexedDB for PDF persistence ---

const DB_NAME = 'scada_pdf_store';
const DB_VERSION = 1;
const STORE_NAME = 'pdfs';

function openDb(): Promise<IDBDatabase> {
  return new Promise((resolve, reject) => {
    const req = indexedDB.open(DB_NAME, DB_VERSION);
    req.onupgradeneeded = () => {
      req.result.createObjectStore(STORE_NAME);
    };
    req.onsuccess = () => resolve(req.result);
    req.onerror = () => reject(req.error);
  });
}

function getPdfKey(): string {
  return `${layout.currentProject}_${layout.currentMcm}`;
}

async function savePdfData(data: ArrayBuffer) {
  try {
    const db = await openDb();
    const tx = db.transaction(STORE_NAME, 'readwrite');
    tx.objectStore(STORE_NAME).put(data, getPdfKey());
    await new Promise<void>((resolve, reject) => {
      tx.oncomplete = () => resolve();
      tx.onerror = () => reject(tx.error);
    });
    db.close();
  } catch (e) {
    console.warn('Failed to save PDF to IndexedDB:', e);
  }
}

async function loadPdfData(): Promise<ArrayBuffer | null> {
  try {
    const db = await openDb();
    const tx = db.transaction(STORE_NAME, 'readonly');
    const req = tx.objectStore(STORE_NAME).get(getPdfKey());
    const result = await new Promise<ArrayBuffer | null>((resolve, reject) => {
      req.onsuccess = () => resolve(req.result ?? null);
      req.onerror = () => reject(req.error);
    });
    db.close();
    return result;
  } catch (e) {
    console.warn('Failed to load PDF from IndexedDB:', e);
    return null;
  }
}

async function deletePdfData() {
  try {
    const db = await openDb();
    const tx = db.transaction(STORE_NAME, 'readwrite');
    tx.objectStore(STORE_NAME).delete(getPdfKey());
    db.close();
  } catch (e) {
    console.warn('Failed to delete PDF from IndexedDB:', e);
  }
}

// --- PDF loading ---

async function ensurePdfjs() {
  if (!pdfjsLib) {
    pdfjsLib = await import('pdfjs-dist');
    // @ts-expect-error Vite handles new URL(..., import.meta.url) at build time
    const workerUrl = new URL('pdfjs-dist/build/pdf.worker.min.mjs', import.meta.url);
    pdfjsLib.GlobalWorkerOptions.workerSrc = workerUrl.href;
  }
  return pdfjsLib;
}

/**
 * Render the PDF page once at high resolution (HI_RES_SCALE x native).
 * This gives crisp quality when zooming in up to ~5x without re-rendering.
 * The canvas is stored as a DOM element and scaled via CSS (instant, no wait).
 */
const HI_RES_SCALE = 5;

async function renderHiRes() {
  if (!pdfPage) return;

  const viewport = pdfPage.getViewport({ scale: HI_RES_SCALE });

  if (!hiResCanvas) {
    hiResCanvas = document.createElement('canvas');
  }
  hiResCanvas.width = viewport.width;
  hiResCanvas.height = viewport.height;

  const ctx = hiResCanvas.getContext('2d');
  if (!ctx) return;
  await pdfPage.render({ canvasContext: ctx, viewport }).promise;

  notifyImage();
}

function notifyImage() {
  if (onImageUpdate) {
    onImageUpdate(hiResCanvas, pdfNatWidth, pdfNatHeight);
  }
}

async function loadPdfFromBuffer(data: ArrayBuffer, resetTransform: boolean) {
  const lib = await ensurePdfjs();
  const pdf = await lib.getDocument({ data }).promise;
  pdfPage = await pdf.getPage(1);

  const vp = pdfPage.getViewport({ scale: 1 });
  pdfNatWidth = vp.width;
  pdfNatHeight = vp.height;

  if (resetTransform) {
    layout.pdfScale = Math.min(layout.canvasW / pdfNatWidth, layout.canvasH / pdfNatHeight);
    layout.pdfOffsetX = 0;
    layout.pdfOffsetY = 0;
  }

  layout.pdfLoaded = true;
  await renderHiRes();
  layout.markDirty();
}

/** Load PDF from raw data, persist to IndexedDB, and save layout state */
async function loadAndPersistPdf(data: ArrayBuffer) {
  const copy = data.slice(0);
  await loadPdfFromBuffer(data, true);
  await savePdfData(copy);
  layout.saveMcmState();
}

// --- Public API ---

export async function loadPdfFile(file: File) {
  await loadAndPersistPdf(await file.arrayBuffer());
}

export async function loadPdfFromPath(path: string) {
  try {
    const resp = await fetch(path);
    if (!resp.ok) return;
    await loadAndPersistPdf(await resp.arrayBuffer());
  } catch (e) {
    console.warn('Could not load PDF from path:', e);
  }
}

export async function restorePdf() {
  const data = await loadPdfData();
  if (!data) return;
  await loadPdfFromBuffer(data, false);
}

export function removePdf() {
  pdfPage = null;
  hiResCanvas = null;
  pdfNatWidth = 0;
  pdfNatHeight = 0;
  layout.pdfScale = 1.0;
  layout.pdfOffsetX = 0;
  layout.pdfOffsetY = 0;
  layout.pdfLoaded = false;
  layout.editingBackground = false;
  notifyImage();
  layout.markDirty();
  deletePdfData();
  layout.saveMcmState();
}

const PDF_ZOOM_STEP = 1.1;
const PDF_MIN_SCALE = 0.05;

export function pdfZoomIn() {
  if (!layout.pdfLoaded) return;
  layout.pdfScale *= PDF_ZOOM_STEP;
  layout.markDirty();
  layout.saveMcmState();
}

export function pdfZoomOut() {
  if (!layout.pdfLoaded) return;
  layout.pdfScale = Math.max(PDF_MIN_SCALE, layout.pdfScale / PDF_ZOOM_STEP);
  layout.markDirty();
  layout.saveMcmState();
}

export function toggleEditBackground() {
  if (!layout.pdfLoaded) return;
  layout.editingBackground = !layout.editingBackground;
}
