import { constants } from "node:fs";
import { access, readFile } from "node:fs/promises";
import { resolve } from "node:path";
import { generateClientFormFromTemplate } from "./intelligence.js";
import { createId } from "./id.js";
import { prisma } from "./prisma.js";
import { createRepositoryItem } from "./repository.js";
import { saveBinaryFile } from "./storage.js";

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

type SpecializedFormDefinition = {
  agentCode: string;
  code: string;
  name: string;
  edition: string;
  versionLabel: string;
  description: string;
  assetFileName: string;
  fieldsManifestFileName: string;
  defaultPrompt: string;
};

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

function createFallbackFieldLabel(fieldKey: string) {
  return compactWhitespace(
    fieldKey
      .replace(/^form\d+\[\d+\]\./i, "")
      .replace(/[#.\[\]_]+/g, " ")
      .replace(/\s+/g, " "),
  ).slice(0, 255);
}

function normalizeBundledFieldLabel(field: SpecializedBundledField) {
  const compactLabel = compactWhitespace(field.label);

  if (!compactLabel) {
    return createFallbackFieldLabel(field.fieldKey);
  }

  if (compactLabel.length <= 255) {
    return compactLabel;
  }

  const sentences = compactLabel
    .split(/(?<=[.!?])\s+/)
    .map((item) => item.trim())
    .filter(Boolean);
  const shortSentence = [...sentences].reverse().find((item) => item.length <= 255);

  if (shortSentence) {
    return shortSentence;
  }

  const trailingInstruction = compactLabel.match(/(Enter|Select|Provide)\b.*$/i)?.[0]?.trim();
  if (trailingInstruction && trailingInstruction.length <= 255) {
    return trailingInstruction;
  }

  const fallbackLabel = createFallbackFieldLabel(field.fieldKey);
  if (fallbackLabel) {
    return fallbackLabel;
  }

  return `${compactLabel.slice(0, 252)}...`;
}

function normalizeBundledSectionName(value: string | null | undefined) {
  const compactValue = compactWhitespace(String(value ?? ""));
  if (!compactValue) {
    return null;
  }

  if (compactValue.length <= 100) {
    return compactValue;
  }

  return `${compactValue.slice(0, 97)}...`;
}

function limitInstructionLength(value: string | null | undefined) {
  const normalized = compactWhitespace(String(value ?? ""));

  if (!normalized) {
    return "";
  }

  return normalized.length > 4000 ? `${normalized.slice(0, 3997).trimEnd()}...` : normalized;
}

function buildI485FieldSpecificInstructions(field: SpecializedBundledField) {
  const pdfFieldName = String(field.pdfFieldName ?? "");
  const normalizedLabel = compactWhitespace(field.label).toLowerCase();
  const instructions: string[] = [];

  if (
    /\b(attorney|accredited representative|volag|interpreter|preparer|signature|uscis officer)\b/i.test(
      normalizedLabel,
    )
  ) {
    instructions.push(
      "Keep this field blank unless there is explicit, current evidence for the named attorney, preparer, interpreter, or USCIS-only section.",
    );
  }

  if (/Pt1Line2(?:a)?_(FamilyName|GivenName|MiddleName)/.test(pdfFieldName)) {
    instructions.push(
      "Use this only for actual other legal names used since birth. Never repeat the applicant's current legal name here.",
    );
  }

  if (/Pt1Line3[AB]_OtherDOB|Pt1Line3A_OtherDOB|Pt1Line3B_OtherDOB/i.test(pdfFieldName)) {
    instructions.push(
      "Fill only if there is explicit evidence that the applicant used another date of birth. Leave blank when no alternate DOB is documented.",
    );
  }

  if (/Pt1Line10_(PassportNum|ExpDate|Passport|CityTown|DateofArrival)/.test(pdfFieldName)) {
    instructions.push(
      "Prefer the passport, visa, and arrival details tied to the applicant's last physical entry to the United States, not merely the most recently renewed passport.",
    );
  }

  if (/Pt2Line11_CB\[\d+\]|Pt1Line11_Admitted/.test(pdfFieldName)) {
    instructions.push(
      "This arrival-status group is easy to misread in the PDF. Confirm whether the applicant was admitted, paroled, other, or never inspected from I-94 / entry evidence before filling.",
    );
  }

  if (/Pt3Line1_CB\[4\]/.test(pdfFieldName)) {
    instructions.push(
      "For employment-based adjustment, this commonly corresponds to the I-864 exemption path. Check it only when the filing basis clearly supports the non-I-864 exemption.",
    );
  }

  if (/Pt5Line5_CityTownOfBirth|Pt5Line10_CityTownOfBirth/.test(pdfFieldName)) {
    instructions.push(
      "Despite the field name, this PDF slot is often used for the parent's country of birth in this form edition. Prefer the authoritative country value when the source only states country.",
    );
  }

  if (/Pt6Line1_MaritalStatus\[\d+\]/.test(pdfFieldName)) {
    instructions.push(
      "Use the edition-specific checkbox indices carefully: [0]=Divorced, [1]=Single, [2]=Widowed, [3]=Married, [4]=Marriage Annulled, [5]=Legally Separated.",
    );
  }

  if (/Pt6Line11_|Pt6Line12_|Pt6Line13_|Pt6Line18_|Pt6Line19_/.test(pdfFieldName)) {
    instructions.push(
      "These lines often refer to prior-marriage history, not the current spouse. Do not copy current-spouse values here unless the prompt and evidence clearly indicate a prior marriage section.",
    );
  }

  if (/Pt7Line4_Weight[123]/.test(pdfFieldName)) {
    instructions.push(
      "This weight is split across separate digit boxes. Only fill when the full weight in pounds is known, and map each digit to its own box.",
    );
  }

  if (/Pt8Line.*YesNo|Pt9Line.*YesNo|(^|[.])a_YesNo|(^|[.])b_YesNo/.test(pdfFieldName)) {
    instructions.push(
      "Do not default this yes/no answer to 'No' just because the evidence is silent. Fill only from direct questionnaire answers, authoritative records, or explicit attorney-reviewed guidance.",
    );
  }

  return instructions.length ? instructions.join(" ") : null;
}

function buildDefaultSpecializedFieldInstructions(
  agentCode: string,
  field: SpecializedBundledField,
) {
  const normalizedLabel = compactWhitespace(field.label);
  const normalizedSectionName = normalizeBundledSectionName(field.sectionName);
  const instructions = [
    normalizedLabel
      ? `Form question or on-page guidance: ${normalizedLabel}`
      : `Fill the field "${field.fieldKey}" only when the evidence clearly supports the answer.`,
    normalizedSectionName ? `I-485 section context: ${normalizedSectionName}.` : null,
    "Use only evidence-supported values. Leave blank when the evidence is missing, conflicting, or ambiguous.",
    "Use this space to explain how the field should be filled and which documents or repository records should be checked first.",
    agentCode === "uscis_i_485" ? buildI485FieldSpecificInstructions(field) : null,
  ].filter(Boolean);

  return limitInstructionLength(instructions.join(" "));
}

export type SpecializedFormSummary = {
  agentCode: string;
  templateId: string;
  code: string;
  name: string;
  edition: string | null;
  versionLabel: string;
  description: string | null;
  basePdfFileId: string | null;
  basePdfOriginalFileName: string | null;
  fieldCount: number;
  prompt: string;
  defaultPrompt: string;
  usesDefaultPrompt: boolean;
  updatedAt: string;
};

export type SpecializedFormFieldGuidance = {
  id: string;
  templateId: string;
  agentCode: string;
  fieldKey: string;
  label: string;
  pdfFieldName: string;
  dataType: string;
  isRequired: boolean;
  sectionName: string | null;
  pageNumber: number | null;
  instructions: string;
  defaultInstructions: string;
};

export type SpecializedFormGenerationResult = {
  agentCode: string;
  templateId: string;
  formName: string;
  clientId: string;
  generatedDocumentId: string;
  repositoryItemId: string;
  fileId: string;
  fileName: string;
  filledCount: number;
  unresolvedCount: number;
  unresolvedFields: string[];
};

export type PreparedSpecializedFormGeneration = {
  agentCode: string;
  templateId: string;
  formName: string;
  prompt: string;
};

const I485_DEFAULT_PROMPT = [
  "You are a specialist legal form-filling agent for USCIS Form I-485, Application to Register Permanent Residence or Adjust Status.",
  "Fill only the principal applicant fields and any other fields that are explicitly supported by the evidence available for this client.",
  "Search the entire client record: client profile, structured facts, all client repository items, all client documents across all cases, and extracted document text.",
  "If a legacy employment-based adjustment export is present, treat case_data.json as canonical structured facts, BUILD_REPORT.json as gap context, and docs_manifest.json as a supporting-document index.",
  "Never invent facts. If a value is missing, contradictory, or not grounded in the evidence, leave that field unanswered.",
  "Dates must use MM/DD/YYYY when the evidence supports a date.",
  "Checkbox answers must use true or false.",
  "Dropdown selections must use the exact option value when it is evident from the evidence, such as a two-letter U.S. state code.",
  "Do not fill attorney, interpreter, preparer, signature, barcode, or certification fields unless the evidence explicitly supports them.",
  "For employment-based adjustment, distinguish carefully between current status, class at last entry, I-140 basis, I-864 exemption logic, and current-vs-prior spouse sections.",
  "Do not copy principal applicant values into spouse, parent, interpreter, attorney, or dependent sections unless the evidence explicitly identifies that related person.",
  "Prefer the most recent confirmed client data when multiple sources are present.",
].join("\n");

const SPECIALIZED_FORM_DEFINITIONS: SpecializedFormDefinition[] = [
  {
    agentCode: "uscis_i_485",
    code: "uscis_i_485",
    name: "Form I-485",
    edition: "01/20/25",
    versionLabel: "2025-01-20",
    description:
      "USCIS Form I-485 filled by a specialized AI agent using the complete client repository.",
    assetFileName: "i-485.pdf",
    fieldsManifestFileName: "i-485.fields.json",
    defaultPrompt: I485_DEFAULT_PROMPT,
  },
];

const bundledFormCache = new Map<
  string,
  { pdfBytes: Buffer; fields: SpecializedBundledField[] }
>();

function getSpecializedFormDefinition(agentCode: string) {
  const normalizedAgentCode = String(agentCode ?? "").trim();
  return (
    SPECIALIZED_FORM_DEFINITIONS.find((item) => item.agentCode === normalizedAgentCode) ?? null
  );
}

async function resolveApiAssetPath(...segments: string[]) {
  const candidates = [
    resolve(process.cwd(), ...segments),
    resolve(process.cwd(), "apps", "api", ...segments),
    resolve(process.cwd(), "..", ...segments),
    resolve(process.cwd(), "..", "..", "apps", "api", ...segments),
  ];

  for (const candidate of candidates) {
    try {
      await access(candidate, constants.F_OK);
      return candidate;
    } catch {
      continue;
    }
  }

  throw new Error(`Unable to resolve bundled asset path for ${segments.join("/")}`);
}

async function loadBundledSpecializedForm(definition: SpecializedFormDefinition) {
  const cacheKey = definition.agentCode;
  const cached = bundledFormCache.get(cacheKey);
  if (cached) {
    return cached;
  }

  const pdfPath = await resolveApiAssetPath("assets", "forms", definition.assetFileName);
  const manifestPath = await resolveApiAssetPath(
    "assets",
    "forms",
    definition.fieldsManifestFileName,
  );

  const pdfBytes = await readFile(pdfPath);
  const parsedManifest = JSON.parse(await readFile(manifestPath, "utf8")) as {
    fields?: SpecializedBundledField[];
  };
  const fields = Array.isArray(parsedManifest.fields) ? parsedManifest.fields : [];

  const loaded = {
    pdfBytes,
    fields: fields
      .map((field) => ({
        fieldKey: String(field.fieldKey ?? "").trim(),
        label: compactWhitespace(String(field.label ?? "")),
        pdfFieldName: String(field.pdfFieldName ?? "").trim(),
        dataType: String(field.dataType ?? "text").trim() || "text",
        isRequired: Boolean(field.isRequired),
        sectionName: normalizeBundledSectionName(field.sectionName),
        pageNumber:
          Number.isFinite(Number(field.pageNumber)) && Number(field.pageNumber) > 0 ?
            Number(field.pageNumber) :
            null,
      }))
      .filter((field) => field.fieldKey && field.label && field.pdfFieldName),
  };

  bundledFormCache.set(cacheKey, loaded);
  return loaded;
}

async function resolveSpecializedFormTemplate(input: {
  lawFirmId: string;
  actorUserId: string;
  agentCode: string;
}) {
  const definition = getSpecializedFormDefinition(input.agentCode);
  if (!definition) {
    throw new Error("Specialized form not found");
  }

  const templateId = await ensureSpecializedFormTemplate({
    lawFirmId: input.lawFirmId,
    actorUserId: input.actorUserId,
    definition,
  });
  const bundled = await loadBundledSpecializedForm(definition);

  return {
    definition,
    templateId,
    bundledFields: bundled.fields,
  };
}

async function createBundledBasePdfFile(input: {
  lawFirmId: string;
  actorUserId: string;
  templateId: string;
  definition: SpecializedFormDefinition;
  pdfBytes: Buffer;
}) {
  const stored = await saveBinaryFile({
    lawFirmId: input.lawFirmId,
    caseId: null,
    fileName: input.definition.assetFileName,
    bytes: input.pdfBytes,
    kind: "uploads",
  });

  const repositoryItemId = await createRepositoryItem({
    lawFirmId: input.lawFirmId,
    itemTypeCode: "document",
    sourceEntityType: "form_template",
    sourceEntityId: input.templateId,
    subject: `${input.definition.name} base PDF`,
    createdByUserId: input.actorUserId,
  });

  const fileId = createId();
  await prisma.$executeRaw`
    INSERT INTO files (
      id, law_firm_id, client_id, case_id, repository_item_id, storage_provider, storage_bucket,
      object_key, storage_region, original_file_name, stored_file_name, mime_type, size_bytes,
      checksum_sha256, is_encrypted, uploaded_by_user_id, uploaded_at, created_at
    ) VALUES (
      ${fileId},
      ${input.lawFirmId},
      NULL,
      NULL,
      ${repositoryItemId},
      'local_dev',
      'workspace',
      ${stored.relativeObjectKey},
      'local',
      ${input.definition.assetFileName},
      ${stored.storedFileName},
      'application/pdf',
      ${input.pdfBytes.length},
      ${stored.checksumSha256},
      0,
      ${input.actorUserId},
      NOW(),
      CURRENT_TIMESTAMP
    )
  `;

  return fileId;
}

async function syncSpecializedFormFields(input: {
  templateId: string;
  fields: SpecializedBundledField[];
}) {
  const existingFields = await prisma.$queryRaw<
    Array<{
      id: string;
      field_key: string;
      pdf_field_name: string;
    }>
  >`
    SELECT id, field_key, pdf_field_name
    FROM form_fields
    WHERE form_template_id = ${input.templateId}
  `;

  const existingFieldByPdfName = new Map(
    existingFields.map((field) => [field.pdf_field_name, field]),
  );
  const existingFieldByKey = new Map(existingFields.map((field) => [field.field_key, field]));

  for (const field of input.fields) {
    const normalizedLabel = normalizeBundledFieldLabel(field);
    const defaultInstructions =
      buildDefaultSpecializedFieldInstructions("uscis_i_485", field) || null;
    const existingField =
      existingFieldByPdfName.get(field.pdfFieldName) ?? existingFieldByKey.get(field.fieldKey) ?? null;

    if (existingField) {
      await prisma.$executeRaw`
        UPDATE form_fields
        SET
          section_name = ${field.sectionName ?? null},
          page_number = ${field.pageNumber ?? null},
          field_key = ${field.fieldKey},
          label = ${normalizedLabel},
          pdf_field_name = ${field.pdfFieldName},
          data_type = ${field.dataType},
          is_required = ${field.isRequired ? 1 : 0},
          is_repeatable = 0,
          instructions = COALESCE(NULLIF(instructions, ''), ${defaultInstructions}),
          updated_at = CURRENT_TIMESTAMP
        WHERE id = ${existingField.id}
      `;
      continue;
    }

    await prisma.$executeRaw`
      INSERT INTO form_fields (
        id, form_template_id, section_name, page_number, field_key, label, pdf_field_name,
        data_type, is_required, is_repeatable, instructions, created_at, updated_at
      ) VALUES (
        ${createId()},
        ${input.templateId},
        ${field.sectionName ?? null},
        ${field.pageNumber ?? null},
        ${field.fieldKey},
        ${normalizedLabel},
        ${field.pdfFieldName},
        ${field.dataType},
        ${field.isRequired ? 1 : 0},
        0,
        ${defaultInstructions},
        CURRENT_TIMESTAMP,
        CURRENT_TIMESTAMP
      )
    `;
  }
}

async function ensureSpecializedFormTemplate(input: {
  lawFirmId: string;
  actorUserId: string;
  definition: SpecializedFormDefinition;
}) {
  const bundled = await loadBundledSpecializedForm(input.definition);

  const [existingTemplate] = await prisma.$queryRaw<
    Array<{
      id: string;
      base_pdf_file_id: string | null;
      agent_prompt: string | null;
    }>
  >`
    SELECT id, base_pdf_file_id, agent_prompt
    FROM form_templates
    WHERE law_firm_id = ${input.lawFirmId}
      AND code = ${input.definition.code}
      AND version_label = ${input.definition.versionLabel}
    LIMIT 1
  `;

  let templateId = existingTemplate?.id ?? null;

  if (!templateId) {
    templateId = createId();
    await prisma.$executeRaw`
      INSERT INTO form_templates (
        id, law_firm_id, code, name, edition, jurisdiction_country_code, version_label,
        base_pdf_file_id, description, agent_code, agent_prompt, is_active, is_system_template,
        created_at, updated_at
      ) VALUES (
        ${templateId},
        ${input.lawFirmId},
        ${input.definition.code},
        ${input.definition.name},
        ${input.definition.edition},
        'US',
        ${input.definition.versionLabel},
        NULL,
        ${input.definition.description},
        ${input.definition.agentCode},
        ${input.definition.defaultPrompt},
        1,
        0,
        CURRENT_TIMESTAMP,
        CURRENT_TIMESTAMP
      )
    `;
  } else {
    await prisma.$executeRaw`
      UPDATE form_templates
      SET
        name = ${input.definition.name},
        edition = ${input.definition.edition},
        jurisdiction_country_code = 'US',
        description = ${input.definition.description},
        agent_code = ${input.definition.agentCode},
        agent_prompt = COALESCE(NULLIF(agent_prompt, ''), ${input.definition.defaultPrompt}),
        updated_at = CURRENT_TIMESTAMP
      WHERE id = ${templateId}
    `;
  }

  const currentBasePdfFileId =
    existingTemplate?.base_pdf_file_id ??
    (
      await prisma.$queryRaw<Array<{ base_pdf_file_id: string | null }>>`
        SELECT base_pdf_file_id
        FROM form_templates
        WHERE id = ${templateId}
        LIMIT 1
      `
    )[0]?.base_pdf_file_id ??
    null;

  if (!currentBasePdfFileId) {
    const basePdfFileId = await createBundledBasePdfFile({
      lawFirmId: input.lawFirmId,
      actorUserId: input.actorUserId,
      templateId,
      definition: input.definition,
      pdfBytes: bundled.pdfBytes,
    });

    await prisma.$executeRaw`
      UPDATE form_templates
      SET base_pdf_file_id = ${basePdfFileId}, updated_at = CURRENT_TIMESTAMP
      WHERE id = ${templateId}
    `;
  }

  await syncSpecializedFormFields({
    templateId,
    fields: bundled.fields,
  });

  return templateId;
}

async function getSpecializedFormSummary(input: {
  lawFirmId: string;
  actorUserId: string;
  definition: SpecializedFormDefinition;
}): Promise<SpecializedFormSummary> {
  const templateId = await ensureSpecializedFormTemplate(input);

  const [row] = await prisma.$queryRaw<
    Array<{
      id: string;
      code: string;
      name: string;
      edition: string | null;
      version_label: string;
      description: string | null;
      base_pdf_file_id: string | null;
      base_pdf_original_file_name: string | null;
      updated_at: Date;
      agent_prompt: string | null;
      field_count: bigint | number | string;
    }>
  >`
    SELECT
      ft.id,
      ft.code,
      ft.name,
      ft.edition,
      ft.version_label,
      ft.description,
      ft.base_pdf_file_id,
      f.original_file_name AS base_pdf_original_file_name,
      ft.updated_at,
      ft.agent_prompt,
      CAST(COUNT(ff.id) AS SIGNED) AS field_count
    FROM form_templates ft
    LEFT JOIN files f ON f.id = ft.base_pdf_file_id
    LEFT JOIN form_fields ff ON ff.form_template_id = ft.id
    WHERE ft.id = ${templateId}
    GROUP BY
      ft.id,
      ft.code,
      ft.name,
      ft.edition,
      ft.version_label,
      ft.description,
      ft.base_pdf_file_id,
      f.original_file_name,
      ft.updated_at,
      ft.agent_prompt
    LIMIT 1
  `;

  if (!row) {
    throw new Error("Specialized form template could not be loaded");
  }

  const storedPrompt = String(row.agent_prompt ?? "").trim();
  return {
    agentCode: input.definition.agentCode,
    templateId: row.id,
    code: row.code,
    name: row.name,
    edition: row.edition,
    versionLabel: row.version_label,
    description: row.description,
    basePdfFileId: row.base_pdf_file_id,
    basePdfOriginalFileName: row.base_pdf_original_file_name,
    fieldCount: Number(row.field_count ?? 0),
    prompt: storedPrompt || input.definition.defaultPrompt,
    defaultPrompt: input.definition.defaultPrompt,
    usesDefaultPrompt: !storedPrompt,
    updatedAt: new Date(row.updated_at).toISOString(),
  };
}

export async function listSpecializedForms(input: {
  lawFirmId: string;
  actorUserId: string;
}) {
  const summaries: SpecializedFormSummary[] = [];

  for (const definition of SPECIALIZED_FORM_DEFINITIONS) {
    summaries.push(
      await getSpecializedFormSummary({
        lawFirmId: input.lawFirmId,
        actorUserId: input.actorUserId,
        definition,
      }),
    );
  }

  return summaries;
}

export async function updateSpecializedFormPrompt(input: {
  lawFirmId: string;
  actorUserId: string;
  agentCode: string;
  prompt: string;
}) {
  const definition = getSpecializedFormDefinition(input.agentCode);
  if (!definition) {
    throw new Error("Specialized form not found");
  }

  const templateId = await ensureSpecializedFormTemplate({
    lawFirmId: input.lawFirmId,
    actorUserId: input.actorUserId,
    definition,
  });
  const normalizedPrompt = String(input.prompt ?? "").trim();

  await prisma.$executeRaw`
    UPDATE form_templates
    SET
      agent_prompt = ${normalizedPrompt || null},
      updated_at = CURRENT_TIMESTAMP
    WHERE id = ${templateId}
  `;

  return getSpecializedFormSummary({
    lawFirmId: input.lawFirmId,
    actorUserId: input.actorUserId,
    definition,
  });
}

export async function listSpecializedFormFieldGuidance(input: {
  lawFirmId: string;
  actorUserId: string;
  agentCode: string;
}) {
  const resolved = await resolveSpecializedFormTemplate(input);
  const bundledFieldByKey = new Map(
    resolved.bundledFields.map((field) => [field.fieldKey, field]),
  );
  const bundledFieldByPdfName = new Map(
    resolved.bundledFields.map((field) => [field.pdfFieldName, field]),
  );

  const rows = await prisma.$queryRaw<
    Array<{
      id: string;
      field_key: string;
      label: string;
      pdf_field_name: string;
      data_type: string;
      is_required: number;
      section_name: string | null;
      page_number: number | null;
      instructions: string | null;
    }>
  >`
    SELECT
      id,
      field_key,
      label,
      pdf_field_name,
      data_type,
      is_required,
      section_name,
      page_number,
      instructions
    FROM form_fields
    WHERE form_template_id = ${resolved.templateId}
    ORDER BY page_number ASC, created_at ASC
  `;

  return rows.map((row) => {
    const bundledField =
      bundledFieldByKey.get(row.field_key) ?? bundledFieldByPdfName.get(row.pdf_field_name) ?? null;
    const defaultInstructions = bundledField
      ? buildDefaultSpecializedFieldInstructions(resolved.definition.agentCode, bundledField)
      : `Fill the field "${row.label}" only when the evidence clearly supports the answer.`;
    const instructions = limitInstructionLength(row.instructions) || defaultInstructions;

    return {
      id: row.id,
      templateId: resolved.templateId,
      agentCode: resolved.definition.agentCode,
      fieldKey: row.field_key,
      label: row.label,
      pdfFieldName: row.pdf_field_name,
      dataType: row.data_type,
      isRequired: Boolean(row.is_required),
      sectionName: row.section_name,
      pageNumber: row.page_number,
      instructions,
      defaultInstructions,
    } satisfies SpecializedFormFieldGuidance;
  });
}

export async function updateSpecializedFormFieldGuidance(input: {
  lawFirmId: string;
  actorUserId: string;
  agentCode: string;
  fieldId: string;
  instructions: string;
}) {
  const resolved = await resolveSpecializedFormTemplate(input);
  const normalizedInstructions = limitInstructionLength(input.instructions);

  const updatedCount = await prisma.$executeRaw`
    UPDATE form_fields
    SET
      instructions = ${normalizedInstructions || null},
      updated_at = CURRENT_TIMESTAMP
    WHERE id = ${input.fieldId}
      AND form_template_id = ${resolved.templateId}
  `;

  if (!Number(updatedCount)) {
    throw new Error("Specialized form field not found");
  }

  const fields = await listSpecializedFormFieldGuidance({
    lawFirmId: input.lawFirmId,
    actorUserId: input.actorUserId,
    agentCode: input.agentCode,
  });

  return fields.find((field) => field.id === input.fieldId) ?? null;
}

export async function prepareSpecializedFormGeneration(input: {
  lawFirmId: string;
  actorUserId: string;
  agentCode: string;
}): Promise<PreparedSpecializedFormGeneration> {
  const definition = getSpecializedFormDefinition(input.agentCode);
  if (!definition) {
    throw new Error("Specialized form not found");
  }

  const summary = await getSpecializedFormSummary({
    lawFirmId: input.lawFirmId,
    actorUserId: input.actorUserId,
    definition,
  });

  return {
    agentCode: definition.agentCode,
    templateId: summary.templateId,
    formName: summary.name,
    prompt: summary.prompt,
  };
}

export async function generateSpecializedFormForClient(input: {
  lawFirmId: string;
  actorUserId: string;
  clientId: string;
  agentCode: string;
  onProgress?: ((update: {
    stageCode: string;
    progressPercent: number;
    detailText?: string | null;
  }) => Promise<void> | void) | null;
}) {
  const prepared = await prepareSpecializedFormGeneration({
    lawFirmId: input.lawFirmId,
    actorUserId: input.actorUserId,
    agentCode: input.agentCode,
  });

  return generateClientFormFromTemplate({
    lawFirmId: input.lawFirmId,
    clientId: input.clientId,
    actorUserId: input.actorUserId,
    formTemplateId: prepared.templateId,
    formName: prepared.formName,
    agentCode: prepared.agentCode,
    agentPrompt: prepared.prompt,
    onProgress: input.onProgress ?? null,
  });
}
