import { execFile as execFileCallback } from "node:child_process";
import { mkdtemp, readdir, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { promisify } from "node:util";
import { PDFDocument } from "pdf-lib";
import { createWorker } from "tesseract.js";
import { extractDocumentText } from "./document-reviews.js";
import { runJsonChatCompletion } from "./tenant-ai.js";

export type FormFieldAnswerMode = "default_value" | "source_hint" | "ask_user" | "leave_blank";
export type FormFieldSuggestionTarget = "source_hint" | "additional_instructions";

export type FormFieldQuestionConfig = {
  questionText: string;
  answerMode: FormFieldAnswerMode;
  defaultValue: string;
  sourceHint: string;
  additionalInstructions: string;
  clientDataStructureNodeId: string;
  clientDataStructureFieldKey: string;
  clientDataStructurePath: string;
};

export type ImportedFormField = {
  fieldKey: string;
  label: string;
  pdfFieldName: string;
  dataType: string;
  isRequired: boolean;
  sectionName: string | null;
  pageNumber: number | null;
  questionConfig: FormFieldQuestionConfig;
  instructions: string;
};

export type SuggestedFormFieldConfigurationValue = {
  target: FormFieldSuggestionTarget;
  value: string;
  reason: string;
  model: string;
  usage: {
    inputTokens: number;
    outputTokens: number;
    totalTokens: number;
  };
};

type CandidateField = {
  fieldKey: string;
  label: string;
  pdfFieldName: string;
  dataType: string;
  isRequired: boolean;
  sectionName: string | null;
  pageNumber: number | null;
};

const MAX_IMPORTED_FIELDS = 220;
const AI_BATCH_SIZE = 24;
const MAX_QUESTION_LENGTH = 500;
const MAX_SOURCE_HINT_LENGTH = 2000;
const MAX_DEFAULT_VALUE_LENGTH = 1000;
const MAX_ADDITIONAL_INSTRUCTIONS_LENGTH = 4000;
const MAX_CLIENT_STRUCTURE_NODE_ID_LENGTH = 64;
const MAX_CLIENT_STRUCTURE_FIELD_KEY_LENGTH = 100;
const MAX_CLIENT_STRUCTURE_PATH_LENGTH = 1000;
const OCR_CACHE_PATH = resolve(process.cwd(), "../../storage/tesseract-cache");
const execFile = promisify(execFileCallback);

function compactWhitespace(value: string | null | undefined) {
  return String(value ?? "")
    .replace(/\u0000/g, "")
    .replace(/\s+/g, " ")
    .trim();
}

function truncateText(value: string | null | undefined, maxLength: number) {
  const normalized = compactWhitespace(value);
  if (!normalized) {
    return "";
  }

  if (normalized.length <= maxLength) {
    return normalized;
  }

  return normalized.slice(0, Math.max(0, maxLength - 3)).trimEnd() + "...";
}

function normalizeIdentifier(value: string, maxLength = 100) {
  const normalized = compactWhitespace(value)
    .replace(/^form\d+\[\d+\]\./gi, "")
    .replace(/#subform\[\d+\]\./gi, "")
    .replace(/[^\p{L}\p{N}]+/gu, "_")
    .replace(/_+/g, "_")
    .replace(/^_+|_+$/g, "")
    .toLowerCase();

  if (!normalized) {
    return "";
  }

  return normalized.slice(0, maxLength);
}

function buildUniqueFieldKey(baseValue: string, usedKeys: Set<string>) {
  const baseKey = normalizeIdentifier(baseValue, 88) || "field";
  let nextKey = baseKey;
  let suffix = 2;

  while (usedKeys.has(nextKey)) {
    nextKey = `${baseKey.slice(0, Math.max(1, 88 - String(suffix).length - 1))}_${suffix}`;
    suffix += 1;
  }

  usedKeys.add(nextKey);
  return nextKey;
}

function humanizeFieldName(value: string) {
  const normalized = compactWhitespace(
    String(value ?? "")
      .replace(/^form\d+\[\d+\]\./gi, "")
      .replace(/#subform\[\d+\]\./gi, "")
      .replace(/([a-z])([A-Z])/g, "$1 $2")
      .replace(/[_[\].#]+/g, " ")
      .replace(/\bPt\b/gi, "Part")
      .replace(/\bCB\b/gi, "Checkbox")
      .replace(/\bDOB\b/g, "Date of birth")
      .replace(/\bZipCode\b/g, "ZIP code")
      .replace(/\bA_Number\b/g, "A-Number"),
  );

  if (!normalized) {
    return "";
  }

  return normalized.charAt(0).toUpperCase() + normalized.slice(1);
}

function inferDataTypeFromText(value: string) {
  const normalized = compactWhitespace(value).toLowerCase();

  if (
    /\byes\/no\b|\byes no\b|\bcheckbox\b|\bcheck box\b|\btrue\b|\bfalse\b/.test(normalized)
  ) {
    return "checkbox";
  }

  if (/\bdate\b|\bdob\b|mm\/dd\/yyyy/.test(normalized)) {
    return "date";
  }

  if (/\bselect\b|\bchoose\b|\bdropdown\b|\boption\b/.test(normalized)) {
    return "select";
  }

  if (/\bemail\b/.test(normalized)) {
    return "email";
  }

  if (/\bphone\b|\btelephone\b|\bmobile\b/.test(normalized)) {
    return "phone";
  }

  return "text";
}

function buildHeuristicQuestionText(field: Pick<CandidateField, "label" | "dataType">) {
  const label = compactWhitespace(field.label).replace(/[:.]+$/, "");

  if (!label) {
    return "What information is required for this field?";
  }

  if (label.endsWith("?")) {
    return truncateText(label, MAX_QUESTION_LENGTH);
  }

  if (field.dataType === "checkbox") {
    if (/\bhas\b|\bhave\b|\bis\b|\bare\b|\bwas\b|\bwere\b|\bdo\b|\bdoes\b|\bdid\b/i.test(label)) {
      return truncateText(label + "?", MAX_QUESTION_LENGTH);
    }

    return truncateText(`Does this apply: ${label}?`, MAX_QUESTION_LENGTH);
  }

  if (/^(family name|given name|middle name|full name|first name|last name)$/i.test(label)) {
    return truncateText(`What is the ${label.toLowerCase()}?`, MAX_QUESTION_LENGTH);
  }

  if (/\bdate of birth\b|\bdob\b/i.test(label)) {
    return "What is the date of birth?";
  }

  if (/\bdate\b/i.test(label)) {
    return truncateText(`What is the ${label.toLowerCase()}?`, MAX_QUESTION_LENGTH);
  }

  return truncateText(`What is the ${label.charAt(0).toLowerCase()}${label.slice(1)}?`, MAX_QUESTION_LENGTH);
}

function sanitizeQuestionConfig(value: Partial<FormFieldQuestionConfig> | null | undefined, fallbackLabel: string) {
  const answerMode =
    value?.answerMode === "default_value" ||
    value?.answerMode === "source_hint" ||
    value?.answerMode === "ask_user" ||
    value?.answerMode === "leave_blank"
      ? value.answerMode
      : "ask_user";

  const questionText = truncateText(
    value?.questionText || buildHeuristicQuestionText({ label: fallbackLabel, dataType: "text" }),
    MAX_QUESTION_LENGTH,
  );

  return {
    questionText,
    answerMode,
    defaultValue: truncateText(value?.defaultValue, MAX_DEFAULT_VALUE_LENGTH),
    sourceHint: truncateText(value?.sourceHint, MAX_SOURCE_HINT_LENGTH),
    additionalInstructions: truncateText(
      value?.additionalInstructions,
      MAX_ADDITIONAL_INSTRUCTIONS_LENGTH,
    ),
    clientDataStructureNodeId: truncateText(
      value?.clientDataStructureNodeId,
      MAX_CLIENT_STRUCTURE_NODE_ID_LENGTH,
    ),
    clientDataStructureFieldKey: truncateText(
      value?.clientDataStructureFieldKey,
      MAX_CLIENT_STRUCTURE_FIELD_KEY_LENGTH,
    ),
    clientDataStructurePath: truncateText(
      value?.clientDataStructurePath,
      MAX_CLIENT_STRUCTURE_PATH_LENGTH,
    ),
  } satisfies FormFieldQuestionConfig;
}

function isPlainObject(value: unknown): value is Record<string, unknown> {
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}

export function parseFormFieldMetadata(rawValue: unknown) {
  if (!rawValue) {
    return {};
  }

  if (typeof rawValue === "string") {
    try {
      const parsed = JSON.parse(rawValue);
      return isPlainObject(parsed) ? parsed : {};
    } catch {
      return {};
    }
  }

  return isPlainObject(rawValue) ? rawValue : {};
}

export function parseFormFieldQuestionConfig(input: {
  rawMetadata: unknown;
  fallbackLabel: string;
}) {
  const metadata = parseFormFieldMetadata(input.rawMetadata);
  const rawQuestionConfig = isPlainObject(metadata.questionConfig) ? metadata.questionConfig : null;

  return sanitizeQuestionConfig(
    rawQuestionConfig
      ? {
          questionText: typeof rawQuestionConfig.questionText === "string" ? rawQuestionConfig.questionText : "",
          answerMode:
            rawQuestionConfig.answerMode === "default_value" ||
            rawQuestionConfig.answerMode === "source_hint" ||
            rawQuestionConfig.answerMode === "ask_user" ||
            rawQuestionConfig.answerMode === "leave_blank"
              ? rawQuestionConfig.answerMode
              : undefined,
          defaultValue:
            typeof rawQuestionConfig.defaultValue === "string" ? rawQuestionConfig.defaultValue : "",
          sourceHint: typeof rawQuestionConfig.sourceHint === "string" ? rawQuestionConfig.sourceHint : "",
          additionalInstructions:
            typeof rawQuestionConfig.additionalInstructions === "string"
              ? rawQuestionConfig.additionalInstructions
              : "",
          clientDataStructureNodeId:
            typeof rawQuestionConfig.clientDataStructureNodeId === "string"
              ? rawQuestionConfig.clientDataStructureNodeId
              : "",
          clientDataStructureFieldKey:
            typeof rawQuestionConfig.clientDataStructureFieldKey === "string"
              ? rawQuestionConfig.clientDataStructureFieldKey
              : "",
          clientDataStructurePath:
            typeof rawQuestionConfig.clientDataStructurePath === "string"
              ? rawQuestionConfig.clientDataStructurePath
              : "",
        }
      : null,
    input.fallbackLabel,
  );
}

export function mergeFormFieldQuestionConfig(input: {
  rawMetadata: unknown;
  fallbackLabel: string;
  nextConfig: Partial<FormFieldQuestionConfig>;
}) {
  const metadata = parseFormFieldMetadata(input.rawMetadata);
  const currentConfig = parseFormFieldQuestionConfig({
    rawMetadata: input.rawMetadata,
    fallbackLabel: input.fallbackLabel,
  });
  const questionConfig = sanitizeQuestionConfig(
    {
      ...currentConfig,
      ...input.nextConfig,
    },
    input.fallbackLabel,
  );

  return {
    ...metadata,
    questionConfig,
  };
}

export function buildFormFieldInstructions(input: {
  label: string;
  dataType: string;
  sectionName: string | null;
  questionConfig: FormFieldQuestionConfig;
}) {
  const lines = [
    `Question to resolve this field: ${input.questionConfig.questionText}`,
    input.questionConfig.answerMode === "default_value" && input.questionConfig.defaultValue
      ? `Preferred answer strategy: use this default value when the client file does not already contain a confirmed answer: ${input.questionConfig.defaultValue}.`
      : null,
    input.questionConfig.answerMode === "source_hint" && input.questionConfig.sourceHint
      ? `Preferred answer strategy: search here first before asking the client: ${input.questionConfig.sourceHint}.`
      : null,
    input.questionConfig.answerMode === "ask_user"
      ? "Preferred answer strategy: if the answer is not already present in the confirmed client data or repository evidence, ask the user directly."
      : null,
    input.questionConfig.answerMode === "leave_blank"
      ? "Preferred answer strategy: leave this field blank by default unless confirmed client data or repository evidence explicitly supports a safe answer."
      : null,
    input.questionConfig.clientDataStructurePath
      ? `Preferred workspace source: Global client tree > ${input.questionConfig.clientDataStructurePath}${
          input.questionConfig.clientDataStructureFieldKey
            ? ` (field key: ${input.questionConfig.clientDataStructureFieldKey})`
            : ""
        }.`
      : null,
    input.questionConfig.additionalInstructions
      ? `Additional guidance: ${input.questionConfig.additionalInstructions}`
      : null,
    `Field label on the form: ${input.label}.`,
    input.sectionName ? `Form section: ${input.sectionName}.` : null,
    input.dataType ? `Field type: ${input.dataType}.` : null,
    "Never invent a value. Leave the field blank when the configured strategy still does not produce a safe answer.",
  ].filter(Boolean);

  return truncateText(lines.join(" "), MAX_ADDITIONAL_INSTRUCTIONS_LENGTH);
}

function createDefaultQuestionConfig(field: CandidateField) {
  return sanitizeQuestionConfig(
    {
      questionText: buildHeuristicQuestionText(field),
      answerMode: "ask_user",
      defaultValue: "",
      sourceHint: "",
      additionalInstructions: "",
      clientDataStructureNodeId: "",
      clientDataStructureFieldKey: "",
      clientDataStructurePath: "",
    },
    field.label,
  );
}

export async function suggestFormFieldConfigurationValue(input: {
  lawFirmId: string;
  target: FormFieldSuggestionTarget;
  formTemplate: {
    name: string;
    code: string;
    versionLabel: string;
    description: string | null;
  };
  field: {
    label: string;
    pdfFieldName: string;
    dataType: string;
    sectionName: string | null;
    pageNumber: number | null;
    questionConfig: FormFieldQuestionConfig;
  };
  nearbyFields: Array<{
    label: string;
    dataType: string;
    sectionName: string | null;
  }>;
}) {
  const completion = await runJsonChatCompletion({
    lawFirmId: input.lawFirmId,
    maxCompletionTokens: 900,
    systemPrompt: [
      "You suggest one configuration value for an AI legal form automation field.",
      'Return JSON only in the format {"value":"...","reason":"..."}.',
      "Use the form type and field context to produce a concise, practical suggestion.",
      'If target is "source_hint", tell the AI where it should look first in the client profile, repository, prior filings, or supporting evidence. Mention sources, not the final answer.',
      'If target is "additional_instructions", write operational guidance, evidence priority, blank-handling rules, checkbox/date mapping guidance, or edge cases. Do not repeat the source hint verbatim.',
      "Never invent client facts or claim that a document definitely exists.",
      "Do not mention OpenAI, prompts, or internal implementation details.",
      "Keep the suggestion compact and directly usable inside a form-field configuration UI.",
    ].join(" "),
    userPrompt: JSON.stringify(
      {
        target: input.target,
        formTemplate: input.formTemplate,
        field: {
          label: input.field.label,
          pdfFieldName: input.field.pdfFieldName,
          dataType: input.field.dataType,
          sectionName: input.field.sectionName,
          pageNumber: input.field.pageNumber,
          questionText: input.field.questionConfig.questionText,
          answerMode: input.field.questionConfig.answerMode,
          currentDefaultValue: input.field.questionConfig.defaultValue,
          currentSourceHint: input.field.questionConfig.sourceHint,
          currentAdditionalInstructions: input.field.questionConfig.additionalInstructions,
          currentClientDataStructureNodeId: input.field.questionConfig.clientDataStructureNodeId,
          currentClientDataStructureFieldKey: input.field.questionConfig.clientDataStructureFieldKey,
          currentClientDataStructurePath: input.field.questionConfig.clientDataStructurePath,
        },
        nearbyFields: input.nearbyFields.slice(0, 10),
        outputRules:
          input.target === "source_hint"
            ? "Return a short source-finding instruction, ideally 1-2 sentences."
            : "Return short operational guidance, ideally 1-3 sentences.",
      },
      null,
      2,
    ),
  });

  const maxLength =
    input.target === "source_hint"
      ? MAX_SOURCE_HINT_LENGTH
      : MAX_ADDITIONAL_INSTRUCTIONS_LENGTH;
  const value = truncateText(String(completion.json.value ?? ""), maxLength);
  const reason = truncateText(String(completion.json.reason ?? ""), 600);

  if (!value) {
    throw new Error("The AI did not return a usable suggestion.");
  }

  return {
    target: input.target,
    value,
    reason,
    model: completion.model,
    usage: completion.usage,
  } satisfies SuggestedFormFieldConfigurationValue;
}

async function extractAcroFormFields(bytes: Buffer) {
  try {
    const pdfDocument = await PDFDocument.load(bytes, {
      ignoreEncryption: true,
      updateMetadata: false,
    });
    const form = pdfDocument.getForm();
    const fields = form.getFields();
    const usedKeys = new Set<string>();
    const extractedFields: CandidateField[] = [];

    for (const field of fields) {
      const fieldName = compactWhitespace(field.getName?.() ?? "");
      const constructorName = field.constructor?.name ?? "PDFTextField";
      const baseLabel = humanizeFieldName(fieldName);
      const dataType =
        constructorName === "PDFCheckBox"
          ? "checkbox"
          : constructorName === "PDFDropdown" || constructorName === "PDFOptionList"
            ? "select"
            : constructorName === "PDFRadioGroup"
              ? "radio"
              : constructorName === "PDFButton"
                ? "button"
                : "text";

      if (!fieldName && !baseLabel) {
        continue;
      }

      extractedFields.push({
        fieldKey: buildUniqueFieldKey(fieldName || baseLabel, usedKeys),
        label: truncateText(baseLabel || fieldName, 255),
        pdfFieldName: truncateText(fieldName || baseLabel, 255),
        dataType,
        isRequired: false,
        sectionName: null,
        pageNumber: null,
      });

      if (extractedFields.length >= MAX_IMPORTED_FIELDS) {
        break;
      }
    }

    return extractedFields;
  } catch {
    return [];
  }
}

function looksLikeSectionHeader(line: string) {
  return /^(part|section|page)\b/i.test(line) || /^general instructions\b/i.test(line);
}

function looksLikeFieldPrompt(line: string) {
  const normalized = compactWhitespace(line);
  if (!normalized || normalized.length < 3 || normalized.length > 180) {
    return false;
  }

  if (looksLikeSectionHeader(normalized)) {
    return false;
  }

  if (/^form\s+[a-z0-9-]+/i.test(normalized) || /^department of /i.test(normalized)) {
    return false;
  }

  return (
    normalized.endsWith("?") ||
    normalized.endsWith(":") ||
    /\byes\/no\b|\byes no\b|\bdate of birth\b|\bfamily name\b|\bgiven name\b|\bmiddle name\b|\baddress\b|\bphone\b|\bemail\b|\bcountry\b|\bcity\b|\bstate\b|\bzip\b/i.test(
      normalized,
    )
  );
}

function extractCandidateFieldsFromDocumentText(extractedText: string) {
  const lines = String(extractedText ?? "")
    .split(/\r?\n/)
    .map((line) => compactWhitespace(line))
    .filter(Boolean);
  const usedKeys = new Set<string>();
  const seenLabels = new Set<string>();
  const fields: CandidateField[] = [];
  let currentSection: string | null = null;

  for (const line of lines) {
    if (looksLikeSectionHeader(line)) {
      currentSection = truncateText(line, 100) || currentSection;
      continue;
    }

    if (!looksLikeFieldPrompt(line)) {
      continue;
    }

    const normalizedLabel = line.toLowerCase();
    if (seenLabels.has(normalizedLabel)) {
      continue;
    }
    seenLabels.add(normalizedLabel);

    const baseLabel = line.replace(/[:?]+$/, "");
    const fieldKey = buildUniqueFieldKey(baseLabel, usedKeys);
    fields.push({
      fieldKey,
      label: truncateText(baseLabel, 255),
      pdfFieldName: truncateText(`ocr_${fieldKey}`, 255),
      dataType: inferDataTypeFromText(baseLabel),
      isRequired: false,
      sectionName: currentSection,
      pageNumber: null,
    });

    if (fields.length >= MAX_IMPORTED_FIELDS) {
      break;
    }
  }

  return fields;
}

async function extractPdfTextWithOcrFallback(bytes: Buffer) {
  const tempDirectory = await mkdtemp(join(tmpdir(), "neurav2-form-ocr-"));

  try {
    const pdfPath = join(tempDirectory, "input.pdf");
    const rasterPrefix = join(tempDirectory, "page");
    await writeFile(pdfPath, bytes);

    try {
      await execFile("pdftoppm", ["-png", "-r", "160", pdfPath, rasterPrefix]);
    } catch {
      return "";
    }

    const imageFiles = (await readdir(tempDirectory))
      .filter((fileName) => /^page-\d+\.png$/i.test(fileName))
      .sort((left, right) => left.localeCompare(right, undefined, { numeric: true }));

    if (!imageFiles.length) {
      return "";
    }

    const worker = await createWorker("eng", 1, {
      cachePath: OCR_CACHE_PATH,
      logger: () => undefined,
    });

    try {
      const textParts: string[] = [];

      for (const imageFile of imageFiles.slice(0, 25)) {
        const imageBytes = await readFile(join(tempDirectory, imageFile));
        const result = await worker.recognize(imageBytes);
        const text = compactWhitespace(result.data.text ?? "");

        if (text) {
          textParts.push(text);
        }
      }

      return textParts.join("\n");
    } finally {
      await worker.terminate().catch(() => undefined);
    }
  } finally {
    await rm(tempDirectory, { recursive: true, force: true }).catch(() => undefined);
  }
}

async function refineFieldQuestionsWithAi(input: {
  lawFirmId: string;
  formName: string;
  extractedText: string;
  fields: CandidateField[];
}) {
  const updates = new Map<string, Partial<FormFieldQuestionConfig>>();
  const extractedTextExcerpt = truncateText(input.extractedText, 10_000);

  for (let index = 0; index < input.fields.length; index += AI_BATCH_SIZE) {
    const batch = input.fields.slice(index, index + AI_BATCH_SIZE);

    try {
      const response = await runJsonChatCompletion({
        lawFirmId: input.lawFirmId,
        maxCompletionTokens: 1800,
        systemPrompt: [
          "You convert legal PDF form fields into client-facing intake questions.",
          "Return JSON only in the format {\"fields\":[{\"fieldKey\":\"...\",\"questionText\":\"...\",\"additionalInstructions\":\"...\"}]}.",
          "Use concise, plain English questions.",
          "Do not invent legal advice or factual answers.",
          "If the field label is technical, rewrite it into a question a case manager can understand.",
          "additionalInstructions should be short and optional.",
        ].join(" "),
        userPrompt: JSON.stringify({
          formName: input.formName,
          extractedTextExcerpt,
          fields: batch.map((field) => ({
            fieldKey: field.fieldKey,
            label: field.label,
            pdfFieldName: field.pdfFieldName,
            dataType: field.dataType,
            sectionName: field.sectionName,
          })),
        }),
      });

      const payloadFields = Array.isArray(response.json.fields) ? response.json.fields : [];
      for (const rawField of payloadFields) {
        if (!isPlainObject(rawField) || typeof rawField.fieldKey !== "string") {
          continue;
        }

        updates.set(rawField.fieldKey, {
          questionText:
            typeof rawField.questionText === "string" ? rawField.questionText : "",
          additionalInstructions:
            typeof rawField.additionalInstructions === "string"
              ? rawField.additionalInstructions
              : "",
        });
      }
    } catch {
      // Heuristic fallback is applied automatically below.
    }
  }

  return updates;
}

export async function analyzeImportedFormTemplate(input: {
  lawFirmId: string;
  fileName: string;
  formName: string;
  bytes: Buffer;
}) {
  const extractedText = await extractDocumentText({
    fileName: input.fileName,
    mimeType: "application/pdf",
    bytes: input.bytes,
  });
  const ocrExtractedText =
    extractedText.length >= 120 ? "" : await extractPdfTextWithOcrFallback(input.bytes);
  const resolvedExtractedText =
    ocrExtractedText.length > extractedText.length ? ocrExtractedText : extractedText;
  const acroFields = await extractAcroFormFields(input.bytes);
  const candidateFields: CandidateField[] = acroFields.length
    ? acroFields
    : extractCandidateFieldsFromDocumentText(resolvedExtractedText);

  if (!candidateFields.length) {
    throw new Error("The uploaded PDF could not be analyzed into editable form fields.");
  }

  const aiUpdates = await refineFieldQuestionsWithAi({
    lawFirmId: input.lawFirmId,
    formName: input.formName,
    extractedText: resolvedExtractedText,
    fields: candidateFields,
  });

  const fields = candidateFields.map((field) => {
    const questionConfig = sanitizeQuestionConfig(
      {
        ...createDefaultQuestionConfig(field),
        ...aiUpdates.get(field.fieldKey),
      },
      field.label,
    );

    return {
      ...field,
      questionConfig,
      instructions: buildFormFieldInstructions({
        label: field.label,
        dataType: field.dataType,
        sectionName: field.sectionName,
        questionConfig,
      }),
    } satisfies ImportedFormField;
  });

  return {
    extractedText: resolvedExtractedText,
    fields,
  };
}
