// Use the legacy build — the main build relies on browser-only APIs (DOMMatrix
// etc.) that aren't available in a Bun/Node server context.
// @ts-ignore — legacy path has no separate .d.ts; types are compatible
import * as pdfjsLib from "pdfjs-dist/legacy/build/pdf.mjs";

// Point at the legacy worker. import.meta.resolve returns a file:// URL that
// Bun can load as a Worker, which pdfjs requires even in server contexts.
pdfjsLib.GlobalWorkerOptions.workerSrc = import.meta.resolve(
  "pdfjs-dist/legacy/build/pdf.worker.mjs",
);

// ─── Types ────────────────────────────────────────────────────────────────────

/** Raw extracted field values keyed by PDF field name. */
export type FieldValues = Record<string, string>;

export interface FrageVorlage {
  id?: string;
  text: string;
}

export interface TemplateLeafObject {
  fieldName: string;
  label?: string;
  antwortFormat?: "einzelfrage" | "mehrfachfrage_ein_antwortfeld";
  fragen?: FrageVorlage[];
}

export type TemplateLeaf = string | TemplateLeafObject;

export interface FieldIndexEntry {
  path: string;
  fieldName: string;
  label?: string;
  antwortFormat?: "einzelfrage" | "mehrfachfrage_ein_antwortfeld";
  fragen?: FrageVorlage[];
  isRich?: boolean;
}

/**
 * Flattened view of a template: PDF field name → metadata about the target path.
 */
export type FieldIndex = Record<string, FieldIndexEntry>;

// ─── Template helpers ─────────────────────────────────────────────────────────

function isTemplateLeafObject(value: unknown): value is TemplateLeafObject {
  return typeof value === "object" && value !== null && typeof (value as Record<string, unknown>).fieldName === "string";
}

/**
 * Recursively flattens the nested template produced by templatebuilder.
 */
export function flattenTemplate(
  node: Record<string, unknown>,
  prefix = "",
): FieldIndex {
  const index: FieldIndex = {};

  for (const [key, value] of Object.entries(node)) {
    const path = prefix ? `${prefix}.${key}` : key;

    if (typeof value === "string") {
      index[value] = {
        path,
        fieldName: value,
        antwortFormat: "einzelfrage",
        isRich: false,
      };
    } else if (isTemplateLeafObject(value)) {
      index[value.fieldName] = {
        path,
        fieldName: value.fieldName,
        label: value.label,
        antwortFormat: value.antwortFormat ?? "einzelfrage",
        fragen: value.fragen,
        isRich: true,
      };
    } else if (typeof value === "object" && value !== null) {
      Object.assign(index, flattenTemplate(value as Record<string, unknown>, path));
    }
  }

  return index;
}

// ─── PDF extraction ───────────────────────────────────────────────────────────

export async function extractFields(
  data: ArrayBuffer,
  options: { maxPages?: number } = {},
): Promise<FieldValues> {
  const pdf = await pdfjsLib.getDocument({ data }).promise;
  if (options.maxPages && pdf.numPages > options.maxPages) {
    throw new Error(`PDF has ${pdf.numPages} pages; the configured limit is ${options.maxPages}.`);
  }
  const values: FieldValues = {};

  for (let p = 1; p <= pdf.numPages; p++) {
    const page = await pdf.getPage(p);
    const annotations = await page.getAnnotations();

    for (const ann of annotations) {
      if (ann.subtype !== "Widget" || !ann.fieldName) continue;

      const raw = ann.fieldValue;
      if (raw === undefined || raw === null) continue;

      values[ann.fieldName] = Array.isArray(raw) ? raw.join(", ") : String(raw);
    }
  }

  return values;
}

// ─── Output builder ───────────────────────────────────────────────────────────

export function buildOutput(
  values: FieldValues,
  index: FieldIndex,
): Record<string, unknown> {
  const output: Record<string, unknown> = {};

  for (const entry of Object.values(index)) {
    const value = values[entry.fieldName];
    if (value === undefined) continue;

    if (!entry.isRich) {
      setNested(output, entry.path, value);
      continue;
    }

    const antwortFormat = entry.antwortFormat ?? "einzelfrage";
    const fragen = (entry.fragen ?? []).map((frage, i) => ({
      id: frage.id ?? `${entry.path.split(".").at(-1) ?? "frage"}_${i + 1}`,
      text: frage.text,
      ...(antwortFormat === "einzelfrage" ? { antwort: value } : {}),
    }));

    setNested(output, entry.path, {
      fieldName: entry.fieldName,
      label: entry.label,
      antwortFormat,
      zuordnungsmodus:
        antwortFormat === "mehrfachfrage_ein_antwortfeld"
          ? "gemeinsame_antwort"
          : "einzelfrage",
      fragen,
      antwort: value,
    });
  }

  return output;
}

export function getNested(obj: Record<string, unknown>, path: string): unknown {
  let cur: unknown = obj;
  for (const key of path.split(".")) {
    if (typeof cur !== "object" || cur === null) return undefined;
    cur = (cur as Record<string, unknown>)[key];
  }
  return cur;
}

// ─── Internal helpers ─────────────────────────────────────────────────────────

function setNested(obj: Record<string, unknown>, path: string, value: unknown): void {
  const parts = path.split(".");
  let cur = obj;
  for (let i = 0; i < parts.length - 1; i++) {
    const key = parts[i]!;
    if (typeof cur[key] !== "object" || cur[key] === null) cur[key] = {};
    cur = cur[key] as Record<string, unknown>;
  }
  cur[parts[parts.length - 1]!] = value;
}
