import { createHash, randomBytes } from "node:crypto";
import { Prisma } from "@prisma/client";
import { prisma } from "./prisma.js";
import { createId } from "./id.js";
import {
  buildFilledFormAuditPdf,
  type FilledFormCompletionSuggestions,
  buildFilledFormPdf,
  buildPacketPdf,
  type FilledFormAuditEntry,
} from "./pdf-documents.js";
import { extractDocumentText } from "./document-reviews.js";
import { createRepositoryItem } from "./repository.js";
import { readBinaryFile, saveBinaryFile } from "./storage.js";
import {
  createAiRun,
  finishAiRun,
  runJsonChatCompletion,
} from "./tenant-ai.js";
import {
  buildWorkflowPortalPreview,
  matchesWorkflowAppliesWhen,
  resolveWorkflowDependentCount,
} from "./workflow-portal-preview.js";
import {
  buildLegacyEmploymentAdjustmentFactCandidates,
  buildLegacyEmploymentAdjustmentKnowledge,
  extractLegacyEmploymentAdjustmentArtifactsFromDocuments,
  parseLegacyEmploymentAdjustmentCaseData,
  type LegacyEmploymentAdjustmentArtifacts,
  type LegacyEmploymentAdjustmentCaseData,
  type LegacyEmploymentAdjustmentKnowledge,
} from "./legacy-employment-adjustment.js";
import {
  extractPassportNumberWithAgent,
  isPassportReaderCandidateDocument,
  isValidPassportNumberCandidate,
  normalizePassportNumberCandidate,
  type PassportNumberAgentResult,
} from "./passport-reader-agent.js";

type DataField = {
  id: string;
  field_key: string;
  label: string;
  data_type: string;
};

type CaseFactRow = {
  id: string;
  data_field_id: string;
  raw_value: string | null;
  normalized_value_json: string | null;
  confidence_score: number;
  status_code: string;
  created_at: Date;
};

type FactReviewFeedbackRow = {
  id: string;
  client_id: string;
  case_id: string | null;
  case_fact_id: string | null;
  data_field_id: string;
  field_key: string;
  label: string;
  review_action: string;
  previous_raw_value: string | null;
  previous_normalized_hash: string | null;
  corrected_raw_value: string | null;
  corrected_normalized_hash: string | null;
  source_hint: string | null;
  search_hint: string | null;
  feedback_note: string | null;
  created_at: Date;
};

type FactReviewContext = {
  rejectedHashesByFieldId: Map<string, Set<string>>;
  guidanceLines: string[];
};

let ensureFactReviewFeedbackTablePromise: Promise<void> | null = null;

async function ensureFactReviewFeedbackTable() {
  if (!ensureFactReviewFeedbackTablePromise) {
    ensureFactReviewFeedbackTablePromise = (async () => {
      await prisma.$executeRawUnsafe(`
        CREATE TABLE IF NOT EXISTS fact_review_feedback (
          id CHAR(36) NOT NULL PRIMARY KEY,
          law_firm_id CHAR(36) NOT NULL,
          client_id CHAR(36) NOT NULL,
          case_id CHAR(36) NULL,
          case_fact_id CHAR(36) NULL,
          data_field_id CHAR(36) NOT NULL,
          review_action VARCHAR(30) NOT NULL,
          previous_raw_value TEXT NULL,
          previous_normalized_hash CHAR(64) NULL,
          corrected_raw_value TEXT NULL,
          corrected_normalized_hash CHAR(64) NULL,
          source_hint TEXT NULL,
          search_hint TEXT NULL,
          feedback_note TEXT NULL,
          created_by_user_id CHAR(36) NULL,
          created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
          updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
          deleted_at TIMESTAMP NULL DEFAULT NULL,
          KEY idx_fact_review_feedback_case_fact (case_fact_id, created_at),
          KEY idx_fact_review_feedback_case (case_id, created_at),
          KEY idx_fact_review_feedback_client_field (client_id, data_field_id, created_at),
          KEY idx_fact_review_feedback_action (review_action, created_at),
          KEY idx_fact_review_feedback_prev_hash (data_field_id, previous_normalized_hash)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
      `);
    })().catch((error) => {
      ensureFactReviewFeedbackTablePromise = null;
      throw error;
    });
  }

  await ensureFactReviewFeedbackTablePromise;
}

type CoverageResult = {
  status: "covered" | "missing" | "low_confidence" | "conflicting";
  currentConfidence: number | null;
  chosenFactId: string | null;
  reasoning: string;
};

type ConsolidatedKnowledgeProfileFact = {
  fieldKey: string;
  label: string;
  value: string;
  confidence: number;
  evidence: string;
  sourceTitles: string[];
};

type ConsolidatedKnowledgeFact = {
  factKey: string;
  label: string;
  value: string;
  confidence: number;
  evidence: string;
  sourceTitles: string[];
};

type ConsolidatedClientKnowledge = {
  aiRunId: string;
  repositoryItemId: string;
  summaryText: string;
  profileFacts: ConsolidatedKnowledgeProfileFact[];
  knowledgeFacts: ConsolidatedKnowledgeFact[];
  sourceDocumentCount: number;
  sourceNoteCount: number;
};

type ClientFormAiProfile = {
  first_name: string;
  middle_name: string | null;
  last_name: string;
  preferred_name: string | null;
  date_of_birth: Date | null;
  email: string | null;
  phone: string | null;
  preferred_language: string | null;
  country_of_citizenship: string | null;
  immigration_status: string | null;
};

type ClientFormAiFact = {
  fieldKey: string;
  label: string;
  rawValue: string | null;
  confidence: number;
  statusCode: string;
  caseId: string | null;
};

type ClientFormAiDocumentEvidence = {
  title: string;
  document_type_code: string;
  extracted_text: string | null;
  scope_code: string;
  created_at: Date;
};

type ClientFormAiRepositoryNote = {
  item_type_code: string;
  subject: string | null;
  body_text: string | null;
  created_at: Date;
};

type ClientFormAiEvidenceBundle = {
  clientProfile: ClientFormAiProfile | null;
  facts: ClientFormAiFact[];
  documents: ClientFormAiDocumentEvidence[];
  repositoryNotes: ClientFormAiRepositoryNote[];
};

type I485SectionFact = {
  factKey: string;
  label: string;
  value: string;
  confidence: number;
  evidence: string;
  sourceTitles: string[];
};

type I485SectionDossier = {
  sectionCode: string;
  title: string;
  summary: string;
  facts: I485SectionFact[];
  missingInformation: string[];
  conflicts: string[];
};

type I485CanonicalDossier = {
  formCode: "uscis_i_485";
  generatedAt: string;
  overview: string;
  unresolvedTopics: string[];
  conflictTopics: string[];
  sections: I485SectionDossier[];
  sourceDocumentCount: number;
  sourceNoteCount: number;
  repositoryItemId: string | null;
};

type I485StructuredName = {
  familyName: string | null;
  givenName: string | null;
  middleName: string | null;
};

type I485StructuredAddress = {
  street: string | null;
  unitType: "Apartment" | "Suite" | "Floor" | null;
  unitNumber: string | null;
  city: string | null;
  state: string | null;
  zipCode: string | null;
  province: string | null;
  postalCode: string | null;
  country: string | null;
  inCareOfName: string | null;
  dateFrom: string | null;
  dateTo: string | null;
};

type I485EmploymentEducationEntry = {
  name: string | null;
  occupation: string | null;
  address: I485StructuredAddress | null;
  dateFrom: string | null;
  dateTo: string | null;
  sourceTitles: string[];
};

type I485FamilyPerson = {
  name: I485StructuredName | null;
  dateOfBirth: string | null;
  countryOfBirth: string | null;
  address: I485StructuredAddress | null;
  deceased: boolean | null;
  sourceTitles: string[];
};

type I485DeterministicSourceProfile = {
  principalName: I485StructuredName | null;
  hasOtherNamesUsed: boolean | null;
  hasOtherDatesOfBirth: boolean | null;
  alienNumber: string | null;
  dateOfBirth: string | null;
  sex: "Female" | "Male" | null;
  birthCity: string | null;
  birthCountry: string | null;
  countryOfCitizenship: string | null;
  passportNumber: string | null;
  passportExpiryDate: string | null;
  passportIssuingCountry: string | null;
  passportIssuingCity: string | null;
  visaNumber: string | null;
  i94Number: string | null;
  classOfAdmission: string | null;
  currentImmigrationStatus: string | null;
  lastArrivalDate: string | null;
  wasInspectedAndAdmitted: boolean | null;
  wasInspectedAndParoled: boolean | null;
  authorizedStayUntil: string | null;
  currentUsAddress: I485StructuredAddress | null;
  priorUsAddress: I485StructuredAddress | null;
  mostRecentForeignAddress: I485StructuredAddress | null;
  currentAddressFromDate: string | null;
  hasResidedAtCurrentAddressForFiveYears: boolean | null;
  currentPhone: string | null;
  currentMobilePhone: string | null;
  email: string | null;
  hasSsn: boolean | null;
  ssn: string | null;
  employmentBasedCategory:
    | "skilled_worker"
    | "professional"
    | "other_worker"
    | "advanced_degree"
    | null;
  i140ReceiptNumber: string | null;
  currentEmploymentOrSchool: I485EmploymentEducationEntry | null;
  mostRecentOutsideUsEmploymentOrSchool: I485EmploymentEducationEntry | null;
  parent1: I485FamilyPerson | null;
  parent2: I485FamilyPerson | null;
  maritalStatus:
    | "Married"
    | "Divorced"
    | "Single, Never Married"
    | "Widowed"
    | "Marriage Annulled"
    | "Legally Separated"
    | null;
  spouseMilitaryStatus: "No" | "Yes" | "Not Applicable" | null;
  currentSpouse: I485FamilyPerson | null;
  marriageDate: string | null;
  marriageCity: string | null;
  marriageStateOrProvince: string | null;
  marriageCountry: string | null;
  spouseApplyingWithApplicant: boolean | null;
  numberOfChildren: string | null;
  child1: I485FamilyPerson | null;
  child1ApplyingWithApplicant: boolean | null;
  child1Relationship: string | null;
  hasAppliedImmigrantVisaAbroad: boolean | null;
  ethnicityHispanic: boolean | null;
  raceAsian: boolean | null;
  heightFeet: string | null;
  heightInches: string | null;
  weightPounds: string | null;
  eyeColor:
    | "Blue"
    | "Black"
    | "Brown"
    | "Gray"
    | "Green"
    | "Hazel"
    | "Maroon"
    | "Pink"
    | "Unknown / Other"
    | null;
  hairColor:
    | "Bald (No hair)"
    | "Black"
    | "Blond"
    | "Brown"
    | "Gray"
    | "Red"
    | "Sandy"
    | "White"
    | "Unknown / Other"
    | null;
};

type I485SectionDefinition = {
  code: string;
  title: string;
  description: string;
  keywords: string[];
  matchesField: (field: {
    pdfFieldName: string;
    label: string;
    dataType: string;
    sectionName?: string | null;
    pageNumber?: number | null;
  }) => boolean;
};

function buildI485StructuredNameFromLegacyCaseData(
  name:
    | {
        family?: string | null;
        given?: string | null;
        middle?: string | null;
      }
    | null
    | undefined,
) {
  const familyName = normalizeConsolidatedKnowledgeText(name?.family) || null;
  const givenName = normalizeConsolidatedKnowledgeText(name?.given) || null;
  const middleName = normalizeConsolidatedKnowledgeText(name?.middle) || null;

  if (!familyName && !givenName && !middleName) {
    return null;
  }

  return {
    familyName,
    givenName,
    middleName,
  } satisfies I485StructuredName;
}

function buildI485StructuredAddressFromLegacyCaseData(
  address:
    | {
        in_care_of?: string | null;
        street?: string | null;
        unit_type?: string | null;
        unit_num?: string | null;
        city?: string | null;
        state?: string | null;
        zip?: string | null;
        province?: string | null;
        postal?: string | null;
        country?: string | null;
      }
    | null
    | undefined,
) {
  if (!address) {
    return null;
  }

  const unitTypeRaw = normalizeConsolidatedKnowledgeText(address.unit_type);
  const unitType =
    /^apt/i.test(unitTypeRaw ?? "") ? "Apartment"
    : /^ste/i.test(unitTypeRaw ?? "") ? "Suite"
    : /^fl/i.test(unitTypeRaw ?? "") ? "Floor"
    : null;

  const structuredAddress = {
    street: normalizeConsolidatedKnowledgeText(address.street) || null,
    unitType,
    unitNumber: normalizeConsolidatedKnowledgeText(address.unit_num) || null,
    city: normalizeConsolidatedKnowledgeText(address.city) || null,
    state: normalizeConsolidatedKnowledgeText(address.state) || null,
    zipCode: normalizeConsolidatedKnowledgeText(address.zip) || null,
    province: normalizeConsolidatedKnowledgeText(address.province) || null,
    postalCode: normalizeConsolidatedKnowledgeText(address.postal) || null,
    country: normalizeConsolidatedKnowledgeText(address.country) || null,
    inCareOfName: normalizeConsolidatedKnowledgeText(address.in_care_of) || null,
    dateFrom: null,
    dateTo: null,
  } satisfies I485StructuredAddress;

  return hasUsableI485Address(structuredAddress) ? structuredAddress : null;
}

function buildI485FamilyPersonFromLegacyCaseData(
  person:
    | {
        family?: string | null;
        given?: string | null;
        middle?: string | null;
        dob?: string | null;
        country_of_birth?: string | null;
      }
    | null
    | undefined,
  sourceTitle: string,
) {
  const name = buildI485StructuredNameFromLegacyCaseData(person);
  const dateOfBirth = formatI485UsDate(person?.dob ?? null) ?? null;
  const countryOfBirth = normalizeConsolidatedKnowledgeText(person?.country_of_birth) || null;

  if (!name && !dateOfBirth && !countryOfBirth) {
    return null;
  }

  return {
    name,
    dateOfBirth,
    countryOfBirth,
    address: null,
    deceased: null,
    sourceTitles: [sourceTitle],
  } satisfies I485FamilyPerson;
}

function normalizeLegacyEmploymentBasedCategory(
  value: string | null | undefined,
): I485DeterministicSourceProfile["employmentBasedCategory"] {
  const normalized = normalizeKnowledgeSearchText(value);
  if (!normalized) {
    return null;
  }

  if (normalized.includes("skilled")) {
    return "skilled_worker";
  }
  if (normalized.includes("professional")) {
    return "professional";
  }
  if (normalized.includes("other")) {
    return "other_worker";
  }
  if (
    normalized.includes("advanced degree") ||
    normalized.includes("exceptional ability") ||
    normalized.includes("eb2")
  ) {
    return "advanced_degree";
  }

  return null;
}

function normalizeLegacyMaritalStatus(
  value: string | null | undefined,
): I485DeterministicSourceProfile["maritalStatus"] {
  const normalized = normalizeKnowledgeSearchText(value);
  if (!normalized) {
    return null;
  }

  if (normalized === "single" || normalized === "never married") {
    return "Single, Never Married";
  }
  if (normalized === "married") {
    return "Married";
  }
  if (normalized === "divorced") {
    return "Divorced";
  }
  if (normalized === "widowed") {
    return "Widowed";
  }
  if (normalized.includes("annul")) {
    return "Marriage Annulled";
  }
  if (normalized.includes("separated")) {
    return "Legally Separated";
  }

  return null;
}

function normalizeLegacyI485EyeColor(
  value: string | null | undefined,
): I485DeterministicSourceProfile["eyeColor"] {
  const normalized = normalizeConsolidatedKnowledgeText(value);
  if (!normalized) {
    return null;
  }

  const title = `${normalized.charAt(0).toUpperCase()}${normalized.slice(1).toLowerCase()}`;
  if (title === "Other" || title === "Unknown") {
    return "Unknown / Other";
  }

  return (
    title === "Blue" ||
    title === "Black" ||
    title === "Brown" ||
    title === "Gray" ||
    title === "Green" ||
    title === "Hazel" ||
    title === "Maroon" ||
    title === "Pink"
  )
    ? title
    : null;
}

function normalizeLegacyI485HairColor(
  value: string | null | undefined,
): I485DeterministicSourceProfile["hairColor"] {
  const normalized = normalizeConsolidatedKnowledgeText(value);
  if (!normalized) {
    return null;
  }

  const title = `${normalized.charAt(0).toUpperCase()}${normalized.slice(1).toLowerCase()}`;
  if (title === "Bald") {
    return "Bald (No hair)";
  }
  if (title === "Other" || title === "Unknown") {
    return "Unknown / Other";
  }

  return (
    title === "Black" ||
    title === "Blond" ||
    title === "Brown" ||
    title === "Gray" ||
    title === "Red" ||
    title === "Sandy" ||
    title === "White"
  )
    ? title
    : null;
}

function normalizeUsableLegacyI485Scalar(value: string | null | undefined) {
  const normalized = normalizeConsolidatedKnowledgeText(value);
  return isUsableI485Scalar(normalized) ? normalized : null;
}

function normalizeComparableText(value: string | null | undefined) {
  return String(value ?? "")
    .replace(/\s+/g, " ")
    .trim()
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, " ")
    .trim();
}

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

function uniqueByKey<T>(items: T[], keySelector: (item: T) => string) {
  const seen = new Set<string>();
  const output: T[] = [];

  for (const item of items) {
    const key = keySelector(item);
    if (!key || seen.has(key)) {
      continue;
    }

    seen.add(key);
    output.push(item);
  }

  return output;
}

function buildCurrentEmploymentOrSchoolFromLegacyCaseData(
  caseData: LegacyEmploymentAdjustmentCaseData,
  sourceTitle: string,
) {
  const i20Entries = Array.isArray(caseData.i20_history) ? caseData.i20_history : [];
  const latestI20 = [...i20Entries].sort((left, right) => {
    const leftDate = Date.parse(String(left.date_issued ?? left.program_end ?? ""));
    const rightDate = Date.parse(String(right.date_issued ?? right.program_end ?? ""));
    return (Number.isNaN(rightDate) ? 0 : rightDate) - (Number.isNaN(leftDate) ? 0 : leftDate);
  })[0] ?? null;
  const currentStatus = normalizeKnowledgeSearchText(caseData.current_status?.class);

  if (latestI20 && currentStatus.startsWith("f")) {
    return {
      name: normalizeConsolidatedKnowledgeText(latestI20.school) || null,
      occupation:
        normalizeConsolidatedKnowledgeText(latestI20.program) ?
          `Student • ${normalizeConsolidatedKnowledgeText(latestI20.program)}` :
          "Student",
      address: null,
      dateFrom: formatI485UsDate(latestI20.program_start ?? null) ?? null,
      dateTo: formatI485UsDate(latestI20.program_end ?? null) ?? null,
      sourceTitles: [sourceTitle],
    } satisfies I485EmploymentEducationEntry;
  }

  const employerName = normalizeConsolidatedKnowledgeText(caseData.petitioner?.legal_name) || null;
  const occupation = normalizeConsolidatedKnowledgeText(caseData.i140?.job_title) || null;
  const address = buildI485StructuredAddressFromLegacyCaseData(caseData.petitioner?.address);

  if (!employerName && !occupation && !address) {
    return null;
  }

  return {
    name: employerName,
    occupation,
    address,
    dateFrom: null,
    dateTo: null,
    sourceTitles: [sourceTitle],
  } satisfies I485EmploymentEducationEntry;
}

function mergeI485DeterministicSourceProfileWithLegacyCaseData(input: {
  profile: I485DeterministicSourceProfile;
  caseData: LegacyEmploymentAdjustmentCaseData | null;
}) {
  const caseData = input.caseData;
  if (!caseData) {
    return input.profile;
  }

  const sourceTitle = `Legacy employment AOS case data${caseData.case_id ? ` • ${caseData.case_id}` : ""}`;
  const latestTravel =
    Array.isArray(caseData.travel_history) && caseData.travel_history.length
      ? caseData.travel_history[caseData.travel_history.length - 1]
      : null;
  const currentMarriage =
    (caseData.marriages ?? []).find((entry) =>
      /^current$/i.test(normalizeConsolidatedKnowledgeText(entry.status) ?? ""),
    ) ?? null;
  const dependentSpouse =
    (caseData.dependents ?? []).find((dependent) =>
      /spouse/i.test(normalizeConsolidatedKnowledgeText(dependent.relationship) ?? ""),
    ) ?? null;
  const child =
    (caseData.dependents ?? []).find((dependent) =>
      /child|son|daughter/i.test(normalizeConsolidatedKnowledgeText(dependent.relationship) ?? ""),
    ) ?? null;
  const applicantMailingAddress = buildI485StructuredAddressFromLegacyCaseData(
    caseData.applicant?.mailing_address,
  );
  const applicantPhysicalAddress = buildI485StructuredAddressFromLegacyCaseData(
    caseData.applicant?.physical_address_same_as_mailing ?
      caseData.applicant?.mailing_address :
      caseData.applicant?.physical_address,
  );
  const foreignAddress = buildI485StructuredAddressFromLegacyCaseData(
    caseData.applicant?.last_foreign_address,
  );
  const parent1 =
    (caseData.parents ?? []).find((parent) =>
      /father/i.test(normalizeConsolidatedKnowledgeText(parent.role) ?? ""),
    ) ?? null;
  const parent2 =
    (caseData.parents ?? []).find((parent) =>
      /mother/i.test(normalizeConsolidatedKnowledgeText(parent.role) ?? ""),
    ) ?? null;
  const childCount = (caseData.dependents ?? []).filter((dependent) =>
    /child|son|daughter/i.test(
      normalizeConsolidatedKnowledgeText(dependent.relationship) ?? "",
    ),
  ).length;

  const mergedProfile = {
    ...input.profile,
    principalName:
      buildI485StructuredNameFromLegacyCaseData(caseData.applicant?.name) ?? input.profile.principalName,
    hasOtherNamesUsed:
      Array.isArray(caseData.applicant?.other_names_used) ?
        caseData.applicant?.other_names_used.length > 0 :
        input.profile.hasOtherNamesUsed,
    alienNumber:
      normalizeUsableLegacyI485Scalar(caseData.applicant?.a_number) ?? input.profile.alienNumber,
    dateOfBirth:
      formatI485UsDate(caseData.applicant?.dob ?? null) ?? input.profile.dateOfBirth,
    sex:
      /^f/i.test(normalizeConsolidatedKnowledgeText(caseData.applicant?.sex) ?? "") ? "Female"
      : /^m/i.test(normalizeConsolidatedKnowledgeText(caseData.applicant?.sex) ?? "") ? "Male"
      : input.profile.sex,
    birthCity:
      normalizeUsableLegacyI485Scalar(caseData.applicant?.city_of_birth) ?? input.profile.birthCity,
    birthCountry:
      normalizeUsableLegacyI485Scalar(caseData.applicant?.country_of_birth) ??
      input.profile.birthCountry,
    countryOfCitizenship:
      normalizeUsableLegacyI485Scalar(caseData.applicant?.country_of_citizenship) ??
      input.profile.countryOfCitizenship,
    passportNumber:
      normalizeUsableLegacyI485Scalar(caseData.applicant?.passport?.number) ??
      input.profile.passportNumber,
    passportExpiryDate:
      formatI485UsDate(caseData.applicant?.passport?.expiration ?? null) ??
      input.profile.passportExpiryDate,
    passportIssuingCountry:
      normalizeUsableLegacyI485Scalar(caseData.applicant?.passport?.country_of_issuance) ??
      input.profile.passportIssuingCountry,
    visaNumber:
      normalizeUsableLegacyI485Scalar(caseData.applicant?.visa_at_last_entry?.number) ??
      input.profile.visaNumber,
    i94Number:
      normalizeUsableLegacyI485Scalar(latestTravel?.i94_number ?? caseData.i94?.number) ??
      input.profile.i94Number,
    classOfAdmission:
      normalizeUsableLegacyI485Scalar(
        latestTravel?.class_of_admission ?? caseData.i94?.class,
      ) ?? input.profile.classOfAdmission,
    currentImmigrationStatus:
      normalizeUsableLegacyI485Scalar(caseData.current_status?.class) ??
      input.profile.currentImmigrationStatus,
    lastArrivalDate:
      formatI485UsDate(latestTravel?.date_of_entry ?? caseData.i94?.date_of_entry ?? null) ??
      input.profile.lastArrivalDate,
    wasInspectedAndAdmitted:
      latestTravel?.inspected ?? input.profile.wasInspectedAndAdmitted,
    wasInspectedAndParoled:
      latestTravel?.inspected === true ? false : input.profile.wasInspectedAndParoled,
    authorizedStayUntil:
      formatI485UsDate(latestTravel?.admit_until ?? caseData.i94?.admit_until ?? null) ??
      input.profile.authorizedStayUntil,
    currentUsAddress: applicantPhysicalAddress ?? applicantMailingAddress ?? input.profile.currentUsAddress,
    mostRecentForeignAddress: foreignAddress ?? input.profile.mostRecentForeignAddress,
    currentPhone:
      normalizeUsableLegacyI485Scalar(caseData.applicant?.phone) ?? input.profile.currentPhone,
    currentMobilePhone:
      normalizeUsableLegacyI485Scalar(caseData.applicant?.phone) ??
      input.profile.currentMobilePhone,
    email:
      normalizeUsableLegacyI485Scalar(caseData.applicant?.email) ?? input.profile.email,
    hasSsn:
      caseData.applicant?.ssn !== undefined && caseData.applicant?.ssn !== null ?
        Boolean(normalizeUsableLegacyI485Scalar(caseData.applicant?.ssn)) :
        input.profile.hasSsn,
    ssn: normalizeUsableLegacyI485Scalar(caseData.applicant?.ssn) ?? input.profile.ssn,
    employmentBasedCategory:
      normalizeLegacyEmploymentBasedCategory(caseData.i140?.category) ??
      input.profile.employmentBasedCategory,
    i140ReceiptNumber:
      normalizeUsableLegacyI485Scalar(caseData.i140?.receipt_number) ??
      input.profile.i140ReceiptNumber,
    currentEmploymentOrSchool:
      buildCurrentEmploymentOrSchoolFromLegacyCaseData(caseData, sourceTitle) ??
      input.profile.currentEmploymentOrSchool,
    parent1:
      buildI485FamilyPersonFromLegacyCaseData(parent1, sourceTitle) ?? input.profile.parent1,
    parent2:
      buildI485FamilyPersonFromLegacyCaseData(parent2, sourceTitle) ?? input.profile.parent2,
    maritalStatus:
      normalizeLegacyMaritalStatus(caseData.applicant?.marital_status) ?? input.profile.maritalStatus,
    currentSpouse:
      buildI485FamilyPersonFromLegacyCaseData(currentMarriage?.spouse, sourceTitle) ??
      input.profile.currentSpouse,
    marriageDate:
      formatI485UsDate(currentMarriage?.date_of_marriage ?? null) ?? input.profile.marriageDate,
    marriageCity:
      normalizeUsableLegacyI485Scalar(currentMarriage?.place_of_marriage?.city) ??
      input.profile.marriageCity,
    marriageStateOrProvince:
      normalizeUsableLegacyI485Scalar(currentMarriage?.place_of_marriage?.state) ??
      input.profile.marriageStateOrProvince,
    marriageCountry:
      normalizeUsableLegacyI485Scalar(currentMarriage?.place_of_marriage?.country) ??
      input.profile.marriageCountry,
    spouseApplyingWithApplicant:
      dependentSpouse?.filing_with_principal ?? input.profile.spouseApplyingWithApplicant,
    numberOfChildren: childCount > 0 ? String(childCount) : input.profile.numberOfChildren,
    child1:
      buildI485FamilyPersonFromLegacyCaseData(
        child ?
          {
            family: child.name?.family,
            given: child.name?.given,
            middle: child.name?.middle,
            dob: child.dob,
            country_of_birth: child.country_of_birth,
          } :
          null,
        sourceTitle,
      ) ?? input.profile.child1,
    child1ApplyingWithApplicant:
      child?.filing_with_principal ?? input.profile.child1ApplyingWithApplicant,
    child1Relationship:
      normalizeUsableLegacyI485Scalar(child?.relationship) ?? input.profile.child1Relationship,
    ethnicityHispanic:
      normalizeKnowledgeSearchText(caseData.applicant?.ethnicity).includes("hispanic") ||
        normalizeKnowledgeSearchText(caseData.applicant?.ethnicity).includes("latino") ?
        true :
      normalizeKnowledgeSearchText(caseData.applicant?.ethnicity).includes("not hispanic") ?
        false :
        input.profile.ethnicityHispanic,
    raceAsian:
      Array.isArray(caseData.applicant?.race) &&
        caseData.applicant?.race.some((entry) =>
          normalizeKnowledgeSearchText(entry).includes("asian"),
        ) ?
        true :
        input.profile.raceAsian,
    heightFeet:
      normalizeConsolidatedKnowledgeText(caseData.applicant?.height_ft) ?? input.profile.heightFeet,
    heightInches:
      normalizeConsolidatedKnowledgeText(caseData.applicant?.height_in) ??
      input.profile.heightInches,
    weightPounds:
      normalizeConsolidatedKnowledgeText(caseData.applicant?.weight_lb) ??
      input.profile.weightPounds,
    eyeColor:
      normalizeLegacyI485EyeColor(caseData.applicant?.eye_color) ?? input.profile.eyeColor,
    hairColor:
      normalizeLegacyI485HairColor(caseData.applicant?.hair_color) ?? input.profile.hairColor,
  } satisfies I485DeterministicSourceProfile;

  return mergedProfile;
}

function extendConsolidatedKnowledgeWithLegacyEmploymentAdjustment(input: {
  consolidatedKnowledge?: {
    summaryText: string;
    profileFacts: ConsolidatedKnowledgeProfileFact[];
    knowledgeFacts: ConsolidatedKnowledgeFact[];
    aiRunId?: string;
    repositoryItemId?: string;
    sourceDocumentCount?: number;
    sourceNoteCount?: number;
  } | null;
  legacyKnowledge: LegacyEmploymentAdjustmentKnowledge | null;
}) {
  if (!input.legacyKnowledge) {
    return input.consolidatedKnowledge ?? null;
  }

  const baseProfileFacts = input.consolidatedKnowledge?.profileFacts ?? [];
  const baseKnowledgeFacts = input.consolidatedKnowledge?.knowledgeFacts ?? [];
  const legacyProfileFacts = input.legacyKnowledge.profileFacts.map((fact) => ({
    fieldKey: fact.factKey,
    label: fact.label,
    value: fact.value,
    confidence: fact.confidence,
    evidence: fact.evidence,
    sourceTitles: fact.sourceTitles,
  })) satisfies ConsolidatedKnowledgeProfileFact[];
  const legacyKnowledgeFacts = input.legacyKnowledge.knowledgeFacts.map((fact) => ({
    factKey: fact.factKey,
    label: fact.label,
    value: fact.value,
    confidence: fact.confidence,
    evidence: fact.evidence,
    sourceTitles: fact.sourceTitles,
  })) satisfies ConsolidatedKnowledgeFact[];

  return {
    aiRunId: input.consolidatedKnowledge?.aiRunId ?? "",
    repositoryItemId: input.consolidatedKnowledge?.repositoryItemId ?? "",
    summaryText:
      [
        input.legacyKnowledge.summaryText,
        input.consolidatedKnowledge?.summaryText ?? "",
      ]
        .map((item) => normalizeConsolidatedKnowledgeText(item))
        .filter(Boolean)
        .join(" "),
    profileFacts: uniqueByKey(
      [...legacyProfileFacts, ...baseProfileFacts],
      (item) =>
        `${normalizeComparableText(item.fieldKey)}:${normalizeComparableValue(item.value)}`,
    ).slice(0, 120),
    knowledgeFacts: uniqueByKey(
      [...legacyKnowledgeFacts, ...baseKnowledgeFacts],
      (item) =>
        `${normalizeComparableText(item.factKey)}:${normalizeComparableValue(item.value)}`,
    ).slice(0, 220),
    sourceDocumentCount: input.consolidatedKnowledge?.sourceDocumentCount ?? 0,
    sourceNoteCount: input.consolidatedKnowledge?.sourceNoteCount ?? 0,
  } satisfies ConsolidatedClientKnowledge;
}

function mergeI485LegacyKnowledgeIntoDossier(input: {
  dossier: I485CanonicalDossier;
  legacyKnowledge: LegacyEmploymentAdjustmentKnowledge | null;
}) {
  if (!input.legacyKnowledge) {
    return input.dossier;
  }

  const unresolvedTopics = uniqueLimitedStrings(
    [...input.dossier.unresolvedTopics, ...input.legacyKnowledge.unresolvedTopics],
    24,
  );
  const overview = [
    normalizeConsolidatedKnowledgeText(input.dossier.overview),
    normalizeConsolidatedKnowledgeText(input.legacyKnowledge.summaryText),
  ]
    .filter(Boolean)
    .join(" ");

  return {
    ...input.dossier,
    overview: overview || input.dossier.overview,
    unresolvedTopics,
  } satisfies I485CanonicalDossier;
}

type SpecializedFormInferenceResult = {
  values: Record<string, string>;
  inputTokens: number;
  outputTokens: number;
  canonicalDossier?: I485CanonicalDossier | null;
  validationWarnings?: string[];
  fieldAuditEntries?: FilledFormAuditEntry[];
  completionSuggestions?: FilledFormCompletionSuggestions | null;
};

type I485CoverageFieldStatus = "filled_valid" | "missing_but_supported" | "conflicting" | "unsupported";

type I485CoverageFieldAssessment = {
  pdfFieldName: string;
  status: I485CoverageFieldStatus;
  reason: string;
};

type I485SectionCoverageBreakdown = {
  sectionCode: string;
  title: string;
  coverage: number;
  filledValidFields: number;
  totalFillableFields: number;
  missingFields: string[];
  conflictingFields: string[];
  unsupportedFields: string[];
};

export type I485OptimizationIterationSummary = {
  iteration: number;
  coverage: number;
  filledFields: number;
  totalFields: number;
  missingFields: string[];
  conflictingFields: string[];
  unsupportedFields: string[];
  improvementsApplied: string[];
  runtimeMs: number;
  sectionBreakdown: I485SectionCoverageBreakdown[];
  generatedDocumentId: string;
  repositoryItemId: string;
  fileId: string;
  fileName: string;
};

export type I485OptimizationFinalReport = {
  runId: string;
  clientId: string;
  formTemplateId: string;
  agentCode: string;
  coverage: number;
  filledFields: number;
  totalFields: number;
  iterations: number;
  runtimeMs: number;
  stopReason: "coverage_reached" | "runtime_exceeded";
  improvementsMade: string[];
  sectionBreakdown: I485SectionCoverageBreakdown[];
  remainingGaps: string[];
  latestGeneratedDocumentId: string;
  latestRepositoryItemId: string;
  latestFileId: string;
  latestFileName: string;
  iterationHistory: I485OptimizationIterationSummary[];
};

type DetailedDocumentImportantFact = {
  label: string;
  value: string;
  confidence: number;
  evidence: string;
};

export type RepositoryDocumentAiReprocessResult = {
  documentRecordId: string;
  repositoryItemId: string | null;
  fileId: string;
  title: string;
  documentTypeCode: string;
  textLength: number;
  aiRunId: string | null;
  extractionId: string | null;
  extractedData: Record<string, string>;
  extractedPreview: Array<{ label: string; value: string }>;
  syncedClientFieldKeys: string[];
  summaryText: string;
  importantFacts: DetailedDocumentImportantFact[];
};

type WorkflowConditionKey = string;

type PortalDocumentRequirementMetadata = {
  workflowTemplateId: string;
  workflowName: string;
  workflowRequiredDocumentId: string;
  documentTypeCode: string;
  displayName: string;
  description: string | null;
  requirementCode: string;
  minimumQuantity: number;
  isRequired: boolean;
};

type PortalQuestionnaireSessionRow = {
  id: string;
  law_firm_id: string;
  client_id: string;
  case_id: string | null;
  status_code: string;
  expires_at: Date;
  completed_at: Date | null;
  client_portal_session_id: string | null;
  information_request_id: string | null;
  request_type: string | null;
  run_reason: string | null;
};

type PortalQuestionRow = {
  id: string;
  sequence_no: number;
  question_type: string;
  question_text: string;
  question_context_group: string | null;
  reason_text: string | null;
  status_code: string;
  answered_at: Date | null;
  created_at: Date;
};

type PortalAnswerRow = {
  id: string;
  question_id: string;
  answer_text: string | null;
  answered_at: Date;
};

type PortalDocumentRequestItemRow = {
  id: string;
  reason_text: string | null;
  status_code: string;
  resolved_at: Date | null;
};

type QuestionnaireCoverageSummary = {
  sourceType: "gap_analysis" | "workflow_portal" | "other";
  workflowTemplateIds: string[];
  portalToken: string | null;
  questionCount: number;
  documentCount: number;
};

const textExtractionPatterns: Record<string, RegExp[]> = {
  client_email: [/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/i],
  client_phone: [/(\+?\d[\d\s().-]{7,}\d)/i],
  passport_number: [
    /(?:passport(?:\/travel document)?(?:\s+number)?|passport no\.?|document no\.?|no\.?\s+of passport|numero de pasaporte|número de pasaporte)[:#\s]*([A-Z0-9< -]{6,15})/i,
  ],
  client_date_of_birth: [/(?:date of birth|dob)[:#]?\s*([0-9]{4}-[0-9]{2}-[0-9]{2}|[0-9]{2}\/[0-9]{2}\/[0-9]{4})/i],
  client_country_of_citizenship: [/(?:country of citizenship|citizenship)[:#]?\s*([A-Za-z ,.'-]+)/i],
  employer_name: [/(?:employer|company)(?:\s+is|[:#])?\s*([A-Za-z0-9 ,.'&-]+)/i],
  job_title: [/(?:job title|position)(?:\s+is|[:#])?\s*([A-Za-z0-9 ,.'&/-]+)/i],
  annual_salary: [/(?:annual salary|salary|wage)[:#]?\s*([$0-9,.\s]+)/i],
};

const clientFieldColumnMap: Record<string, string> = {
  client_first_name: "first_name",
  client_last_name: "last_name",
  client_email: "email",
  client_phone: "phone",
  client_date_of_birth: "date_of_birth",
  client_country_of_citizenship: "country_of_citizenship",
  client_immigration_status: "immigration_status",
};

const countryCodeAliasMap: Record<string, string> = {
  bangladesh: "BD",
  bangladeshi: "BD",
  brazil: "BR",
  brazilian: "BR",
  canada: "CA",
  canadian: "CA",
  china: "CN",
  chinese: "CN",
  india: "IN",
  indian: "IN",
  mexico: "MX",
  mexican: "MX",
  pakistan: "PK",
  pakistani: "PK",
  unitedstates: "US",
  unitedstatesofamerica: "US",
  usa: "US",
  us: "US",
};

const inferredDocumentTypePatterns: Array<{
  code: string;
  patterns: RegExp[];
}> = [
  {
    code: "passport",
    patterns: [/\bpassport\b/i, /\bpassaporte\b/i],
  },
  {
    code: "birth_certificate",
    patterns: [
      /\bbirth certificate\b/i,
      /\bcertificate of birth\b/i,
      /\bcertidao de nascimento\b/i,
    ],
  },
  {
    code: "marriage_certificate",
    patterns: [
      /\bmarriage certificate\b/i,
      /\bcertificate of marriage\b/i,
      /\bcertidao de casamento\b/i,
    ],
  },
  {
    code: "visa",
    patterns: [/\bvisa\b/i, /\bi-797\b/i, /\bds-160\b/i],
  },
  {
    code: "i94",
    patterns: [/\bi-?94\b/i, /\badmission record\b/i],
  },
  {
    code: "resume",
    patterns: [/\bresume\b/i, /\bcurriculum vitae\b/i, /\bcurriculo\b/i, /\bcv\b/i],
  },
  {
    code: "employment_letter",
    patterns: [
      /\bemployment letter\b/i,
      /\boffer letter\b/i,
      /\bverification of employment\b/i,
      /\bjob offer\b/i,
    ],
  },
  {
    code: "pay_stub",
    patterns: [/\bpay stub\b/i, /\bpaycheck stub\b/i, /\bpayslip\b/i, /\bpay slip\b/i],
  },
  {
    code: "tax_return",
    patterns: [/\btax return\b/i, /\bform 1040\b/i, /\bw-2\b/i, /\birs\b/i],
  },
  {
    code: "translation",
    patterns: [/\btranslation\b/i, /\btranslated\b/i, /\btraducao\b/i],
  },
];

function hashString(value: string) {
  return createHash("sha256").update(value).digest("hex");
}

function inferDocumentTypeCode(input: {
  requestedDocumentTypeCode?: string | null;
  title: string;
  originalFileName: string;
  mimeType: string;
  textContent?: string | null;
}) {
  const requestedDocumentTypeCode = String(input.requestedDocumentTypeCode ?? "").trim();

  if (requestedDocumentTypeCode && requestedDocumentTypeCode !== "other_supporting") {
    return requestedDocumentTypeCode;
  }

  const matchingText = `${input.title} ${input.originalFileName} ${input.textContent ?? ""}`
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, " ")
    .toLowerCase();

  for (const candidate of inferredDocumentTypePatterns) {
    if (candidate.patterns.some((pattern) => pattern.test(matchingText))) {
      return candidate.code;
    }
  }

  if (
    input.mimeType.startsWith("image/") &&
    /\b(photo|photograph|fotografia|foto)\b/i.test(matchingText)
  ) {
    return "photo";
  }

  return requestedDocumentTypeCode || "other_supporting";
}

async function classifyDocumentType(input: {
  lawFirmId: string;
  clientId: string;
  caseId?: string | null;
  title: string;
  originalFileName: string;
  mimeType: string;
  textContent?: string | null;
  fallbackDocumentTypeCode?: string | null;
}) {
  const fallbackDocumentTypeCode = String(input.fallbackDocumentTypeCode ?? "").trim();
  const heuristicDocumentTypeCode = inferDocumentTypeCode({
    requestedDocumentTypeCode: "",
    title: input.title,
    originalFileName: input.originalFileName,
    mimeType: input.mimeType,
    textContent: input.textContent ?? null,
  });

  const documentTypes = await prisma.$queryRaw<
    Array<{
      code: string;
      name: string;
      description: string | null;
    }>
  >`
    SELECT code, name, description
    FROM document_types
    ORDER BY code ASC
  `;

  const validCodes = new Set(documentTypes.map((item) => item.code));
  let aiRunId: string | null = null;
  try {
    const aiRun = await createAiRun({
      lawFirmId: input.lawFirmId,
      caseId: input.caseId ?? null,
      clientId: input.clientId,
      runType: "classification",
    });
    aiRunId = aiRun.id;

    const completion = await runJsonChatCompletion({
      lawFirmId: input.lawFirmId,
      systemPrompt:
        "You classify immigration and legal support documents. Reply with JSON only using the exact schema: {\"documentTypeCode\": string, \"confidence\": number, \"reason\": string}. The documentTypeCode must be one of the allowed codes provided by the user. Choose other_supporting when uncertain. Confidence must be between 0 and 1.",
      userPrompt: JSON.stringify({
        title: input.title,
        originalFileName: input.originalFileName,
        mimeType: input.mimeType,
        fallbackDocumentTypeCode: fallbackDocumentTypeCode || null,
        heuristicDocumentTypeCode,
        extractedTextPreview: String(input.textContent ?? "").slice(0, 6000),
        allowedDocumentTypes: documentTypes,
      }),
      maxCompletionTokens: 300,
    });

    const aiDocumentTypeCode = String(completion.json.documentTypeCode ?? "").trim();
    const aiConfidence = Number(completion.json.confidence ?? 0);
    const aiReason = String(completion.json.reason ?? "").trim() || null;

    if (
      validCodes.has(aiDocumentTypeCode) &&
      Number.isFinite(aiConfidence) &&
      aiConfidence >= 0.45
    ) {
      await finishAiRun({
        aiRunId: aiRun.id,
        status: "completed",
        inputTokens: completion.usage.inputTokens,
        outputTokens: completion.usage.outputTokens,
        estimatedCost: 0,
      });

      return {
        documentTypeCode: aiDocumentTypeCode,
        classificationSource: "ai" as const,
        classificationConfidence: Math.max(0, Math.min(1, aiConfidence)),
        classificationReason: aiReason,
      };
    }

    await finishAiRun({
      aiRunId: aiRun.id,
      status: "completed",
      inputTokens: completion.usage.inputTokens,
      outputTokens: completion.usage.outputTokens,
      estimatedCost: 0,
      errorMessage: aiReason ?? "AI classification returned low confidence",
    });
  } catch (error) {
    if (aiRunId) {
      await finishAiRun({
        aiRunId,
        status: "failed",
        errorMessage: error instanceof Error ? error.message : "AI classification failed",
      });
    }
  }

  if (heuristicDocumentTypeCode && heuristicDocumentTypeCode !== "other_supporting") {
    return {
      documentTypeCode: heuristicDocumentTypeCode,
      classificationSource: "heuristic" as const,
      classificationConfidence: null,
      classificationReason: "Matched by filename or extracted text pattern",
    };
  }

  return {
    documentTypeCode:
      (fallbackDocumentTypeCode && validCodes.has(fallbackDocumentTypeCode) ?
        fallbackDocumentTypeCode :
        "other_supporting"),
    classificationSource: "fallback" as const,
    classificationConfidence: null,
    classificationReason:
      fallbackDocumentTypeCode && validCodes.has(fallbackDocumentTypeCode) ?
        "Used the selected fallback document type" :
        "No reliable document type match was found",
  };
}

export function hashPortalToken(token: string) {
  return hashString(token);
}

function normalizeWorkflowTemplateSelection(values: string[]) {
  return Array.from(new Set(values.map((item) => item.trim()).filter(Boolean))).sort((left, right) =>
    left.localeCompare(right),
  );
}

function areStringArraysEqual(left: string[], right: string[]) {
  if (left.length !== right.length) {
    return false;
  }

  return left.every((value, index) => value === right[index]);
}

function buildQuestionnaireCoverageSummary(input: {
  sourceType: QuestionnaireCoverageSummary["sourceType"];
  workflowTemplateIds?: string[];
  portalToken?: string | null;
  questionCount?: number;
  documentCount?: number;
}) {
  return JSON.stringify({
    sourceType: input.sourceType,
    workflowTemplateIds: normalizeWorkflowTemplateSelection(input.workflowTemplateIds ?? []),
    portalToken: input.portalToken ?? null,
    questionCount: Math.max(0, Math.trunc(input.questionCount ?? 0)),
    documentCount: Math.max(0, Math.trunc(input.documentCount ?? 0)),
  } satisfies QuestionnaireCoverageSummary);
}

function parseQuestionnaireCoverageSummary(
  value: unknown,
  fallbackSourceType: QuestionnaireCoverageSummary["sourceType"] = "other",
): QuestionnaireCoverageSummary | null {
  if (!value) {
    return null;
  }

  let parsedValue = value;

  if (typeof parsedValue === "string") {
    try {
      parsedValue = JSON.parse(parsedValue);
    } catch {
      return null;
    }
  }

  if (!parsedValue || typeof parsedValue !== "object" || Array.isArray(parsedValue)) {
    return null;
  }

  const workflowTemplateIds = normalizeWorkflowTemplateSelection(
    Array.isArray((parsedValue as { workflowTemplateIds?: unknown[] }).workflowTemplateIds)
      ? (parsedValue as { workflowTemplateIds: unknown[] }).workflowTemplateIds.map((item) =>
          String(item ?? ""),
        )
      : [],
  );
  const sourceType = String((parsedValue as { sourceType?: unknown }).sourceType ?? "").trim();
  const portalToken = String((parsedValue as { portalToken?: unknown }).portalToken ?? "").trim();
  const questionCount = Number((parsedValue as { questionCount?: unknown }).questionCount ?? 0);
  const documentCount = Number((parsedValue as { documentCount?: unknown }).documentCount ?? 0);

  return {
    sourceType:
      sourceType === "gap_analysis" || sourceType === "workflow_portal" || sourceType === "other"
        ? sourceType
        : fallbackSourceType,
    workflowTemplateIds,
    portalToken: portalToken || null,
    questionCount: Number.isFinite(questionCount) && questionCount >= 0 ? questionCount : 0,
    documentCount: Number.isFinite(documentCount) && documentCount >= 0 ? documentCount : 0,
  };
}

function normalizeWorkflowAppliesWhen(values: unknown) {
  if (!Array.isArray(values)) {
    return [];
  }

  return Array.from(
    new Set(
      values
        .map((item) =>
          String(item ?? "")
            .normalize("NFD")
            .replace(/[\u0300-\u036f]/g, "")
            .trim()
            .toLowerCase()
            .replace(/[^a-z0-9]+/g, "_")
            .replace(/^_+|_+$/g, ""),
        )
        .filter((item): item is WorkflowConditionKey => Boolean(item)),
    ),
  );
}

function parseWorkflowConditionConfig(value: unknown) {
  if (!value) {
    return {
      aiInstructions: null,
      appliesWhen: [] as WorkflowConditionKey[],
    };
  }

  let parsedValue = value;

  if (typeof parsedValue === "string") {
    try {
      parsedValue = JSON.parse(parsedValue);
    } catch {
      return {
        aiInstructions: null,
        appliesWhen: [] as WorkflowConditionKey[],
      };
    }
  }

  if (!parsedValue || typeof parsedValue !== "object") {
    return {
      aiInstructions: null,
      appliesWhen: [] as WorkflowConditionKey[],
    };
  }

  const aiInstructions = String(
    (parsedValue as { aiInstructions?: string }).aiInstructions ?? "",
  ).trim();

  return {
    aiInstructions: aiInstructions || null,
    appliesWhen: normalizeWorkflowAppliesWhen(
      (parsedValue as { appliesWhen?: unknown[] }).appliesWhen ?? [],
    ),
  };
}

async function autoLinkCaseRequiredDocumentsFromClientRepository(input: {
  lawFirmId: string;
  caseId: string;
  clientId: string;
  workflowInstanceId?: string | null;
}) {
  const pendingRequirements = await prisma.$queryRaw<
    Array<{
      id: string;
      document_type_code: string;
      created_at: Date;
    }>
  >`
    SELECT id, document_type_code, created_at
    FROM case_required_documents
    WHERE law_firm_id = ${input.lawFirmId}
      AND case_id = ${input.caseId}
      AND status_code = 'pending'
      AND satisfied_by_document_record_id IS NULL
      AND (${input.workflowInstanceId ?? null} IS NULL OR workflow_instance_id = ${input.workflowInstanceId ?? null})
    ORDER BY created_at ASC
  `;

  if (!pendingRequirements.length) {
    return {
      linkedCount: 0,
      linkedRequirementIds: [] as string[],
      linkedDocumentRecordIds: [] as string[],
    };
  }

  const documentTypeCodes = Array.from(
    new Set(
      pendingRequirements
        .map((requirement) => requirement.document_type_code.trim())
        .filter(Boolean),
    ),
  );

  if (!documentTypeCodes.length) {
    return {
      linkedCount: 0,
      linkedRequirementIds: [] as string[],
      linkedDocumentRecordIds: [] as string[],
    };
  }

  const candidateDocuments = await prisma.$queryRaw<
    Array<{
      id: string;
      document_type_code: string;
      case_id: string | null;
      created_at: Date;
    }>
  >`
    SELECT id, document_type_code, case_id, created_at
    FROM document_records
    WHERE law_firm_id = ${input.lawFirmId}
      AND client_id = ${input.clientId}
      AND document_type_code IN (${Prisma.join(documentTypeCodes)})
      AND (case_id = ${input.caseId} OR case_id IS NULL)
    ORDER BY
      CASE WHEN case_id = ${input.caseId} THEN 0 ELSE 1 END ASC,
      created_at DESC
  `;

  if (!candidateDocuments.length) {
    return {
      linkedCount: 0,
      linkedRequirementIds: [] as string[],
      linkedDocumentRecordIds: [] as string[],
    };
  }

  const candidatesByType = new Map<string, typeof candidateDocuments>();
  for (const candidate of candidateDocuments) {
    const currentCandidates = candidatesByType.get(candidate.document_type_code) ?? [];
    currentCandidates.push(candidate);
    candidatesByType.set(candidate.document_type_code, currentCandidates);
  }

  const consumedDocumentRecordIds = new Set<string>();
  const linkedRequirementIds: string[] = [];
  const linkedDocumentRecordIds: string[] = [];

  for (const requirement of pendingRequirements) {
    const availableCandidates =
      candidatesByType
        .get(requirement.document_type_code)
        ?.filter((candidate) => !consumedDocumentRecordIds.has(candidate.id)) ?? [];

    const matchedDocument = availableCandidates[0];
    if (!matchedDocument) {
      continue;
    }

    const linkNote =
      matchedDocument.case_id === input.caseId ?
        "Auto-linked from case documents during process generation" :
        "Auto-linked from client repository during process generation";

    await prisma.$executeRaw`
      UPDATE case_required_documents
      SET status_code = 'received',
          satisfied_by_document_record_id = ${matchedDocument.id},
          notes = CASE
            WHEN notes IS NULL OR TRIM(notes) = '' THEN ${linkNote}
            ELSE notes
          END,
          updated_at = CURRENT_TIMESTAMP
      WHERE id = ${requirement.id}
    `;

    consumedDocumentRecordIds.add(matchedDocument.id);
    linkedRequirementIds.push(requirement.id);
    linkedDocumentRecordIds.push(matchedDocument.id);
  }

  return {
    linkedCount: linkedRequirementIds.length,
    linkedRequirementIds,
    linkedDocumentRecordIds,
  };
}

function buildPortalDocumentRequirementKey(metadata: Pick<
  PortalDocumentRequirementMetadata,
  "workflowTemplateId" | "workflowRequiredDocumentId" | "documentTypeCode" | "requirementCode"
>) {
  return [
    metadata.workflowTemplateId,
    metadata.workflowRequiredDocumentId || metadata.requirementCode || metadata.documentTypeCode,
  ].join(":");
}

function parsePortalDocumentRequirementMetadata(value: unknown) {
  if (typeof value !== "string" || !value.trim()) {
    return null;
  }

  try {
    const parsedValue = JSON.parse(value) as Partial<PortalDocumentRequirementMetadata>;
    const workflowTemplateId = String(parsedValue.workflowTemplateId ?? "").trim();
    const workflowRequiredDocumentId = String(parsedValue.workflowRequiredDocumentId ?? "").trim();
    const documentTypeCode = String(parsedValue.documentTypeCode ?? "").trim();
    const displayName = String(parsedValue.displayName ?? "").trim();
    const requirementCode = String(parsedValue.requirementCode ?? "").trim();

    if (
      !workflowTemplateId ||
      !workflowRequiredDocumentId ||
      !documentTypeCode ||
      !displayName ||
      !requirementCode
    ) {
      return null;
    }

    const minimumQuantity = Number(parsedValue.minimumQuantity ?? 1);

    return {
      workflowTemplateId,
      workflowName: String(parsedValue.workflowName ?? "").trim() || "Workflow",
      workflowRequiredDocumentId,
      documentTypeCode,
      displayName,
      description: String(parsedValue.description ?? "").trim() || null,
      requirementCode,
      minimumQuantity:
        Number.isFinite(minimumQuantity) && minimumQuantity > 0 ? Math.trunc(minimumQuantity) : 1,
      isRequired: Boolean(parsedValue.isRequired ?? true),
    } satisfies PortalDocumentRequirementMetadata;
  } catch {
    return null;
  }
}

function parseWorkflowResponsePrompts(value: unknown) {
  if (!value) {
    return [];
  }

  let parsedValue = value;

  if (typeof parsedValue === "string") {
    try {
      parsedValue = JSON.parse(parsedValue);
    } catch {
      return [];
    }
  }

  const rawItems =
    parsedValue &&
    typeof parsedValue === "object" &&
    Array.isArray((parsedValue as { responsePrompts?: unknown[] }).responsePrompts)
      ? (parsedValue as { responsePrompts: unknown[] }).responsePrompts
      : [];

  return rawItems
    .map((item, index) => {
      if (!item || typeof item !== "object") {
        return null;
      }

      const question = String((item as { question?: string }).question ?? "").trim();

      if (!question) {
        return null;
      }

      return {
        id: String((item as { id?: string }).id ?? `prompt_${index + 1}`),
        question,
        targetType:
          String((item as { targetType?: string }).targetType ?? "client") === "sponsor"
            ? "sponsor"
            : String((item as { targetType?: string }).targetType ?? "client") === "internal"
              ? "internal"
              : "client",
      } as const;
    })
    .filter(
      (
        item,
      ): item is {
        id: string;
        question: string;
        targetType: "client" | "sponsor" | "internal";
      } => item !== null,
    );
}

function normalizeValue(fieldKey: string, dataType: string, value: string) {
  const trimmed = value.trim();

  if (fieldKey === "client_phone") {
    const normalized = trimmed.replace(/[^\d+]/g, "");

    return {
      raw: trimmed,
      normalizedJson: JSON.stringify({ value: normalized }),
      normalizedHash: hashString(normalized),
    };
  }

  if (dataType === "date") {
    const normalized = trimmed.includes("/")
      ? trimmed.split("/").reverse().join("-")
      : trimmed;

    return {
      raw: trimmed,
      normalizedJson: JSON.stringify({ value: normalized }),
      normalizedHash: hashString(normalized.toLowerCase()),
    };
  }

  if (fieldKey === "employer_name") {
    const normalized = trimmed
      .toLowerCase()
      .replace(/[.,]/g, " ")
      .replace(/\b(llc|inc|ltd|corp|corporation|company|co)\b/g, " ")
      .replace(/\s+/g, " ")
      .trim();

    return {
      raw: trimmed,
      normalizedJson: JSON.stringify({ value: normalized }),
      normalizedHash: hashString(normalized),
    };
  }

  if (fieldKey === "job_title") {
    const normalized = trimmed
      .replace(/^is\s+/i, "")
      .replace(/[.]+$/g, "")
      .toLowerCase()
      .replace(/\s+/g, " ")
      .trim();

    return {
      raw: trimmed,
      normalizedJson: JSON.stringify({ value: normalized }),
      normalizedHash: hashString(normalized),
    };
  }

  return {
    raw: trimmed,
    normalizedJson: JSON.stringify({ value: trimmed }),
    normalizedHash: hashString(trimmed.toLowerCase()),
  };
}

function normalizeCountryCode(value: string) {
  const trimmed = value.trim();
  if (!trimmed) {
    return null;
  }

  const normalized = trimmed
    .toLowerCase()
    .replace(/[^a-z]/g, "");

  if (!normalized) {
    return null;
  }

  if (normalized.length === 2) {
    return normalized.toUpperCase();
  }

  return countryCodeAliasMap[normalized] ?? null;
}

function parseJsonRecord(value: unknown) {
  if (!value) {
    return {} as Record<string, string>;
  }

  if (typeof value === "string") {
    return JSON.parse(value) as Record<string, string>;
  }

  if (typeof value === "object") {
    return value as Record<string, string>;
  }

  return {} as Record<string, string>;
}

function sanitizeExtractedValue(fieldKey: string, rawValue: string) {
  if (fieldKey === "passport_number") {
    return normalizePassportNumberCandidate(rawValue) ?? "";
  }

  if (fieldKey === "job_title" || fieldKey === "employer_name") {
    return rawValue.replace(/^is\s+/i, "").replace(/[.]+$/g, "").trim();
  }

  return rawValue.trim();
}

function isValidExtractedValue(fieldKey: string, rawValue: string) {
  const trimmed = rawValue.trim();

  if (fieldKey === "passport_number") {
    return isValidPassportNumberCandidate(trimmed);
  }

  if (fieldKey === "client_phone") {
    if (/^\d{4}-\d{2}-\d{2}$/.test(trimmed) || /^\d{2}\/\d{2}\/\d{4}$/.test(trimmed)) {
      return false;
    }

    const digits = trimmed.replace(/\D/g, "");
    return digits.length >= 10 && digits.length <= 15;
  }

  return Boolean(trimmed);
}

async function getDataFields() {
  return prisma.$queryRaw<DataField[]>`
    SELECT id, field_key, label, data_type
    FROM data_fields
    ORDER BY label
  `;
}

async function getCaseFacts(input: { clientId: string; caseId?: string | null }) {
  return prisma.$queryRaw<CaseFactRow[]>`
    SELECT id, data_field_id, raw_value, normalized_value_json, confidence_score, status_code, created_at
    FROM case_facts
    WHERE client_id = ${input.clientId}
      AND (${input.caseId ?? null} IS NULL OR case_id = ${input.caseId ?? null} OR case_id IS NULL)
      AND deleted_at IS NULL
      AND status_code <> 'rejected'
    ORDER BY created_at DESC
  `;
}

function buildFactReviewGuidanceLine(
  feedback: FactReviewFeedbackRow,
  scope: "local" | "global",
) {
  if (scope === "local") {
    const details = [
      feedback.previous_raw_value
        ? `previous value rejected: "${feedback.previous_raw_value}"`
        : null,
      feedback.corrected_raw_value
        ? `correct value: "${feedback.corrected_raw_value}"`
        : null,
      feedback.source_hint ? `look in: ${feedback.source_hint}` : null,
      feedback.search_hint ? `search for: ${feedback.search_hint}` : null,
      feedback.feedback_note ? `note: ${feedback.feedback_note}` : null,
    ].filter(Boolean);

    if (details.length === 0) {
      return null;
    }

    return `${feedback.label} (${feedback.field_key}) -> ${details.join("; ")}`;
  }

  const normalizedAction = normalizeStatusCode(feedback.review_action);
  const genericAction =
    normalizedAction === "correct"
      ? "prior human correction exists for this field; prefer primary evidence over weak matches"
      : normalizedAction === "reject"
        ? "prior human rejection exists for this field; avoid repeating low-quality extraction patterns"
        : normalizedAction === "research_hint"
          ? "apply prior human research guidance for this field"
          : "apply prior human review guidance for this field";
  const details = [
    genericAction,
    feedback.source_hint ? `look in: ${feedback.source_hint}` : null,
    feedback.search_hint ? `search for: ${feedback.search_hint}` : null,
    feedback.feedback_note ? `guidance: ${feedback.feedback_note}` : null,
  ].filter(Boolean);

  return `${feedback.label} (${feedback.field_key}) -> ${details.join("; ")}`;
}

async function loadFactReviewContext(input: {
  lawFirmId: string;
  clientId: string;
  caseId?: string | null;
}) {
  await ensureFactReviewFeedbackTable();

  const rows = await prisma.$queryRaw<FactReviewFeedbackRow[]>`
    SELECT
      frf.id,
      frf.client_id,
      frf.case_id,
      frf.case_fact_id,
      frf.data_field_id,
      df.field_key,
      df.label,
      frf.review_action,
      frf.previous_raw_value,
      frf.previous_normalized_hash,
      frf.corrected_raw_value,
      frf.corrected_normalized_hash,
      frf.source_hint,
      frf.search_hint,
      frf.feedback_note,
      frf.created_at
    FROM fact_review_feedback frf
    JOIN data_fields df ON df.id = frf.data_field_id
    WHERE frf.law_firm_id = ${input.lawFirmId}
      AND frf.deleted_at IS NULL
    ORDER BY frf.created_at DESC
    LIMIT 240
  `;

  const rejectedHashesByFieldId = new Map<string, Set<string>>();
  const localLatestByFieldKey = new Map<string, FactReviewFeedbackRow>();
  const globalLatestByFieldKey = new Map<string, FactReviewFeedbackRow>();

  for (const row of rows) {
    const isLocal =
      row.client_id === input.clientId &&
      (!row.case_id || !input.caseId || row.case_id === input.caseId);

    if (
      isLocal &&
      ["rejected", "corrected"].includes(normalizeStatusCode(row.review_action)) &&
      row.previous_normalized_hash
    ) {
      const current = rejectedHashesByFieldId.get(row.data_field_id) ?? new Set<string>();
      current.add(row.previous_normalized_hash);
      rejectedHashesByFieldId.set(row.data_field_id, current);
    }

    if (isLocal) {
      if (!localLatestByFieldKey.has(row.field_key)) {
        localLatestByFieldKey.set(row.field_key, row);
      }
      continue;
    }

    if (!globalLatestByFieldKey.has(row.field_key)) {
      globalLatestByFieldKey.set(row.field_key, row);
    }
  }

  const guidanceLines = [
    ...Array.from(localLatestByFieldKey.values())
      .map((row) => buildFactReviewGuidanceLine(row, "local"))
      .filter((row): row is string => Boolean(row)),
    ...Array.from(globalLatestByFieldKey.values())
      .map((row) => buildFactReviewGuidanceLine(row, "global"))
      .filter((row): row is string => Boolean(row)),
  ]
    .slice(0, 24);

  return {
    rejectedHashesByFieldId,
    guidanceLines,
  } satisfies FactReviewContext;
}

async function isRejectedFactValueByHuman(input: {
  lawFirmId: string;
  clientId: string;
  caseId?: string | null;
  dataFieldId: string;
  normalizedHash: string;
  reviewContext?: FactReviewContext | null;
}) {
  if (input.reviewContext) {
    return Boolean(input.reviewContext.rejectedHashesByFieldId.get(input.dataFieldId)?.has(input.normalizedHash));
  }

  await ensureFactReviewFeedbackTable();

  const [row] = await prisma.$queryRaw<Array<{ id: string }>>`
    SELECT id
    FROM fact_review_feedback
    WHERE law_firm_id = ${input.lawFirmId}
      AND client_id = ${input.clientId}
      AND data_field_id = ${input.dataFieldId}
      AND previous_normalized_hash = ${input.normalizedHash}
      AND review_action IN ('rejected', 'corrected')
      AND (
        case_id IS NULL
        OR ${input.caseId ?? null} IS NULL
        OR case_id = ${input.caseId ?? null}
      )
      AND deleted_at IS NULL
    LIMIT 1
  `;

  return Boolean(row);
}

function buildHumanReviewEvidenceExcerpt(input: {
  action: string;
  previousRawValue?: string | null;
  correctedRawValue?: string | null;
  sourceHint?: string | null;
  searchHint?: string | null;
  feedbackNote?: string | null;
}) {
  const parts = [
    `Human review action: ${input.action}`,
    input.previousRawValue ? `previous value "${input.previousRawValue}"` : null,
    input.correctedRawValue ? `corrected to "${input.correctedRawValue}"` : null,
    input.sourceHint ? `source hint: ${input.sourceHint}` : null,
    input.searchHint ? `search hint: ${input.searchHint}` : null,
    input.feedbackNote ? `note: ${input.feedbackNote}` : null,
  ].filter(Boolean);

  return parts.join(" ").slice(0, 255);
}

async function getWorkflowConditionFactMap(input: { clientId: string; caseId?: string | null }) {
  const facts = await prisma.$queryRaw<
    Array<{
      field_key: string;
      raw_value: string | null;
      normalized_value_json: unknown;
    }>
  >`
    SELECT df.field_key, cf.raw_value, cf.normalized_value_json
    FROM case_facts cf
    JOIN data_fields df ON df.id = cf.data_field_id
    WHERE cf.client_id = ${input.clientId}
      AND (${input.caseId ?? null} IS NULL OR cf.case_id = ${input.caseId ?? null} OR cf.case_id IS NULL)
      AND cf.deleted_at IS NULL
      AND cf.status_code <> 'rejected'
    ORDER BY cf.created_at DESC
  `;

  const factMap = new Map<string, string[]>();

  for (const fact of facts) {
    const values: string[] = [];

    if (typeof fact.raw_value === "string" && fact.raw_value.trim()) {
      values.push(fact.raw_value.trim());
    }

    let normalizedValue = fact.normalized_value_json;

    if (typeof normalizedValue === "string") {
      try {
        normalizedValue = JSON.parse(normalizedValue);
      } catch {
        normalizedValue = null;
      }
    }

    if (
      normalizedValue &&
      typeof normalizedValue === "object" &&
      !Array.isArray(normalizedValue)
    ) {
      const candidateValue = (normalizedValue as { value?: unknown }).value;

      if (candidateValue !== undefined && candidateValue !== null) {
        values.push(String(candidateValue));
      }
    }

    if (!values.length) {
      continue;
    }

    const currentValues = factMap.get(fact.field_key) ?? [];
    factMap.set(fact.field_key, [...currentValues, ...values]);
  }

  return factMap;
}

async function getBestFactCoverage(input: {
  clientId: string;
  caseId?: string | null;
  dataFieldId: string;
  threshold: number;
}) {
  const facts = await prisma.$queryRaw<CaseFactRow[]>`
    SELECT id, data_field_id, raw_value, normalized_value_json, confidence_score, status_code, created_at
    FROM case_facts
    WHERE client_id = ${input.clientId}
      AND data_field_id = ${input.dataFieldId}
      AND (${input.caseId ?? null} IS NULL OR case_id = ${input.caseId ?? null} OR case_id IS NULL)
      AND deleted_at IS NULL
      AND status_code <> 'rejected'
    ORDER BY confidence_score DESC, created_at DESC
  `;

  if (facts.length === 0) {
    return {
      status: "missing",
      currentConfidence: null,
      chosenFactId: null,
      reasoning: "No fact found for this field.",
    } satisfies CoverageResult;
  }

  const uniqueValues = new Set(
    facts
      .map((fact) => fact.normalized_value_json ?? fact.raw_value ?? "")
      .filter(Boolean),
  );

  const confirmedFact = facts.find((fact) => fact.status_code === "confirmed");
  const bestFact = confirmedFact ?? facts[0];

  if (uniqueValues.size > 1 && !confirmedFact) {
    return {
      status: "conflicting",
      currentConfidence: Number(bestFact.confidence_score),
      chosenFactId: bestFact.id,
      reasoning: "Multiple conflicting values were found across sources.",
    } satisfies CoverageResult;
  }

  if (Number(bestFact.confidence_score) < input.threshold) {
    return {
      status: "low_confidence",
      currentConfidence: Number(bestFact.confidence_score),
      chosenFactId: bestFact.id,
      reasoning: "A value exists but confidence is below the threshold.",
    } satisfies CoverageResult;
  }

  return {
    status: "covered",
    currentConfidence: Number(bestFact.confidence_score),
    chosenFactId: bestFact.id,
    reasoning: confirmedFact
      ? "Field is already human-confirmed."
      : "Field is covered by existing sources with sufficient confidence.",
  } satisfies CoverageResult;
}

type GapAnalysisCaseForm = {
  id: string;
  form_template_id: string;
  requirement_name: string;
  form_name: string;
  form_code: string | null;
  effective_form_template_id: string;
  effective_form_name: string;
  effective_form_code: string | null;
};

function inferSpecializedFormCodeFromCaseContext(value: string | null | undefined) {
  const normalized = normalizeKnowledgeSearchText(value);

  if (
    normalized.includes("i-485") ||
    normalized.includes("form i 485") ||
    normalized.includes("adjustment of status")
  ) {
    return "uscis_i_485";
  }

  return null;
}

async function resolveEffectiveGapAnalysisCaseForms(input: {
  lawFirmId: string;
  caseTitle: string | null;
  caseForms: Array<{
    id: string;
    form_template_id: string;
    requirement_name: string;
    form_name: string;
    form_code: string | null;
  }>;
}) {
  const templateIds = Array.from(new Set(input.caseForms.map((item) => item.form_template_id)));
  const templateRows =
    templateIds.length > 0
      ? await prisma.$queryRaw<
          Array<{ id: string; code: string | null; name: string; field_count: number | bigint }>
        >`
          SELECT
            ft.id,
            ft.code,
            ft.name,
            CAST(COUNT(ff.id) AS SIGNED) AS field_count
          FROM form_templates ft
          LEFT JOIN form_fields ff ON ff.form_template_id = ft.id
          WHERE ft.id IN (${Prisma.join(templateIds)})
          GROUP BY ft.id, ft.code, ft.name
        `
      : [];

  const templateMetaById = new Map(
    templateRows.map((row) => [
      row.id,
      {
        code: row.code,
        name: row.name,
        fieldCount: Number(row.field_count ?? 0),
      },
    ]),
  );

  const fallbackCode = inferSpecializedFormCodeFromCaseContext(input.caseTitle);
  let fallbackTemplate:
    | { id: string; code: string | null; name: string }
    | null = null;

  if (fallbackCode) {
    const [resolved] = await prisma.$queryRaw<Array<{ id: string; code: string | null; name: string }>>`
      SELECT id, code, name
      FROM form_templates
      WHERE code = ${fallbackCode}
        AND (law_firm_id = ${input.lawFirmId} OR law_firm_id IS NULL)
      ORDER BY CASE WHEN law_firm_id = ${input.lawFirmId} THEN 0 ELSE 1 END, updated_at DESC
      LIMIT 1
    `;

    fallbackTemplate = resolved ?? null;
  }

  return input.caseForms.map((item) => {
    const currentTemplate = templateMetaById.get(item.form_template_id);
    const shouldUseFallback =
      Boolean(fallbackTemplate) && Number(currentTemplate?.fieldCount ?? 0) === 0;

    if (!shouldUseFallback || !fallbackTemplate) {
      return {
        ...item,
        effective_form_template_id: item.form_template_id,
        effective_form_name: item.form_name,
        effective_form_code: item.form_code,
      } satisfies GapAnalysisCaseForm;
    }

    return {
      ...item,
      effective_form_template_id: fallbackTemplate.id,
      effective_form_name: fallbackTemplate.name,
      effective_form_code: fallbackTemplate.code,
    } satisfies GapAnalysisCaseForm;
  });
}

function inferI485DataFieldKey(field: {
  field_key: string;
  label: string;
  pdf_field_name: string;
  section_name: string | null;
}) {
  const haystack = normalizeKnowledgeSearchText(
    [field.field_key, field.label, field.pdf_field_name, field.section_name]
      .filter(Boolean)
      .join(" "),
  ).replace(/[^\p{L}\p{N}\s]/gu, " ");

  const includesAny = (...tokens: string[]) =>
    tokens.some((token) => haystack.includes(normalizeKnowledgeSearchText(token)));
  const includesAll = (...tokens: string[]) =>
    tokens.every((token) => haystack.includes(normalizeKnowledgeSearchText(token)));

  if (
    includesAny(
      "otherdob",
      "other date of birth",
      "other a number",
      "pt1line5a",
      "pt1line5b",
      "prior address",
      "recentdatefrom",
      "recentdateto",
      "priordatefrom",
      "priordateto",
      "line11 admitted",
      "line11 paroled",
      "line11 other",
      "consent",
      "ssa yn",
      "signature",
      "interpreter",
      "preparer",
      "attorney",
      "uscis online account",
      "safe or alternate mailing address",
    )
  ) {
    return null;
  }

  if (includesAny("email")) return "client_email";
  if (includesAny("mobile telephone", "daytime telephone", "telephone number", "phone")) {
    return "client_phone";
  }

  if (includesAny("pt1line19 ssn", "social security number", " ssn ")) return "client_ssn";

  if (includesAny("passportnum", "passport or travel document number")) {
    return "passport_number";
  }

  if (
    includesAny("country that issued this passport", "passport issuing country") ||
    (includesAny("passport") && includesAny("issued this passport"))
  ) {
    return "client_passport_issuing_country";
  }

  if (
    includesAny("expiration date of this passport", "passport expiration") ||
    (includesAny("passport") && includesAny("expdate"))
  ) {
    return "client_passport_expiration_date";
  }

  if (
    includesAny("aliennumber", "alien registration number") &&
    !includesAny("pt1line4 yn", "pt1line5")
  ) {
    return "client_alien_number";
  }

  if (includesAny("familyname", "family name", "last name") && !includesAny("spouse", "parent")) {
    return "client_last_name";
  }

  if (includesAny("givenname", "given name", "first name") && !includesAny("spouse", "parent")) {
    return "client_first_name";
  }

  if (includesAny("middlename", "middle name") && !includesAny("spouse", "parent")) {
    return "client_middle_name";
  }

  if (
    includesAny("date of birth", " pt1line3 dob ", "principal applicant s date of birth") &&
    !includesAny("spouse", "parent", "child")
  ) {
    return "client_date_of_birth";
  }

  if (includesAny("citytownofbirth", "city town of birth")) return "client_birth_city";
  if (includesAny("countryofbirth", "country of birth")) return "client_birth_country";
  if (includesAny("countryofcitizenshipnationality", "country of citizenship or nationality")) {
    return "client_country_of_citizenship";
  }

  if (includesAny("dateofarrival", "date of last arrival")) return "client_last_arrival_date";
  if (includesAny(" i94 ", "arrival departure record number")) return "client_i94_number";
  if (includesAny("authorized stay", "pt1line12 date")) return "client_authorized_stay_until";
  if (includesAny("status on form i 94", "class of admission")) return "client_class_of_admission";
  if (includesAny("current immigration status")) return "client_current_immigration_status";

  if (
    includesAny("streetnumbername", "street number and name") &&
    !includesAny("recent", "prior", "outside the united states")
  ) {
    return "client_current_address_line1";
  }

  if (
    includesAny("currentcityortown", " city or town ") &&
    !includesAny("recent", "prior", "birth", "outside the united states", "place and date of last arrival")
  ) {
    return "client_current_city";
  }

  if (includesAny("currentstate", " state ") && !includesAny("recent", "prior", "place and date of last arrival")) {
    return "client_current_state";
  }

  if (includesAny("currentzipcode", " zip code ") && !includesAny("recent", "prior")) {
    return "client_current_zip_code";
  }

  if (includesAny("recentstreetname", "recentnumber")) return "client_foreign_address_line1";
  if (includesAny("recentcity")) return "client_foreign_city";
  if (includesAny("recentprovince")) return "client_foreign_province";
  if (includesAny("recentpostalcode", "recentzipcode")) return "client_foreign_postal_code";
  if (includesAny("recentcountry")) return "client_foreign_country";

  if (includesAll("height", "feet")) return "height_feet";
  if (includesAll("height", "inches")) return "height_inches";
  if (includesAny("weight", "pounds")) return "weight_pounds";
  if (includesAny("eye color")) return "eye_color";
  if (includesAny("hair color")) return "hair_color";

  if (includesAny("current spouse") && includesAny("name")) return "spouse_full_name";
  if (includesAny("current spouse") && includesAny("date of birth")) return "spouse_date_of_birth";
  if (includesAny("current spouse") && includesAny("country of birth")) {
    return "spouse_country_of_birth";
  }

  if (includesAny("marriage") && includesAny("date")) return "marriage_date";
  if (includesAny("marriage") && includesAny("country")) return "marriage_country";

  if (includesAny("mother") && includesAny("name")) return "parent_1_full_name";
  if (includesAny("mother") && includesAny("country of birth")) return "parent_1_country_of_birth";
  if (includesAny("father") && includesAny("name")) return "parent_2_full_name";
  if (includesAny("father") && includesAny("country of birth")) return "parent_2_country_of_birth";

  return null;
}

function resolveGapAnalysisDataField(input: {
  formCode: string | null;
  field: {
    field_key: string;
    label: string;
    pdf_field_name: string;
    section_name: string | null;
  };
  mapping: { data_field_id: string } | undefined;
  dataFieldById: Map<string, DataField>;
  dataFieldByKey: Map<string, DataField>;
}) {
  if (input.mapping?.data_field_id) {
    const mappedField = input.dataFieldById.get(input.mapping.data_field_id);
    if (mappedField) {
      return mappedField;
    }
  }

  const directField = input.dataFieldByKey.get(input.field.field_key);
  if (directField) {
    return directField;
  }

  if (input.formCode === "uscis_i_485") {
    const inferredKey = inferI485DataFieldKey(input.field);
    if (inferredKey) {
      return input.dataFieldByKey.get(inferredKey) ?? null;
    }
  }

  return null;
}

async function getFactAuditReference(caseFactId: string) {
  const sources = await prisma.$queryRaw<
    Array<{
      source_type: string;
      evidence_excerpt: string | null;
      repository_subject: string | null;
      document_title: string | null;
      extracted_document_title: string | null;
    }>
  >`
    SELECT
      fs.source_type,
      fs.evidence_excerpt,
      ri.subject AS repository_subject,
      dr.title AS document_title,
      drecord.title AS extracted_document_title
    FROM fact_sources fs
    LEFT JOIN repository_items ri ON ri.id = fs.repository_item_id
    LEFT JOIN document_records dr
      ON fs.source_type = 'document_record'
      AND dr.id = fs.source_id
    LEFT JOIN document_extractions de
      ON fs.source_type = 'document_extraction'
      AND de.id = fs.source_id
    LEFT JOIN document_records drecord
      ON drecord.id = de.document_record_id
    WHERE fs.case_fact_id = ${caseFactId}
    ORDER BY fs.source_rank ASC, fs.created_at ASC
    LIMIT 6
  `;

  const sourceTitles = Array.from(
    new Set(
      sources
        .map((source) =>
          normalizeConsolidatedKnowledgeText(
            source.document_title ||
              source.extracted_document_title ||
              source.repository_subject ||
              source.source_type,
          ),
        )
        .filter(Boolean),
    ),
  ).slice(0, 6);

  const evidenceExcerpt =
    sources
      .map((source) => normalizeConsolidatedKnowledgeText(source.evidence_excerpt))
      .find(Boolean) || null;

  return {
    sourceTitles,
    evidenceExcerpt,
  };
}

function extractCandidatesFromText(input: {
  bodyText: string;
  dataFields: DataField[];
  confidence: number;
  sourceType: string;
  sourceId: string;
  repositoryItemId?: string | null;
  aiRunId?: string | null;
}) {
  const candidates: Array<{
    dataField: DataField;
    rawValue: string;
    confidence: number;
    sourceType: string;
    sourceId: string;
    repositoryItemId?: string | null;
    aiRunId?: string | null;
    evidenceExcerpt: string;
  }> = [];

  for (const dataField of input.dataFields) {
    const patterns = textExtractionPatterns[dataField.field_key];
    if (!patterns) continue;

    for (const pattern of patterns) {
      const match = input.bodyText.match(pattern);
      if (!match?.[1] && !match?.[0]) continue;

      const sanitizedRawValue = sanitizeExtractedValue(
        dataField.field_key,
        match[1] ?? match[0],
      );

      if (!isValidExtractedValue(dataField.field_key, sanitizedRawValue)) {
        continue;
      }

      candidates.push({
        dataField,
        rawValue: sanitizedRawValue,
        confidence: input.confidence,
        sourceType: input.sourceType,
        sourceId: input.sourceId,
        repositoryItemId: input.repositoryItemId ?? null,
        aiRunId: input.aiRunId ?? null,
        evidenceExcerpt: match[0].slice(0, 255),
      });
      break;
    }
  }

  return candidates;
}

function buildPassportReaderSummaryLine(result: PassportNumberAgentResult) {
  if (result.accepted && result.passportNumber) {
    return `Specialized passport reader confirmed passport number ${result.passportNumber}.`;
  }

  return "Specialized passport reader could not confirm the passport number with sufficient confidence.";
}

function buildPassportReaderImportantFact(
  result: PassportNumberAgentResult,
): DetailedDocumentImportantFact {
  return {
    label: "Passport number reading",
    value: result.passportNumber ?? "uncertain",
    confidence: result.accepted ? Math.max(result.confidenceScore, 0.9) : result.confidenceScore,
    evidence:
      result.rationale ??
      (result.accepted && result.passportNumber
        ? `Specialized passport reader selected ${result.passportNumber}.`
        : "Specialized passport reader returned an uncertain result."),
  };
}

async function maybeExtractPassportNumberWithSpecialist(input: {
  lawFirmId: string;
  documentTypeCode: string;
  title: string;
  originalFileName: string;
  mimeType: string;
  textContent: string;
}) {
  if (
    !isPassportReaderCandidateDocument({
      documentTypeCode: input.documentTypeCode,
      title: input.title,
      originalFileName: input.originalFileName,
      textContent: input.textContent,
    })
  ) {
    return null;
  }

  try {
    const result = await extractPassportNumberWithAgent({
      lawFirmId: input.lawFirmId,
      documentTypeCode: input.documentTypeCode,
      title: input.title,
      originalFileName: input.originalFileName,
      mimeType: input.mimeType,
      textContent: input.textContent,
    });

    return {
      ...result,
      summaryLine: buildPassportReaderSummaryLine(result),
      importantFact: buildPassportReaderImportantFact(result),
    };
  } catch {
    return null;
  }
}

async function insertCaseFact(input: {
  lawFirmId: string;
  clientId: string;
  caseId?: string | null;
  dataField: DataField;
  rawValue: string;
  confidence: number;
  aiRunId?: string | null;
  sourceContextCode: string;
  requiresHumanConfirmation?: boolean;
  reviewContext?: FactReviewContext | null;
}) {
  const normalized = normalizeValue(
    input.dataField.field_key,
    input.dataField.data_type,
    input.rawValue,
  );

  if (
    await isRejectedFactValueByHuman({
      lawFirmId: input.lawFirmId,
      clientId: input.clientId,
      caseId: input.caseId ?? null,
      dataFieldId: input.dataField.id,
      normalizedHash: normalized.normalizedHash,
      reviewContext: input.reviewContext ?? null,
    })
  ) {
    return null;
  }

  const id = createId();

  const [existing] = await prisma.$queryRaw<Array<{ id: string }>>`
    SELECT id
    FROM case_facts
    WHERE client_id = ${input.clientId}
      AND data_field_id = ${input.dataField.id}
      AND normalized_value_hash = ${normalized.normalizedHash}
      AND (${input.caseId ?? null} IS NULL OR case_id = ${input.caseId ?? null} OR case_id IS NULL)
      AND deleted_at IS NULL
      AND status_code <> 'rejected'
    LIMIT 1
  `;

  if (existing) {
    return existing.id;
  }

  await prisma.$executeRaw`
    INSERT INTO case_facts (
      id, law_firm_id, client_id, case_id, related_party_id, data_field_id,
      source_context_code, raw_value, normalized_value_json, normalized_value_hash,
      confidence_score, status_code, requires_human_confirmation, ai_run_id,
      created_at, updated_at
    ) VALUES (
      ${id},
      ${input.lawFirmId},
      ${input.clientId},
      ${input.caseId ?? null},
      NULL,
      ${input.dataField.id},
      ${input.sourceContextCode},
      ${normalized.raw},
      ${normalized.normalizedJson},
      ${normalized.normalizedHash},
      ${input.confidence},
      ${input.confidence >= 0.99 ? "confirmed" : "proposed"},
      ${input.requiresHumanConfirmation ? 1 : 0},
      ${input.aiRunId ?? null},
      CURRENT_TIMESTAMP,
      CURRENT_TIMESTAMP
    )
  `;

  return id;
}

async function insertFactSource(input: {
  caseFactId: string;
  sourceType: string;
  sourceId?: string | null;
  repositoryItemId?: string | null;
  aiRunId?: string | null;
  evidenceExcerpt?: string | null;
  confidenceContribution?: number | null;
}) {
  await prisma.$executeRaw`
    INSERT INTO fact_sources (
      id, case_fact_id, source_type, source_id, repository_item_id, ai_run_id,
      source_rank, field_path, page_number, evidence_excerpt, confidence_contribution, created_at
    ) VALUES (
      ${createId()},
      ${input.caseFactId},
      ${input.sourceType},
      ${input.sourceId ?? null},
      ${input.repositoryItemId ?? null},
      ${input.aiRunId ?? null},
      1,
      NULL,
      NULL,
      ${input.evidenceExcerpt ?? null},
      ${input.confidenceContribution ?? null},
      CURRENT_TIMESTAMP
    )
  `;
}

async function syncClientStructuredData(input: {
  clientId: string;
  fieldKey: string;
  rawValue: string;
}) {
  const column = clientFieldColumnMap[input.fieldKey];
  if (!column) return;

  if (column === "date_of_birth") {
    const normalized = normalizeValue(input.fieldKey, "date", input.rawValue).raw;
    await prisma.$executeRawUnsafe(
      `UPDATE clients SET ${column} = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`,
      normalized,
      input.clientId,
    );
    return;
  }

  if (column === "country_of_citizenship" || column === "country_of_birth") {
    const normalizedCountryCode = normalizeCountryCode(input.rawValue);
    if (!normalizedCountryCode) {
      return;
    }

    await prisma.$executeRawUnsafe(
      `UPDATE clients SET ${column} = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`,
      normalizedCountryCode,
      input.clientId,
    );
    return;
  }

  await prisma.$executeRawUnsafe(
    `UPDATE clients SET ${column} = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`,
    input.rawValue,
    input.clientId,
  );
}

async function syncClientStructuredDataFromExtraction(input: {
  clientId: string;
  extractedData: Record<string, string>;
}) {
  const syncedFieldKeys: string[] = [];

  for (const [fieldKey, rawValue] of Object.entries(input.extractedData)) {
    const normalizedValue = String(rawValue ?? "").trim();
    if (!normalizedValue) {
      continue;
    }

    await syncClientStructuredData({
      clientId: input.clientId,
      fieldKey,
      rawValue: normalizedValue,
    });

    if (clientFieldColumnMap[fieldKey]) {
      syncedFieldKeys.push(fieldKey);
    }
  }

  return Array.from(new Set(syncedFieldKeys));
}

async function generateDocumentExtraction(input: {
  lawFirmId: string;
  clientId: string;
  caseId?: string | null;
  documentRecordId: string;
  repositoryItemId: string;
  title?: string | null;
  documentTypeCode?: string | null;
  originalFileName?: string | null;
  mimeType?: string | null;
  textContent: string;
}) {
  const aiRun = await createAiRun({
    lawFirmId: input.lawFirmId,
    caseId: input.caseId ?? null,
    clientId: input.clientId,
    runType: "extraction",
  });

  const dataFields = await getDataFields();
  const fieldByKey = new Map(dataFields.map((field) => [field.field_key, field]));
  const legacyCaseData = parseLegacyEmploymentAdjustmentCaseData(input.textContent);
  const legacyCandidates: Array<{
    dataField: DataField;
    rawValue: string;
    confidence: number;
    sourceType: string;
    sourceId: string;
    repositoryItemId?: string | null;
    aiRunId?: string | null;
    evidenceExcerpt: string;
  }> =
    legacyCaseData ?
      buildLegacyEmploymentAdjustmentFactCandidates(legacyCaseData).reduce<
        Array<{
          dataField: DataField;
          rawValue: string;
          confidence: number;
          sourceType: string;
          sourceId: string;
          repositoryItemId?: string | null;
          aiRunId?: string | null;
          evidenceExcerpt: string;
        }>
      >((accumulator, candidate) => {
        const dataField = fieldByKey.get(candidate.fieldKey);
        if (!dataField) {
          return accumulator;
        }

        accumulator.push({
          dataField,
          rawValue: candidate.rawValue,
          confidence: candidate.confidence,
          sourceType: "legacy_case_export",
          sourceId: input.documentRecordId,
          repositoryItemId: input.repositoryItemId,
          aiRunId: aiRun.id,
          evidenceExcerpt: candidate.evidenceExcerpt,
        });

        return accumulator;
      }, [])
    : [];
  const candidates =
    legacyCaseData ?
      legacyCandidates
    : extractCandidatesFromText({
        bodyText: input.textContent,
        dataFields,
        confidence: 0.86,
        sourceType: "document_record",
        sourceId: input.documentRecordId,
        repositoryItemId: input.repositoryItemId,
        aiRunId: aiRun.id,
      });

  const extractedData = Object.fromEntries(
    candidates.map((candidate) => [candidate.dataField.field_key, candidate.rawValue]),
  );
  const passportReaderResult = await maybeExtractPassportNumberWithSpecialist({
    lawFirmId: input.lawFirmId,
    documentTypeCode: input.documentTypeCode ?? "",
    title: input.title ?? "",
    originalFileName: input.originalFileName ?? "",
    mimeType: input.mimeType ?? "",
    textContent: input.textContent,
  });

  if (passportReaderResult) {
    if (passportReaderResult.accepted && passportReaderResult.passportNumber) {
      extractedData.passport_number = passportReaderResult.passportNumber;
    } else {
      delete extractedData.passport_number;
    }
  }

  const extractionId = createId();
  const extractedFieldCount = Object.keys(extractedData).length;
  const extractorName =
    passportReaderResult ? "rules_engine+passport_number_agent" : "rules_engine";
  const confidenceScore =
    passportReaderResult?.accepted
      ? Math.max(0.9000, passportReaderResult.confidenceScore)
      : extractedFieldCount > 0
        ? 0.8600
        : 0.2000;
  await prisma.$executeRaw`
    INSERT INTO document_extractions (
      id, law_firm_id, document_record_id, ai_run_id, extraction_version, status,
      extractor_name, extracted_data_json, confidence_score, raw_text, created_at, updated_at
    ) VALUES (
      ${extractionId},
      ${input.lawFirmId},
      ${input.documentRecordId},
      ${aiRun.id},
      1,
      'completed',
      ${extractorName},
      ${JSON.stringify(extractedData)},
      ${confidenceScore},
      ${input.textContent},
      CURRENT_TIMESTAMP,
      CURRENT_TIMESTAMP
    )
  `;

  await finishAiRun({
    aiRunId: aiRun.id,
    status: "completed",
    inputTokens:
      (passportReaderResult?.usage.inputTokens ?? 0) || Math.ceil(input.textContent.length / 4),
    outputTokens:
      (passportReaderResult?.usage.outputTokens ?? 0) + Math.max(extractedFieldCount * 10, 1),
    estimatedCost: 0,
  });

  return {
    aiRunId: aiRun.id,
    extractionId,
    extractedData,
  };
}

async function generateDetailedDocumentExtractionWithAi(input: {
  lawFirmId: string;
  clientId: string;
  caseId?: string | null;
  documentRecordId: string;
  repositoryItemId: string | null;
  title: string;
  documentTypeCode: string;
  originalFileName: string;
  mimeType: string;
  textContent: string;
}) {
  const aiRun = await createAiRun({
    lawFirmId: input.lawFirmId,
    caseId: input.caseId ?? null,
    clientId: input.clientId,
    runType: "extraction",
  });

  try {
    const dataFields = await getDataFields();
    const factReviewContext = await loadFactReviewContext({
      lawFirmId: input.lawFirmId,
      clientId: input.clientId,
      caseId: input.caseId ?? null,
    });
    const completion = await runJsonChatCompletion({
      lawFirmId: input.lawFirmId,
      systemPrompt: [
        "You are an expert immigration document extraction agent.",
        "Read a single client document and extract only values directly supported by the evidence.",
        "Never invent, infer, or copy labels/headings as if they were answers.",
        "Return JSON only with this exact shape:",
        '{"summary":"string","dataFieldValues":[{"fieldKey":"string","value":"string","confidence":0.0,"evidence":"string"}],"importantFacts":[{"label":"string","value":"string","confidence":0.0,"evidence":"string"}]}',
        "Use dataFieldValues only for fieldKey values from the allowed field list.",
        "importantFacts may contain extra facts useful for future USCIS or immigration forms even if they do not map directly to a structured field.",
        "If a value is missing or uncertain, omit it.",
        "Prefer YYYY-MM-DD only when the document gives a clear full date.",
        "Keep summary concise and factual.",
      ].join("\n"),
      userPrompt: JSON.stringify({
        document: {
          title: input.title,
          documentTypeCode: input.documentTypeCode,
          originalFileName: input.originalFileName,
          mimeType: input.mimeType,
        },
        allowedDataFields: dataFields.map((field) => ({
          fieldKey: field.field_key,
          label: field.label,
          dataType: field.data_type,
        })),
        humanReviewGuidance: factReviewContext.guidanceLines,
        textContent: limitText(input.textContent, 22000),
      }),
      maxCompletionTokens: 2600,
    });

    const fieldByKey = new Map(dataFields.map((field) => [field.field_key, field]));
    const aiEntries = Array.isArray(completion.json.dataFieldValues)
      ? completion.json.dataFieldValues
      : [];
    const aiExtractedData = new Map<string, string>();

    for (const entry of aiEntries) {
      if (!entry || typeof entry !== "object") {
        continue;
      }

      const fieldKey = String((entry as { fieldKey?: unknown }).fieldKey ?? "").trim();
      const dataField = fieldByKey.get(fieldKey);
      const normalizedValue = normalizeAiAnswerValue((entry as { value?: unknown }).value);

      if (!dataField || !normalizedValue) {
        continue;
      }

      const sanitizedValue = sanitizeExtractedValue(fieldKey, normalizedValue);
      if (!isValidExtractedValue(fieldKey, sanitizedValue)) {
        continue;
      }

      if (!aiExtractedData.has(fieldKey)) {
        aiExtractedData.set(fieldKey, sanitizedValue);
      }
    }

    const legacyCaseData = parseLegacyEmploymentAdjustmentCaseData(input.textContent);
    if (legacyCaseData) {
      for (const candidate of buildLegacyEmploymentAdjustmentFactCandidates(legacyCaseData)) {
        if (fieldByKey.has(candidate.fieldKey) && !aiExtractedData.has(candidate.fieldKey)) {
          aiExtractedData.set(candidate.fieldKey, candidate.rawValue);
        }
      }
    }

    const fallbackCandidates = extractCandidatesFromText({
      bodyText: input.textContent,
      dataFields,
      confidence: 0.78,
      sourceType: "document_record",
      sourceId: input.documentRecordId,
      repositoryItemId: input.repositoryItemId ?? undefined,
      aiRunId: aiRun.id,
    });

    for (const candidate of fallbackCandidates) {
      if (!aiExtractedData.has(candidate.dataField.field_key)) {
        aiExtractedData.set(candidate.dataField.field_key, candidate.rawValue);
      }
    }

    const passportReaderResult = await maybeExtractPassportNumberWithSpecialist({
      lawFirmId: input.lawFirmId,
      documentTypeCode: input.documentTypeCode,
      title: input.title,
      originalFileName: input.originalFileName,
      mimeType: input.mimeType,
      textContent: input.textContent,
    });

    if (passportReaderResult) {
      if (passportReaderResult.accepted && passportReaderResult.passportNumber) {
        aiExtractedData.set("passport_number", passportReaderResult.passportNumber);
      } else {
        aiExtractedData.delete("passport_number");
      }
    }

    const extractedData = Object.fromEntries(aiExtractedData.entries());
    const importantFacts = Array.isArray(completion.json.importantFacts)
      ? completion.json.importantFacts
          .map((entry) => {
            if (!entry || typeof entry !== "object") {
              return null;
            }

            const label = normalizeDetailedDocumentFactLabel((entry as { label?: unknown }).label);
            const value = normalizeAiAnswerValue((entry as { value?: unknown }).value);

            if (!label || !value) {
              return null;
            }

            return {
              label,
              value,
              confidence: normalizeDetailedDocumentConfidence(
                (entry as { confidence?: unknown }).confidence,
                0.84,
              ),
              evidence:
                normalizeAiAnswerValue((entry as { evidence?: unknown }).evidence) ?? value,
            } satisfies DetailedDocumentImportantFact;
          })
          .filter((entry): entry is DetailedDocumentImportantFact => Boolean(entry))
          .slice(0, 24)
      : [];

    if (passportReaderResult?.importantFact) {
      importantFacts.unshift(passportReaderResult.importantFact);
    }
    const normalizedImportantFacts = importantFacts.slice(0, 24);

    const extractionId = createId();
    const confidenceScore =
      passportReaderResult?.accepted
        ? Math.max(0.9400, passportReaderResult.confidenceScore)
      : aiEntries.length || normalizedImportantFacts.length
        ? 0.9200
        : fallbackCandidates.length
          ? 0.7800
          : 0.2200;
    const extractorName =
      passportReaderResult ? "ai_detailed_reprocess+passport_number_agent" : "ai_detailed_reprocess";
    const summaryTextBase =
      normalizeDetailedDocumentSummary(completion.json.summary) ||
      `Extracted ${Object.keys(extractedData).length} structured field(s) from the document.`;
    const summaryText = passportReaderResult?.summaryLine
      ? `${summaryTextBase} ${passportReaderResult.summaryLine}`.trim()
      : summaryTextBase;

    await prisma.$executeRaw`
      INSERT INTO document_extractions (
        id, law_firm_id, document_record_id, ai_run_id, extraction_version, status,
        extractor_name, extracted_data_json, confidence_score, raw_text, created_at, updated_at
      ) VALUES (
        ${extractionId},
        ${input.lawFirmId},
        ${input.documentRecordId},
        ${aiRun.id},
        1,
        'completed',
        ${extractorName},
        ${JSON.stringify(extractedData)},
        ${confidenceScore},
        ${input.textContent},
        CURRENT_TIMESTAMP,
        CURRENT_TIMESTAMP
      )
    `;

    await finishAiRun({
      aiRunId: aiRun.id,
      status: "completed",
      inputTokens: completion.usage.inputTokens + (passportReaderResult?.usage.inputTokens ?? 0),
      outputTokens: completion.usage.outputTokens + (passportReaderResult?.usage.outputTokens ?? 0),
    });

    return {
      aiRunId: aiRun.id,
      extractionId,
      extractedData,
      summaryText,
      importantFacts: normalizedImportantFacts,
    };
  } catch (error) {
    await finishAiRun({
      aiRunId: aiRun.id,
      status: "failed",
      errorMessage:
        error instanceof Error ? error.message : "AI document reprocessing failed",
    });
    throw error;
  }
}

export async function consolidateCaseFacts(input: {
  lawFirmId: string;
  caseId: string;
  actorUserId: string;
}) {
  const [caseRow] = await prisma.$queryRaw<
    Array<{
      id: string;
      client_id: string;
      case_number: string;
    }>
  >`
    SELECT id, client_id, case_number
    FROM cases
    WHERE id = ${input.caseId}
      AND law_firm_id = ${input.lawFirmId}
      AND deleted_at IS NULL
    LIMIT 1
  `;

  if (!caseRow) {
    throw new Error("Case not found");
  }

  const aiRun = await createAiRun({
    lawFirmId: input.lawFirmId,
    caseId: caseRow.id,
    clientId: caseRow.client_id,
    runType: "fact_consolidation",
  });

  try {
    const dataFields = await getDataFields();
    const fieldByKey = new Map(dataFields.map((item) => [item.field_key, item]));
    const factReviewContext = await loadFactReviewContext({
      lawFirmId: input.lawFirmId,
      clientId: caseRow.client_id,
      caseId: input.caseId,
    });
    const [client] = await prisma.$queryRaw<
      Array<Record<string, string | null>>
    >`
      SELECT *
      FROM clients
      WHERE id = ${caseRow.client_id}
      LIMIT 1
    `;

    const consolidatedFactIds: string[] = [];

    const structuredCandidates: Array<{
      fieldKey: string;
      value: string;
      confidence: number;
      sourceType: string;
      evidence: string;
    }> = [];

    for (const [fieldKey, column] of Object.entries(clientFieldColumnMap)) {
      const value = client[column];
      if (!value) continue;
      structuredCandidates.push({
        fieldKey,
        value: String(value),
        confidence: 0.96,
        sourceType: "client_profile",
        evidence: String(value),
      });
    }

    if (client.first_name && client.last_name) {
      structuredCandidates.push({
        fieldKey: "client_full_name",
        value: `${client.first_name} ${client.last_name}`.trim(),
        confidence: 0.98,
        sourceType: "client_profile",
        evidence: `${client.first_name} ${client.last_name}`,
      });
    }

    const repositoryItems = await prisma.$queryRaw<
      Array<{
        id: string;
        body_text: string | null;
        item_type_code: string;
      }>
    >`
      SELECT id, body_text, item_type_code
      FROM repository_items
      WHERE law_firm_id = ${input.lawFirmId}
        AND (case_id = ${input.caseId} OR client_id = ${caseRow.client_id})
      ORDER BY occurred_at ASC
    `;

    const documentRows = await prisma.$queryRaw<
      Array<{
        id: string;
        title: string;
        extracted_text: string | null;
      }>
    >`
      SELECT id, title, extracted_text
      FROM document_records
      WHERE law_firm_id = ${input.lawFirmId}
        AND case_id = ${input.caseId}
      ORDER BY created_at ASC
    `;

    const documentExtractions = await prisma.$queryRaw<
      Array<{
        id: string;
        document_record_id: string;
        extracted_data_json: string;
        raw_text: string | null;
      }>
    >`
      SELECT de.id, de.document_record_id, de.extracted_data_json, de.raw_text
      FROM document_extractions de
      JOIN document_records dr ON dr.id = de.document_record_id
      WHERE dr.law_firm_id = ${input.lawFirmId}
        AND dr.case_id = ${input.caseId}
      ORDER BY de.created_at ASC
    `;
    const legacyArtifacts = extractLegacyEmploymentAdjustmentArtifactsFromDocuments(documentRows);
    const legacyFactCandidates =
      legacyArtifacts.caseData ? buildLegacyEmploymentAdjustmentFactCandidates(legacyArtifacts.caseData) : [];

    for (const candidate of structuredCandidates) {
      const dataField = fieldByKey.get(candidate.fieldKey);
      if (!dataField) continue;

      const factId = await insertCaseFact({
        lawFirmId: input.lawFirmId,
        clientId: caseRow.client_id,
        caseId: input.caseId,
        dataField,
        rawValue: candidate.value,
        confidence: candidate.confidence,
        aiRunId: aiRun.id,
        sourceContextCode: "client_profile",
        reviewContext: factReviewContext,
      });

      if (!factId) {
        continue;
      }

      await insertFactSource({
        caseFactId: factId,
        sourceType: candidate.sourceType,
        sourceId: caseRow.client_id,
        aiRunId: aiRun.id,
        evidenceExcerpt: candidate.evidence,
        confidenceContribution: candidate.confidence,
      });

      consolidatedFactIds.push(factId);
    }

    for (const candidate of legacyFactCandidates) {
      const dataField = fieldByKey.get(candidate.fieldKey);
      if (!dataField) continue;

      const factId = await insertCaseFact({
        lawFirmId: input.lawFirmId,
        clientId: caseRow.client_id,
        caseId: input.caseId,
        dataField,
        rawValue: candidate.rawValue,
        confidence: candidate.confidence,
        aiRunId: aiRun.id,
        sourceContextCode: "legacy_case_export",
        requiresHumanConfirmation: candidate.requiresHumanConfirmation ?? false,
        reviewContext: factReviewContext,
      });

      if (!factId) {
        continue;
      }

      await insertFactSource({
        caseFactId: factId,
        sourceType: "legacy_case_export",
        sourceId: null,
        aiRunId: aiRun.id,
        evidenceExcerpt: candidate.evidenceExcerpt,
        confidenceContribution: candidate.confidence,
      });

      consolidatedFactIds.push(factId);
    }

    for (const extraction of documentExtractions) {
      const parsed = parseJsonRecord(extraction.extracted_data_json);

      for (const [fieldKey, rawValue] of Object.entries(parsed)) {
        const dataField = fieldByKey.get(fieldKey);
        if (!dataField || !rawValue) continue;

        const factId = await insertCaseFact({
          lawFirmId: input.lawFirmId,
          clientId: caseRow.client_id,
          caseId: input.caseId,
          dataField,
          rawValue,
          confidence: 0.88,
          aiRunId: aiRun.id,
          sourceContextCode: "document_extraction",
          reviewContext: factReviewContext,
        });

        if (!factId) {
          continue;
        }

        await insertFactSource({
          caseFactId: factId,
          sourceType: "document_extraction",
          sourceId: extraction.id,
          aiRunId: aiRun.id,
          evidenceExcerpt: extraction.raw_text?.slice(0, 255) ?? rawValue,
          confidenceContribution: 0.88,
        });

        consolidatedFactIds.push(factId);
      }
    }

    for (const item of repositoryItems) {
      if (!item.body_text) continue;

      const candidates = extractCandidatesFromText({
        bodyText: item.body_text,
        dataFields,
        confidence: 0.72,
        sourceType: item.item_type_code,
        sourceId: item.id,
        repositoryItemId: item.id,
        aiRunId: aiRun.id,
      });

      for (const candidate of candidates) {
        const factId = await insertCaseFact({
          lawFirmId: input.lawFirmId,
          clientId: caseRow.client_id,
          caseId: input.caseId,
          dataField: candidate.dataField,
          rawValue: candidate.rawValue,
          confidence: candidate.confidence,
          aiRunId: aiRun.id,
          sourceContextCode: "repository",
          requiresHumanConfirmation: candidate.confidence < 0.8,
          reviewContext: factReviewContext,
        });

        if (!factId) {
          continue;
        }

        await insertFactSource({
          caseFactId: factId,
          sourceType: candidate.sourceType,
          sourceId: candidate.sourceId,
          repositoryItemId: candidate.repositoryItemId,
          aiRunId: candidate.aiRunId,
          evidenceExcerpt: candidate.evidenceExcerpt,
          confidenceContribution: candidate.confidence,
        });

        consolidatedFactIds.push(factId);
      }
    }

    await finishAiRun({
      aiRunId: aiRun.id,
      status: "completed",
      inputTokens: repositoryItems.reduce(
        (sum, item) => sum + Math.ceil((item.body_text?.length ?? 0) / 4),
        0,
      ),
      outputTokens: Math.max(consolidatedFactIds.length * 10, 1),
      estimatedCost: 0,
    });

    return {
      aiRunId: aiRun.id,
      caseId: input.caseId,
      createdFactCount: consolidatedFactIds.length,
      facts: await listCaseFacts({
        lawFirmId: input.lawFirmId,
        caseId: input.caseId,
      }),
    };
  } catch (error) {
    await finishAiRun({
      aiRunId: aiRun.id,
      status: "failed",
      errorMessage: error instanceof Error ? error.message : "Unexpected error",
    });
    throw error;
  }
}

export async function listCaseFacts(input: { lawFirmId: string; caseId: string }) {
  await ensureFactReviewFeedbackTable();

  const facts = await prisma.$queryRaw<
    Array<{
      id: string;
      fieldKey: string;
      label: string;
      rawValue: string | null;
      confidence: number | string;
      statusCode: string;
      sourceCount: bigint | number | string;
      createdAt: Date;
    }>
  >`
    SELECT
      cf.id,
      df.field_key AS fieldKey,
      df.label AS label,
      cf.raw_value AS rawValue,
      cf.confidence_score AS confidence,
      cf.status_code AS statusCode,
      CAST(COUNT(fs.id) AS SIGNED) AS sourceCount,
      cf.created_at AS createdAt
    FROM case_facts cf
    JOIN data_fields df ON df.id = cf.data_field_id
    LEFT JOIN fact_sources fs ON fs.case_fact_id = cf.id
    WHERE cf.law_firm_id = ${input.lawFirmId}
      AND cf.case_id = ${input.caseId}
      AND cf.deleted_at IS NULL
    GROUP BY cf.id, df.field_key, df.label, cf.raw_value, cf.confidence_score, cf.status_code, cf.created_at
    ORDER BY
      df.label ASC,
      CASE
        WHEN cf.status_code = 'confirmed' THEN 0
        WHEN cf.status_code = 'rejected' THEN 2
        ELSE 1
      END ASC,
      cf.created_at DESC
  `;

  return Promise.all(
    facts.map(async (fact) => {
      const audit = await getFactAuditReference(fact.id);
      const [latestReview] = await prisma.$queryRaw<
        Array<{
          review_action: string;
          corrected_raw_value: string | null;
          source_hint: string | null;
          search_hint: string | null;
          feedback_note: string | null;
          created_at: Date;
        }>
      >`
        SELECT review_action, corrected_raw_value, source_hint, search_hint, feedback_note, created_at
        FROM fact_review_feedback
        WHERE case_fact_id = ${fact.id}
          AND deleted_at IS NULL
        ORDER BY created_at DESC
        LIMIT 1
      `;

      return {
        ...fact,
        confidence: Number(fact.confidence),
        sourceCount: Number(fact.sourceCount),
        sourceTitles: audit.sourceTitles,
        evidenceExcerpt: audit.evidenceExcerpt,
        latestReview: latestReview
          ? {
              action: latestReview.review_action,
              correctedValue: latestReview.corrected_raw_value,
              sourceHint: latestReview.source_hint,
              searchHint: latestReview.search_hint,
              feedbackNote: latestReview.feedback_note,
              createdAt: latestReview.created_at,
            }
          : null,
      };
    }),
  );
}

export async function reviewCaseFact(input: {
  lawFirmId: string;
  caseId: string;
  factId: string;
  actorUserId: string;
  action: "confirm" | "correct" | "reject" | "research_hint";
  correctedValue?: string | null;
  feedbackNote?: string | null;
  searchHint?: string | null;
  sourceHint?: string | null;
}) {
  await ensureFactReviewFeedbackTable();

  const [factRow] = await prisma.$queryRaw<
    Array<{
      id: string;
      client_id: string;
      case_id: string | null;
      data_field_id: string;
      raw_value: string | null;
      normalized_value_hash: string | null;
      status_code: string;
      field_key: string;
      label: string;
      data_type: string;
    }>
  >`
    SELECT
      cf.id,
      cf.client_id,
      cf.case_id,
      cf.data_field_id,
      cf.raw_value,
      cf.normalized_value_hash,
      cf.status_code,
      df.field_key,
      df.label,
      df.data_type
    FROM case_facts cf
    JOIN data_fields df ON df.id = cf.data_field_id
    WHERE cf.id = ${input.factId}
      AND cf.law_firm_id = ${input.lawFirmId}
      AND cf.case_id = ${input.caseId}
      AND cf.deleted_at IS NULL
    LIMIT 1
  `;

  if (!factRow) {
    throw new Error("Fact not found");
  }

  const correctedValue = String(input.correctedValue ?? "").trim();
  const feedbackNote = String(input.feedbackNote ?? "").trim();
  const searchHint = String(input.searchHint ?? "").trim();
  const sourceHint = String(input.sourceHint ?? "").trim();

  if (input.action === "correct" && !correctedValue) {
    throw new Error("A corrected value is required");
  }

  if (input.action === "research_hint" && !feedbackNote && !searchHint && !sourceHint) {
    throw new Error("Provide a note or hint to teach the system");
  }

  const correctedNormalized =
    correctedValue
      ? normalizeValue(factRow.field_key, factRow.data_type, correctedValue)
      : null;
  const feedbackId = createId();

  await prisma.$executeRaw`
    INSERT INTO fact_review_feedback (
      id,
      law_firm_id,
      client_id,
      case_id,
      case_fact_id,
      data_field_id,
      review_action,
      previous_raw_value,
      previous_normalized_hash,
      corrected_raw_value,
      corrected_normalized_hash,
      source_hint,
      search_hint,
      feedback_note,
      created_by_user_id,
      created_at,
      updated_at
    ) VALUES (
      ${feedbackId},
      ${input.lawFirmId},
      ${factRow.client_id},
      ${factRow.case_id},
      ${factRow.id},
      ${factRow.data_field_id},
      ${input.action},
      ${factRow.raw_value ?? null},
      ${factRow.normalized_value_hash ?? null},
      ${correctedNormalized?.raw ?? null},
      ${correctedNormalized?.normalizedHash ?? null},
      ${sourceHint || null},
      ${searchHint || null},
      ${feedbackNote || null},
      ${input.actorUserId},
      CURRENT_TIMESTAMP,
      CURRENT_TIMESTAMP
    )
  `;

  let reviewedFactId = factRow.id;
  const humanReviewEvidence = buildHumanReviewEvidenceExcerpt({
    action: input.action,
    previousRawValue: factRow.raw_value,
    correctedRawValue: correctedValue || null,
    sourceHint: sourceHint || null,
    searchHint: searchHint || null,
    feedbackNote: feedbackNote || null,
  });

  if (input.action === "confirm") {
    await prisma.$executeRaw`
      UPDATE case_facts
      SET status_code = 'confirmed',
          confidence_score = 1.0000,
          requires_human_confirmation = 0,
          updated_at = CURRENT_TIMESTAMP
      WHERE id = ${factRow.id}
    `;

    await insertFactSource({
      caseFactId: factRow.id,
      sourceType: "human_review",
      sourceId: feedbackId,
      evidenceExcerpt: humanReviewEvidence,
      confidenceContribution: 1,
    });

    if (factRow.raw_value?.trim() && clientFieldColumnMap[factRow.field_key]) {
      await syncClientStructuredData({
        clientId: factRow.client_id,
        fieldKey: factRow.field_key,
        rawValue: factRow.raw_value,
      });
    }
  }

  if (input.action === "reject") {
    await prisma.$executeRaw`
      UPDATE case_facts
      SET status_code = 'rejected',
          requires_human_confirmation = 0,
          updated_at = CURRENT_TIMESTAMP
      WHERE id = ${factRow.id}
    `;

    await insertFactSource({
      caseFactId: factRow.id,
      sourceType: "human_review",
      sourceId: feedbackId,
      evidenceExcerpt: humanReviewEvidence,
      confidenceContribution: 1,
    });
  }

  if (input.action === "research_hint") {
    await insertFactSource({
      caseFactId: factRow.id,
      sourceType: "human_review",
      sourceId: feedbackId,
      evidenceExcerpt: humanReviewEvidence,
      confidenceContribution: 1,
    });
  }

  if (input.action === "correct") {
    await prisma.$executeRaw`
      UPDATE case_facts
      SET status_code = 'rejected',
          requires_human_confirmation = 0,
          updated_at = CURRENT_TIMESTAMP
      WHERE id = ${factRow.id}
    `;

    const reviewContext = await loadFactReviewContext({
      lawFirmId: input.lawFirmId,
      clientId: factRow.client_id,
      caseId: factRow.case_id,
    });

    const correctedFactId = await insertCaseFact({
      lawFirmId: input.lawFirmId,
      clientId: factRow.client_id,
      caseId: factRow.case_id,
      dataField: {
        id: factRow.data_field_id,
        field_key: factRow.field_key,
        label: factRow.label,
        data_type: factRow.data_type,
      },
      rawValue: correctedValue,
      confidence: 1,
      aiRunId: null,
      sourceContextCode: "human_review",
      reviewContext,
    });

    if (!correctedFactId) {
      throw new Error("The corrected value matches a previously rejected human review decision");
    }

    reviewedFactId = correctedFactId;

    await insertFactSource({
      caseFactId: correctedFactId,
      sourceType: "human_review",
      sourceId: feedbackId,
      evidenceExcerpt: humanReviewEvidence,
      confidenceContribution: 1,
    });

    if (clientFieldColumnMap[factRow.field_key]) {
      await syncClientStructuredData({
        clientId: factRow.client_id,
        fieldKey: factRow.field_key,
        rawValue: correctedValue,
      });
    }
  }

  return {
    action: input.action,
    reviewedFactId,
    facts: await listCaseFacts({
      lawFirmId: input.lawFirmId,
      caseId: input.caseId,
    }),
  };
}

export async function analyzeCaseGaps(input: {
  lawFirmId: string;
  caseId: string;
  actorUserId: string;
}) {
  const [caseRow] = await prisma.$queryRaw<
    Array<{ id: string; client_id: string; title: string | null }>
  >`
    SELECT id, client_id, title
    FROM cases
    WHERE id = ${input.caseId}
      AND law_firm_id = ${input.lawFirmId}
      AND deleted_at IS NULL
    LIMIT 1
  `;

  if (!caseRow) {
    throw new Error("Case not found");
  }

  const [workflowInstance] = await prisma.$queryRaw<
    Array<{ id: string }>
  >`
    SELECT id
    FROM workflow_instances
    WHERE case_id = ${input.caseId}
      AND law_firm_id = ${input.lawFirmId}
    ORDER BY created_at DESC
    LIMIT 1
  `;

  if (!workflowInstance) {
    throw new Error("No workflow instance is active for this case");
  }

  const aiRun = await createAiRun({
    lawFirmId: input.lawFirmId,
    caseId: input.caseId,
    clientId: caseRow.client_id,
    runType: "gap_analysis",
  });

  try {
    const caseForms = await prisma.$queryRaw<
      Array<{
        id: string;
        form_template_id: string;
        requirement_name: string;
        form_name: string;
        form_code: string | null;
      }>
    >`
      SELECT cf.id, cf.form_template_id, cf.requirement_name, ft.name AS form_name, ft.code AS form_code
      FROM case_forms cf
      JOIN form_templates ft ON ft.id = cf.form_template_id
      WHERE cf.case_id = ${input.caseId}
        AND cf.law_firm_id = ${input.lawFirmId}
      ORDER BY cf.created_at ASC
    `;

    if (caseForms.length === 0) {
      throw new Error("No case forms were generated for this case");
    }

    const effectiveCaseForms = await resolveEffectiveGapAnalysisCaseForms({
      lawFirmId: input.lawFirmId,
      caseTitle: caseRow.title,
      caseForms,
    });
    const formTemplateIds = Array.from(
      new Set(effectiveCaseForms.map((item) => item.effective_form_template_id)),
    );

    const formFields = await prisma.$queryRaw<
      Array<{
        id: string;
        form_template_id: string;
        field_key: string;
        label: string;
        pdf_field_name: string;
        data_type: string;
        is_required: number;
        section_name: string | null;
      }>
    >`
      SELECT id, form_template_id, field_key, label, pdf_field_name, data_type, is_required, section_name
      FROM form_fields
      WHERE form_template_id IN (${Prisma.join(formTemplateIds)})
    `;

    const mappings = await prisma.$queryRaw<
      Array<{ form_field_id: string; data_field_id: string; confidence_threshold: number }>
    >`
      SELECT form_field_id, data_field_id, confidence_threshold
      FROM form_mappings
      WHERE form_template_id IN (${Prisma.join(formTemplateIds)})
        AND is_active = 1
    `;

    const dataFields = await getDataFields();
    const dataFieldByKey = new Map(dataFields.map((item) => [item.field_key, item]));
    const dataFieldById = new Map(dataFields.map((item) => [item.id, item]));
    const mappingByFormFieldId = new Map(
      mappings.map((item) => [item.form_field_id, item]),
    );

    const questionnaireRunId = createId();
    await prisma.$executeRaw`
      INSERT INTO questionnaire_runs (
        id, law_firm_id, client_id, case_id, workflow_instance_id, information_request_id,
        ai_run_id, status_code, run_reason, coverage_summary_json, requested_by_user_id,
        started_at, completed_at, created_at
      ) VALUES (
        ${questionnaireRunId},
        ${input.lawFirmId},
        ${caseRow.client_id},
        ${input.caseId},
        ${workflowInstance.id},
        NULL,
        ${aiRun.id},
        'completed',
        'Dynamic gap analysis',
        NULL,
        ${input.actorUserId},
        NOW(),
        NOW(),
        CURRENT_TIMESTAMP
      )
    `;

    const consultedRepositoryItems = await prisma.$queryRaw<
      Array<{ id: string }>
    >`
      SELECT id
      FROM repository_items
      WHERE law_firm_id = ${input.lawFirmId}
        AND (case_id = ${input.caseId} OR client_id = ${caseRow.client_id})
    `;

    for (const repositoryItem of consultedRepositoryItems) {
      await prisma.$executeRaw`
        INSERT INTO questionnaire_run_sources (
          id, questionnaire_run_id, source_type, source_id, repository_item_id, was_consulted, created_at
        ) VALUES (
          ${createId()},
          ${questionnaireRunId},
          'repository_item',
          ${repositoryItem.id},
          ${repositoryItem.id},
          1,
          CURRENT_TIMESTAMP
        )
      `;
    }

    const consultedFacts = await getCaseFacts({
      clientId: caseRow.client_id,
      caseId: input.caseId,
    });

    for (const fact of consultedFacts) {
      await prisma.$executeRaw`
        INSERT INTO questionnaire_run_sources (
          id, questionnaire_run_id, source_type, source_id, case_fact_id, was_consulted, created_at
        ) VALUES (
          ${createId()},
          ${questionnaireRunId},
          'case_fact',
          ${fact.id},
          ${fact.id},
          1,
          CURRENT_TIMESTAMP
        )
      `;
    }

    const infoRequestId = createId();
    await prisma.$executeRaw`
      INSERT INTO information_requests (
        id, law_firm_id, client_id, case_id, request_type, title, status_code,
        delivery_channel_code, triggered_by_ai_run_id, requested_by_user_id,
        created_at, updated_at
      ) VALUES (
        ${infoRequestId},
        ${input.lawFirmId},
        ${caseRow.client_id},
        ${input.caseId},
        'questionnaire',
        'Dynamic information gap request',
        'issued',
        'portal',
        ${aiRun.id},
        ${input.actorUserId},
        CURRENT_TIMESTAMP,
        CURRENT_TIMESTAMP
      )
    `;

    await prisma.$executeRaw`
      UPDATE questionnaire_runs
      SET information_request_id = ${infoRequestId}
      WHERE id = ${questionnaireRunId}
    `;

    const portalToken = randomBytes(24).toString("hex");
    const tokenHash = hashPortalToken(portalToken);
    const portalSessionId = createId();
    const questionnaireSessionId = createId();

    await prisma.$executeRaw`
      INSERT INTO client_portal_sessions (
        id, law_firm_id, client_id, case_id, information_request_id, token_hash,
        session_type, status_code, expires_at, created_at, updated_at
      ) VALUES (
        ${portalSessionId},
        ${input.lawFirmId},
        ${caseRow.client_id},
        ${input.caseId},
        ${infoRequestId},
        ${tokenHash},
        'questionnaire',
        'issued',
        DATE_ADD(NOW(), INTERVAL 7 DAY),
        CURRENT_TIMESTAMP,
        CURRENT_TIMESTAMP
      )
    `;

    await prisma.$executeRaw`
      INSERT INTO questionnaire_sessions (
        id, law_firm_id, client_id, case_id, questionnaire_run_id, client_portal_session_id,
        token_hash, status_code, expires_at, started_at, last_activity_at, is_reopen_allowed,
        max_reopens, created_at, updated_at
      ) VALUES (
        ${questionnaireSessionId},
        ${input.lawFirmId},
        ${caseRow.client_id},
        ${input.caseId},
        ${questionnaireRunId},
        ${portalSessionId},
        ${tokenHash},
        'issued',
        DATE_ADD(NOW(), INTERVAL 7 DAY),
        NOW(),
        NOW(),
        1,
        1,
        CURRENT_TIMESTAMP,
        CURRENT_TIMESTAMP
      )
    `;

    const questionsCreated: string[] = [];
    const questionByDataFieldId = new Map<
      string,
      { questionId: string; informationRequestItemId: string }
    >();
    const coverageByDataFieldId = new Map<string, CoverageResult>();

    for (const caseForm of effectiveCaseForms) {
      const runFormId = createId();
      const relatedFields = formFields.filter(
        (field) =>
          field.form_template_id === caseForm.effective_form_template_id && field.is_required === 1,
      );

      let coveredCount = 0;
      let missingCount = 0;
      let lowConfidenceCount = 0;
      let conflictingCount = 0;

      await prisma.$executeRaw`
        INSERT INTO questionnaire_run_forms (
          id, questionnaire_run_id, form_template_id, case_form_id, evaluation_status,
          covered_count, missing_count, low_confidence_count, conflicting_count, created_at
        ) VALUES (
          ${runFormId},
          ${questionnaireRunId},
          ${caseForm.effective_form_template_id},
          ${caseForm.id},
          'evaluated',
          0, 0, 0, 0,
          CURRENT_TIMESTAMP
        )
      `;

      let questionSequence = questionsCreated.length + 1;

      for (const field of relatedFields) {
        const mapping = mappingByFormFieldId.get(field.id);
        const dataField = resolveGapAnalysisDataField({
          formCode: caseForm.effective_form_code,
          field,
          mapping,
          dataFieldById,
          dataFieldByKey,
        });

        if (!dataField) {
          continue;
        }

        const threshold = mapping?.confidence_threshold ?? 0.8;
        let coverage = coverageByDataFieldId.get(dataField.id);

        if (!coverage) {
          coverage = await getBestFactCoverage({
            clientId: caseRow.client_id,
            caseId: input.caseId,
            dataFieldId: dataField.id,
            threshold,
          });
          coverageByDataFieldId.set(dataField.id, coverage);
        }

        if (coverage.status === "covered") coveredCount += 1;
        if (coverage.status === "missing") missingCount += 1;
        if (coverage.status === "low_confidence") lowConfidenceCount += 1;
        if (coverage.status === "conflicting") conflictingCount += 1;

        const fieldResultId = createId();
        await prisma.$executeRaw`
          INSERT INTO questionnaire_run_field_results (
            id, questionnaire_run_form_id, form_field_id, data_field_id, related_party_id,
            chosen_case_fact_id, result_status, current_confidence, reasoning, source_summary_json, created_at
          ) VALUES (
            ${fieldResultId},
            ${runFormId},
            ${field.id},
            ${dataField.id},
            NULL,
            ${coverage.chosenFactId},
            ${coverage.status},
            ${coverage.currentConfidence},
            ${coverage.reasoning},
            NULL,
            CURRENT_TIMESTAMP
          )
        `;

        if (coverage.status === "covered") {
          continue;
        }

        let existingQuestion = questionByDataFieldId.get(dataField.id);

        if (!existingQuestion) {
          const infoRequestItemId = createId();
          const reasonCode =
            coverage.status === "conflicting"
              ? "conflict"
              : coverage.status === "low_confidence"
                ? "low_confidence"
                : "missing";
          const requestLabel = dataField.label || field.label;

          await prisma.$executeRaw`
            INSERT INTO information_request_items (
              id, information_request_id, data_field_id, related_party_id, form_field_id,
              requirement_type, reason_code, reason_text, current_confidence, is_required,
              status_code, created_at, updated_at
            ) VALUES (
              ${infoRequestItemId},
              ${infoRequestId},
              ${dataField.id},
              NULL,
              ${field.id},
              'field',
              ${reasonCode},
              ${coverage.reasoning},
              ${coverage.currentConfidence},
              1,
              'pending',
              CURRENT_TIMESTAMP,
              CURRENT_TIMESTAMP
            )
          `;

          const tokenFriendlyReason =
            coverage.status === "conflicting"
              ? `We found conflicting values for ${requestLabel}. Please confirm the correct information.`
              : coverage.status === "low_confidence"
                ? `We found ${requestLabel}, but confidence is low. Please confirm it.`
                : `We still need ${requestLabel} to complete ${caseForm.effective_form_name}.`;

          const questionId = createId();
          await prisma.$executeRaw`
            INSERT INTO questionnaire_questions (
              id, questionnaire_session_id, generated_by_run_id, sequence_no, question_type,
              question_text, question_context_group, reason_text, consulted_sources_json,
              current_confidence, is_required, is_confirmation, priority,
              dependency_expression_json, status_code, asked_at, created_at, updated_at
            ) VALUES (
              ${questionId},
              ${questionnaireSessionId},
              ${questionnaireRunId},
              ${questionSequence},
              ${coverage.status === "missing" ? "question" : "confirmation"},
              ${tokenFriendlyReason},
              ${field.section_name ?? caseForm.effective_form_name},
              ${coverage.reasoning},
              NULL,
              ${coverage.currentConfidence},
              1,
              ${coverage.status === "missing" ? 0 : 1},
              ${coverage.status === "conflicting" ? 10 : coverage.status === "low_confidence" ? 20 : 30},
              NULL,
              'pending',
              NOW(),
              CURRENT_TIMESTAMP,
              CURRENT_TIMESTAMP
            )
          `;

          existingQuestion = {
            questionId,
            informationRequestItemId: infoRequestItemId,
          };
          questionByDataFieldId.set(dataField.id, existingQuestion);
          questionsCreated.push(questionId);
          questionSequence += 1;
        }

        await prisma.$executeRaw`
          INSERT INTO questionnaire_question_targets (
            id, question_id, data_field_id, form_field_id, information_request_item_id, related_party_id,
            target_role_code, target_priority, created_at
          ) VALUES (
            ${createId()},
            ${existingQuestion.questionId},
            ${dataField.id},
            ${field.id},
            ${existingQuestion.informationRequestItemId},
            NULL,
            NULL,
            100,
            CURRENT_TIMESTAMP
          )
        `;
      }

      await prisma.$executeRaw`
        UPDATE questionnaire_run_forms
        SET
          covered_count = ${coveredCount},
          missing_count = ${missingCount},
          low_confidence_count = ${lowConfidenceCount},
          conflicting_count = ${conflictingCount}
        WHERE id = ${runFormId}
      `;
    }

    await prisma.$executeRaw`
      INSERT INTO questionnaire_events (
        id, questionnaire_session_id, question_id, answer_id, ai_run_id, event_type,
        payload_json, occurred_at, created_at
      ) VALUES (
        ${createId()},
        ${questionnaireSessionId},
        NULL,
        NULL,
        ${aiRun.id},
        'session_generated',
        ${JSON.stringify({ questionCount: questionsCreated.length })},
        NOW(),
        CURRENT_TIMESTAMP
      )
    `;

    await prisma.$executeRaw`
      UPDATE questionnaire_runs
      SET coverage_summary_json = ${buildQuestionnaireCoverageSummary({
        sourceType: "gap_analysis",
        portalToken,
        questionCount: questionsCreated.length,
        documentCount: 0,
      })}
      WHERE id = ${questionnaireRunId}
    `;

    await finishAiRun({
      aiRunId: aiRun.id,
      status: "completed",
      inputTokens: 0,
      outputTokens: questionsCreated.length * 20,
      estimatedCost: 0,
    });

    return {
      questionnaireRunId,
      questionnaireSessionId,
      portalToken,
      portalLink: `http://localhost:5173/questionnaire/${portalToken}`,
      questionCount: questionsCreated.length,
    };
  } catch (error) {
    await finishAiRun({
      aiRunId: aiRun.id,
      status: "failed",
      errorMessage: error instanceof Error ? error.message : "Unexpected error",
    });
    throw error;
  }
}

async function getPortalQuestionnaireSessionByToken(token: string) {
  const tokenHash = hashPortalToken(token);
  const [session] = await prisma.$queryRaw<PortalQuestionnaireSessionRow[]>`
    SELECT
      qs.id,
      qs.law_firm_id,
      qs.client_id,
      qs.case_id,
      qs.status_code,
      qs.expires_at,
      qs.completed_at,
      qs.client_portal_session_id,
      cps.information_request_id,
      ir.request_type,
      qr.run_reason
    FROM questionnaire_sessions qs
    LEFT JOIN client_portal_sessions cps ON cps.id = qs.client_portal_session_id
    LEFT JOIN information_requests ir ON ir.id = cps.information_request_id
    LEFT JOIN questionnaire_runs qr ON qr.id = qs.questionnaire_run_id
    WHERE qs.token_hash = ${tokenHash}
    LIMIT 1
  `;

  if (!session) {
    throw new Error("Questionnaire session not found");
  }

  const questions = await prisma.$queryRaw<PortalQuestionRow[]>`
    SELECT id, sequence_no, question_type, question_text, question_context_group, reason_text,
           status_code, answered_at, created_at
    FROM questionnaire_questions
    WHERE questionnaire_session_id = ${session.id}
    ORDER BY priority ASC, sequence_no ASC
  `;

  const answers = await prisma.$queryRaw<PortalAnswerRow[]>`
    SELECT id, question_id, answer_text, answered_at
    FROM questionnaire_answers
    WHERE questionnaire_session_id = ${session.id}
    ORDER BY answered_at ASC
  `;

  const documentRequestItems = session.information_request_id
    ? await prisma.$queryRaw<PortalDocumentRequestItemRow[]>`
        SELECT id, reason_text, status_code, resolved_at
        FROM information_request_items
        WHERE information_request_id = ${session.information_request_id}
          AND requirement_type = 'document'
        ORDER BY created_at ASC
      `
    : [];

  return {
    session,
    questions,
    answers,
    documentRequestItems,
  };
}

function normalizeStatusCode(value: string | null | undefined) {
  return (value ?? "").trim().toLowerCase().replace(/[\s-]+/g, "_");
}

function resolvePortalSessionKind(input: {
  requestType: string | null;
  runReason: string | null;
}) {
  if (
    normalizeStatusCode(input.requestType) === "workflow_portal" ||
    normalizeStatusCode(input.runReason) === "workflow_portal_selection"
  ) {
    return "workflow_portal" as const;
  }

  if (
    normalizeStatusCode(input.requestType) === "questionnaire" ||
    normalizeStatusCode(input.runReason) === "dynamic_gap_analysis"
  ) {
    return "gap_questionnaire" as const;
  }

  return "unknown" as const;
}

function formatPortalQuestionnaireSession(bundle: {
  session: PortalQuestionnaireSessionRow;
  questions: PortalQuestionRow[];
  answers: PortalAnswerRow[];
  documentRequestItems: PortalDocumentRequestItemRow[];
}) {
  const groupedDocuments = new Map<
    string,
    {
      id: string;
      workflowTemplateId: string;
      workflowName: string;
      workflowRequiredDocumentId: string;
      documentTypeCode: string;
      displayName: string;
      description: string | null;
      isRequired: boolean;
      requiredCount: number;
      receivedCount: number;
      pendingCount: number;
      statusCode: "pending" | "partial" | "received";
    }
  >();

  for (const item of bundle.documentRequestItems) {
    const metadata = parsePortalDocumentRequirementMetadata(item.reason_text);

    if (!metadata) {
      continue;
    }

    const key = buildPortalDocumentRequirementKey(metadata);
    const currentGroup = groupedDocuments.get(key);
    const nextRequiredCount = (currentGroup?.requiredCount ?? 0) + 1;
    const nextReceivedCount =
      (currentGroup?.receivedCount ?? 0) + (item.status_code === "resolved" ? 1 : 0);
    const nextPendingCount = Math.max(0, nextRequiredCount - nextReceivedCount);

    groupedDocuments.set(key, {
      id: key,
      workflowTemplateId: metadata.workflowTemplateId,
      workflowName: metadata.workflowName,
      workflowRequiredDocumentId: metadata.workflowRequiredDocumentId,
      documentTypeCode: metadata.documentTypeCode,
      displayName: metadata.displayName,
      description: metadata.description,
      isRequired: metadata.isRequired,
      requiredCount: nextRequiredCount,
      receivedCount: nextReceivedCount,
      pendingCount: nextPendingCount,
      statusCode:
        nextPendingCount === 0 ? "received" : nextReceivedCount > 0 ? "partial" : "pending",
    });
  }

  const requiredDocuments = Array.from(groupedDocuments.values()).sort((left, right) => {
    const byWorkflow = left.workflowName.localeCompare(right.workflowName);

    if (byWorkflow !== 0) {
      return byWorkflow;
    }

    return left.displayName.localeCompare(right.displayName);
  });
  const pendingQuestions = bundle.questions.filter((question) => question.status_code === "pending").length;
  const pendingDocuments = requiredDocuments.filter((document) => document.pendingCount > 0).length;
  const receivedDocuments = requiredDocuments.reduce(
    (total, document) => total + document.receivedCount,
    0,
  );
  const sessionKind = resolvePortalSessionKind({
    requestType: bundle.session.request_type,
    runReason: bundle.session.run_reason,
  });

  return {
    session: {
      id: bundle.session.id,
      law_firm_id: bundle.session.law_firm_id,
      client_id: bundle.session.client_id,
      case_id: bundle.session.case_id,
      status_code: bundle.session.status_code,
      expires_at: bundle.session.expires_at,
      completed_at: bundle.session.completed_at,
      request_type: bundle.session.request_type,
      run_reason: bundle.session.run_reason,
      session_kind: sessionKind,
    },
    summary: {
      totalQuestions: bundle.questions.length,
      pendingQuestions,
      answeredQuestions: bundle.answers.length,
      totalRequiredDocuments: requiredDocuments.length,
      pendingDocuments,
      receivedDocuments,
      hasAnyTasks: bundle.questions.length > 0 || requiredDocuments.length > 0,
    },
    questions: bundle.questions,
    answers: bundle.answers,
    nextQuestion: bundle.questions.find((question) => question.status_code === "pending") ?? null,
    requiredDocuments,
  };
}

async function updatePortalSessionState(input: { sessionId: string }) {
  const [counts] = await prisma.$queryRaw<
    Array<{
      pending_questions: bigint;
      pending_documents: bigint;
      client_portal_session_id: string | null;
      information_request_id: string | null;
    }>
  >`
    SELECT
      (
        SELECT COUNT(*)
        FROM questionnaire_questions qq
        WHERE qq.questionnaire_session_id = qs.id
          AND qq.status_code = 'pending'
      ) AS pending_questions,
      (
        SELECT COUNT(*)
        FROM information_request_items iri
        WHERE iri.information_request_id = cps.information_request_id
          AND iri.requirement_type = 'document'
          AND iri.status_code = 'pending'
      ) AS pending_documents,
      qs.client_portal_session_id,
      cps.information_request_id
    FROM questionnaire_sessions qs
    LEFT JOIN client_portal_sessions cps ON cps.id = qs.client_portal_session_id
    WHERE qs.id = ${input.sessionId}
    LIMIT 1
  `;

  const pendingQuestions = Number(counts?.pending_questions ?? 0n);
  const pendingDocuments = Number(counts?.pending_documents ?? 0n);
  const hasPendingWork = pendingQuestions > 0 || pendingDocuments > 0;

  await prisma.$executeRaw`
    UPDATE questionnaire_sessions
    SET
      status_code = ${hasPendingWork ? "active" : "completed"},
      completed_at = ${hasPendingWork ? null : new Date()},
      last_activity_at = NOW(),
      updated_at = CURRENT_TIMESTAMP
    WHERE id = ${input.sessionId}
  `;

  if (counts?.client_portal_session_id) {
    await prisma.$executeRaw`
      UPDATE client_portal_sessions
      SET
        status_code = ${hasPendingWork ? "active" : "completed"},
        opened_at = COALESCE(opened_at, NOW()),
        last_activity_at = NOW(),
        closed_at = ${hasPendingWork ? null : new Date()},
        updated_at = CURRENT_TIMESTAMP
      WHERE id = ${counts.client_portal_session_id}
    `;
  }

  if (counts?.information_request_id) {
    await prisma.$executeRaw`
      UPDATE information_requests
      SET
        status_code = ${hasPendingWork ? "opened" : "completed"},
        opened_at = COALESCE(opened_at, NOW()),
        completed_at = ${hasPendingWork ? null : new Date()},
        updated_at = CURRENT_TIMESTAMP
      WHERE id = ${counts.information_request_id}
    `;
  }

  return {
    pendingQuestions,
    pendingDocuments,
    remainingPending: pendingQuestions + pendingDocuments,
  };
}

export async function createWorkflowPortalLink(input: {
  lawFirmId: string;
  clientId: string;
  caseId: string;
  actorUserId: string;
  workflowTemplateIds: string[];
}) {
  const context = await loadWorkflowPortalSelectionContext(input);

  if (context.preview.totals.isEmpty) {
    throw new Error(
      "The selected workflows do not publish any client-facing questions or document requests for the current case conditions.",
    );
  }

  const infoRequestTitle = `Workflow portal • ${context.workflowTemplates
    .map((item) => item.name)
    .join(", ")}`.slice(0, 255);
  const infoRequestId = createId();
  await prisma.$executeRaw`
    INSERT INTO information_requests (
      id, law_firm_id, client_id, case_id, request_type, title, status_code,
      delivery_channel_code, triggered_by_ai_run_id, requested_by_user_id, sent_at,
      created_at, updated_at
    ) VALUES (
      ${infoRequestId},
      ${input.lawFirmId},
      ${input.clientId},
      ${input.caseId},
      'workflow_portal',
      ${infoRequestTitle},
      'issued',
      'portal',
      NULL,
      ${input.actorUserId},
      NOW(),
      CURRENT_TIMESTAMP,
      CURRENT_TIMESTAMP
    )
  `;

  const questionnaireRunId = createId();
  await prisma.$executeRaw`
    INSERT INTO questionnaire_runs (
      id, law_firm_id, client_id, case_id, workflow_instance_id, information_request_id,
      ai_run_id, status_code, run_reason, coverage_summary_json, requested_by_user_id,
      started_at, completed_at, created_at
    ) VALUES (
      ${questionnaireRunId},
      ${input.lawFirmId},
      ${input.clientId},
      ${input.caseId},
      NULL,
      ${infoRequestId},
      NULL,
      'completed',
      'Workflow portal selection',
      NULL,
      ${input.actorUserId},
      NOW(),
      NOW(),
      CURRENT_TIMESTAMP
    )
  `;

  const portalToken = randomBytes(24).toString("hex");
  const tokenHash = hashPortalToken(portalToken);
  const portalSessionId = createId();
  const questionnaireSessionId = createId();

  await prisma.$executeRaw`
    INSERT INTO client_portal_sessions (
      id, law_firm_id, client_id, case_id, information_request_id, token_hash,
      session_type, status_code, expires_at, created_at, updated_at
    ) VALUES (
      ${portalSessionId},
      ${input.lawFirmId},
      ${input.clientId},
      ${input.caseId},
      ${infoRequestId},
      ${tokenHash},
      'questionnaire',
      'issued',
      DATE_ADD(NOW(), INTERVAL 7 DAY),
      CURRENT_TIMESTAMP,
      CURRENT_TIMESTAMP
    )
  `;

  await prisma.$executeRaw`
    INSERT INTO questionnaire_sessions (
      id, law_firm_id, client_id, case_id, questionnaire_run_id, client_portal_session_id,
      token_hash, status_code, expires_at, started_at, last_activity_at, is_reopen_allowed,
      max_reopens, created_at, updated_at
    ) VALUES (
      ${questionnaireSessionId},
      ${input.lawFirmId},
      ${input.clientId},
      ${input.caseId},
      ${questionnaireRunId},
      ${portalSessionId},
      ${tokenHash},
      'issued',
      DATE_ADD(NOW(), INTERVAL 7 DAY),
      NOW(),
      NOW(),
      1,
      1,
      CURRENT_TIMESTAMP,
      CURRENT_TIMESTAMP
    )
  `;

  let questionSequence = 1;

  for (const questionPlan of context.preview.questionPlans) {
    const infoRequestItemId = createId();
    const reasonText = `${questionPlan.workflowName} • ${questionPlan.stepName}`;

    await prisma.$executeRaw`
      INSERT INTO information_request_items (
        id, information_request_id, data_field_id, related_party_id, form_field_id,
        requirement_type, reason_code, reason_text, current_confidence, is_required,
        status_code, created_at, updated_at
      ) VALUES (
        ${infoRequestItemId},
        ${infoRequestId},
        NULL,
        NULL,
        NULL,
        'question',
        'workflow_prompt',
        ${reasonText},
        NULL,
        1,
        'pending',
        CURRENT_TIMESTAMP,
        CURRENT_TIMESTAMP
      )
    `;

    const questionId = createId();
    await prisma.$executeRaw`
      INSERT INTO questionnaire_questions (
        id, questionnaire_session_id, generated_by_run_id, sequence_no, question_type,
        question_text, question_context_group, reason_text, consulted_sources_json,
        current_confidence, is_required, is_confirmation, priority,
        dependency_expression_json, status_code, asked_at, created_at, updated_at
      ) VALUES (
        ${questionId},
        ${questionnaireSessionId},
        NULL,
        ${questionSequence},
        'question',
        ${questionPlan.question},
        ${questionPlan.stepName},
        ${`${questionPlan.workflowName} • ${context.caseRow.title}`},
        NULL,
        NULL,
        1,
        0,
        ${questionSequence * 10},
        NULL,
        'pending',
        NOW(),
        CURRENT_TIMESTAMP,
        CURRENT_TIMESTAMP
      )
    `;

    await prisma.$executeRaw`
      INSERT INTO questionnaire_question_targets (
        id, question_id, data_field_id, form_field_id, information_request_item_id, related_party_id,
        target_role_code, target_priority, created_at
      ) VALUES (
        ${createId()},
        ${questionId},
        NULL,
        NULL,
        ${infoRequestItemId},
        NULL,
        ${questionPlan.targetType},
        100,
        CURRENT_TIMESTAMP
      )
    `;

    questionSequence += 1;
  }

  for (const documentPlan of context.preview.documentPlans) {
    const metadata = {
      workflowTemplateId: documentPlan.workflowTemplateId,
      workflowName: documentPlan.workflowName,
      workflowRequiredDocumentId: documentPlan.workflowRequiredDocumentId,
      documentTypeCode: documentPlan.documentTypeCode,
      displayName: documentPlan.displayName,
      description: documentPlan.description,
      requirementCode: documentPlan.requirementCode,
      minimumQuantity: documentPlan.requiredCount,
      isRequired: documentPlan.isRequired,
    } satisfies PortalDocumentRequirementMetadata;

    for (let index = 0; index < documentPlan.requiredCount; index += 1) {
      await prisma.$executeRaw`
        INSERT INTO information_request_items (
          id, information_request_id, data_field_id, related_party_id, form_field_id,
          requirement_type, reason_code, reason_text, current_confidence, is_required,
          status_code, created_at, updated_at
        ) VALUES (
          ${createId()},
          ${infoRequestId},
          NULL,
          NULL,
          NULL,
          'document',
          'workflow_document',
          ${JSON.stringify(metadata)},
          NULL,
          ${metadata.isRequired ? 1 : 0},
          'pending',
          CURRENT_TIMESTAMP,
          CURRENT_TIMESTAMP
        )
      `;
    }
  }

  await prisma.$executeRaw`
    UPDATE questionnaire_runs
    SET coverage_summary_json = ${buildQuestionnaireCoverageSummary({
      sourceType: "workflow_portal",
      workflowTemplateIds: context.selectedWorkflowTemplateIds,
      portalToken,
      questionCount: context.preview.totals.questionCount,
      documentCount: context.preview.totals.documentCount,
    })}
    WHERE id = ${questionnaireRunId}
  `;

  await prisma.$executeRaw`
    INSERT INTO questionnaire_events (
      id, questionnaire_session_id, question_id, answer_id, ai_run_id, event_type,
      payload_json, occurred_at, created_at
    ) VALUES (
      ${createId()},
      ${questionnaireSessionId},
      NULL,
      NULL,
      NULL,
      'session_generated',
      ${buildQuestionnaireCoverageSummary({
        sourceType: "workflow_portal",
        workflowTemplateIds: context.selectedWorkflowTemplateIds,
        portalToken,
        questionCount: context.preview.totals.questionCount,
        documentCount: context.preview.totals.documentCount,
      })},
      NOW(),
      CURRENT_TIMESTAMP
    )
  `;

  return {
    questionnaireSessionId,
    portalToken,
    portalLink: `http://localhost:5173/questionnaire/${portalToken}`,
    questionCount: context.preview.totals.questionCount,
    documentCount: context.preview.totals.documentCount,
    workflowCount: context.selectedWorkflowTemplateIds.length,
  };
}

async function loadWorkflowPortalSelectionContext(input: {
  lawFirmId: string;
  clientId: string;
  caseId: string;
  actorUserId: string;
  workflowTemplateIds: string[];
}) {
  const selectedWorkflowTemplateIds = normalizeWorkflowTemplateSelection(input.workflowTemplateIds);

  if (!selectedWorkflowTemplateIds.length) {
    throw new Error("Select at least one workflow template");
  }

  const [caseRow] = await prisma.$queryRaw<
    Array<{ id: string; client_id: string; title: string }>
  >`
    SELECT id, client_id, title
    FROM cases
    WHERE id = ${input.caseId}
      AND law_firm_id = ${input.lawFirmId}
      AND deleted_at IS NULL
    LIMIT 1
  `;

  if (!caseRow) {
    throw new Error("Case not found");
  }

  if (caseRow.client_id !== input.clientId) {
    throw new Error("The selected case does not belong to this client");
  }

  const workflowTemplates = await prisma.$queryRaw<
    Array<{ id: string; name: string }>
  >`
    SELECT id, name
    FROM workflow_templates
    WHERE id IN (${Prisma.join(selectedWorkflowTemplateIds)})
      AND law_firm_id = ${input.lawFirmId}
      AND version_number = 1
  `;

  if (workflowTemplates.length !== selectedWorkflowTemplateIds.length) {
    throw new Error("One or more workflow templates were not found");
  }

  const factMap = await getWorkflowConditionFactMap({
    clientId: input.clientId,
    caseId: input.caseId,
  });
  const workflowSteps = await prisma.$queryRaw<
    Array<{
      id: string;
      workflow_template_id: string;
      name: string;
      step_order: number;
      completion_rule_json: unknown;
      condition_expression_json: unknown;
    }>
  >`
    SELECT id, workflow_template_id, name, step_order, completion_rule_json, condition_expression_json
    FROM workflow_steps
    WHERE workflow_template_id IN (${Prisma.join(selectedWorkflowTemplateIds)})
      AND retired_at IS NULL
    ORDER BY workflow_template_id ASC, step_order ASC
  `;

  const workflowDocuments = await prisma.$queryRaw<
    Array<{
      id: string;
      workflow_template_id: string;
      document_type_code: string;
      display_name: string;
      description: string | null;
      requirement_code: string;
      minimum_quantity: number;
      is_required: number;
      condition_expression_json: unknown;
    }>
  >`
    SELECT
      id,
      workflow_template_id,
      document_type_code,
      display_name,
      description,
      requirement_code,
      minimum_quantity,
      is_required,
      condition_expression_json
    FROM workflow_required_documents
    WHERE workflow_template_id IN (${Prisma.join(selectedWorkflowTemplateIds)})
      AND retired_at IS NULL
    ORDER BY workflow_template_id ASC, display_name ASC
  `;

  return {
    selectedWorkflowTemplateIds,
    caseRow,
    workflowTemplates,
    preview: buildWorkflowPortalPreview({
      factMap,
      workflows: workflowTemplates.map((workflowTemplate) => ({
        workflowTemplateId: workflowTemplate.id,
        workflowName: workflowTemplate.name,
        steps: workflowSteps
          .filter((step) => step.workflow_template_id === workflowTemplate.id)
          .map((step) => ({
            stepName: step.name,
            appliesWhen: parseWorkflowConditionConfig(step.condition_expression_json).appliesWhen,
            responsePrompts: parseWorkflowResponsePrompts(step.completion_rule_json),
          })),
        requiredDocuments: workflowDocuments
          .filter((document) => document.workflow_template_id === workflowTemplate.id)
          .map((document) => ({
            workflowRequiredDocumentId: document.id,
            documentTypeCode: document.document_type_code,
            displayName: document.display_name,
            description: document.description,
            requirementCode: document.requirement_code,
            minimumQuantity: document.minimum_quantity,
            isRequired: document.is_required === 1,
            appliesWhen: parseWorkflowConditionConfig(document.condition_expression_json).appliesWhen,
          })),
      })),
    }),
  };
}

export async function previewWorkflowPortalLink(input: {
  lawFirmId: string;
  clientId: string;
  caseId: string;
  actorUserId: string;
  workflowTemplateIds: string[];
}) {
  const context = await loadWorkflowPortalSelectionContext(input);

  return {
    totals: context.preview.totals,
    workflows: context.preview.workflows,
  };
}

export async function getPortalQuestionnaireByToken(token: string) {
  const bundle = await getPortalQuestionnaireSessionByToken(token);
  return formatPortalQuestionnaireSession(bundle);
}

async function reevaluateQuestionnaireSession(input: {
  sessionId: string;
  lawFirmId: string;
  caseId?: string | null;
  clientId: string;
}) {
  const pendingQuestions = await prisma.$queryRaw<
    Array<{
      id: string;
      question_text: string;
      data_field_id: string | null;
      information_request_item_id: string | null;
    }>
  >`
    SELECT qq.id, qq.question_text, qqt.data_field_id, qqt.information_request_item_id
    FROM questionnaire_questions qq
    LEFT JOIN questionnaire_question_targets qqt ON qqt.question_id = qq.id
    WHERE qq.questionnaire_session_id = ${input.sessionId}
      AND qq.status_code = 'pending'
    ORDER BY qq.sequence_no ASC
  `;

  for (const pendingQuestion of pendingQuestions) {
    if (!pendingQuestion.data_field_id) continue;

    const coverage = await getBestFactCoverage({
      clientId: input.clientId,
      caseId: input.caseId ?? null,
      dataFieldId: pendingQuestion.data_field_id,
      threshold: 0.8,
    });

    if (coverage.status === "covered") {
      await prisma.$executeRaw`
        UPDATE questionnaire_questions
        SET status_code = 'retired', retired_at = NOW(), updated_at = CURRENT_TIMESTAMP
        WHERE id = ${pendingQuestion.id}
      `;

      if (pendingQuestion.information_request_item_id) {
        await prisma.$executeRaw`
          UPDATE information_request_items
          SET status_code = 'resolved', resolved_by_source_type = 'case_fact',
              resolved_at = NOW(), updated_at = CURRENT_TIMESTAMP
          WHERE id = ${pendingQuestion.information_request_item_id}
        `;
      }

    }
  }

  const portalState = await updatePortalSessionState({
    sessionId: input.sessionId,
  });

  return portalState.remainingPending;
}

export async function answerPortalQuestion(input: {
  token: string;
  questionId: string;
  answerText: string;
}) {
  const sessionBundle = await getPortalQuestionnaireByToken(input.token);
  const { session } = sessionBundle;
  const question = sessionBundle.questions.find((item) => item.id === input.questionId);

  if (!question || question.status_code !== "pending") {
    throw new Error("Question is not pending");
  }

  const targets = await prisma.$queryRaw<
    Array<{
      data_field_id: string | null;
      form_field_id: string | null;
      information_request_item_id: string | null;
    }>
  >`
    SELECT data_field_id, form_field_id, information_request_item_id
    FROM questionnaire_question_targets
    WHERE question_id = ${input.questionId}
  `;

  const repositoryItemId = await createRepositoryItem({
    lawFirmId: session.law_firm_id,
    clientId: session.client_id,
    caseId: session.case_id,
    itemTypeCode: "questionnaire_answer",
    channelCode: "portal",
    sourceEntityType: "questionnaire_question",
    sourceEntityId: input.questionId,
    bodyText: input.answerText,
    createdByUserId: null,
  });

  const questionAnswerId = createId();
  await prisma.$executeRaw`
    INSERT INTO questionnaire_answers (
      id, questionnaire_session_id, question_id, repository_item_id, channel_code,
      answer_text, structured_answer_json, answer_source, answered_at, created_case_fact_id,
      confidence_override, created_at
    ) VALUES (
      ${questionAnswerId},
      ${session.id},
      ${input.questionId},
      ${repositoryItemId},
      'portal',
      ${input.answerText},
      ${JSON.stringify({ answer: input.answerText })},
      'client',
      NOW(),
      NULL,
      1.0000,
      CURRENT_TIMESTAMP
    )
  `;

  const dataFields = await getDataFields();
  const factIds: string[] = [];
  for (const target of targets) {
    if (target.data_field_id) {
      const dataField = dataFields.find((item) => item.id === target.data_field_id);

      if (dataField) {
        const factId = await insertCaseFact({
          lawFirmId: session.law_firm_id,
          clientId: session.client_id,
          caseId: session.case_id,
          dataField,
          rawValue: input.answerText,
          confidence: 1,
          aiRunId: null,
          sourceContextCode: "client_portal",
        });

        if (!factId) {
          continue;
        }

        await insertFactSource({
          caseFactId: factId,
          sourceType: "questionnaire_answer",
          sourceId: questionAnswerId,
          repositoryItemId,
          evidenceExcerpt: input.answerText.slice(0, 255),
          confidenceContribution: 1,
        });

        await syncClientStructuredData({
          clientId: session.client_id,
          fieldKey: dataField.field_key,
          rawValue: input.answerText,
        });

        factIds.push(factId);
      }
    }

    if (target.information_request_item_id) {
      await prisma.$executeRaw`
        UPDATE information_request_items
        SET status_code = 'resolved',
            resolved_by_source_type = 'questionnaire_answer',
            resolved_by_source_id = ${questionAnswerId},
            resolved_at = NOW(),
            updated_at = CURRENT_TIMESTAMP
        WHERE id = ${target.information_request_item_id}
      `;
    }
  }

  await prisma.$executeRaw`
    UPDATE questionnaire_answers
    SET created_case_fact_id = ${factIds[0] ?? null}
    WHERE id = ${questionAnswerId}
  `;

  await prisma.$executeRaw`
    UPDATE questionnaire_questions
    SET status_code = 'answered', answered_at = NOW(), updated_at = CURRENT_TIMESTAMP
    WHERE id = ${input.questionId}
  `;

  await prisma.$executeRaw`
    INSERT INTO questionnaire_events (
      id, questionnaire_session_id, question_id, answer_id, ai_run_id, event_type,
      payload_json, occurred_at, created_at
    ) VALUES (
      ${createId()},
      ${session.id},
      ${input.questionId},
      ${questionAnswerId},
      NULL,
      'answer_received',
      ${JSON.stringify({ answerText: input.answerText })},
      NOW(),
      CURRENT_TIMESTAMP
    )
  `;

  const remainingPending = await reevaluateQuestionnaireSession({
    sessionId: session.id,
    lawFirmId: session.law_firm_id,
    caseId: session.case_id,
    clientId: session.client_id,
  });

  return {
    answerId: questionAnswerId,
    createdFactIds: factIds,
    remainingPending,
    session: await getPortalQuestionnaireByToken(input.token),
  };
}

export async function uploadPortalDocument(input: {
  token: string;
  requiredDocumentId: string;
  title: string;
  originalFileName: string;
  mimeType: string;
  fileBuffer: Buffer;
  textContent?: string | null;
}) {
  const bundle = await getPortalQuestionnaireSessionByToken(input.token);
  const formattedSession = formatPortalQuestionnaireSession(bundle);
  const requiredDocument = formattedSession.requiredDocuments.find(
    (item) => item.id === input.requiredDocumentId,
  );

  if (!requiredDocument) {
    throw new Error("Required document not found");
  }

  const created = await createDocumentAndExtraction({
    lawFirmId: bundle.session.law_firm_id,
    clientId: bundle.session.client_id,
    caseId: bundle.session.case_id,
    actorUserId: null,
    title: input.title.trim() || requiredDocument.displayName,
    documentTypeCode: requiredDocument.documentTypeCode,
    originalFileName: input.originalFileName,
    mimeType: input.mimeType,
    fileBuffer: input.fileBuffer,
    textContent: input.textContent ?? null,
  });

  const pendingDocumentItem = bundle.documentRequestItems.find((item) => {
    if (item.status_code !== "pending") {
      return false;
    }

    const metadata = parsePortalDocumentRequirementMetadata(item.reason_text);
    return metadata ? buildPortalDocumentRequirementKey(metadata) === input.requiredDocumentId : false;
  });

  if (pendingDocumentItem) {
    await prisma.$executeRaw`
      UPDATE information_request_items
      SET status_code = 'resolved',
          resolved_by_source_type = 'document_record',
          resolved_by_source_id = ${created.documentRecordId},
          resolved_at = NOW(),
          updated_at = CURRENT_TIMESTAMP
      WHERE id = ${pendingDocumentItem.id}
    `;
  }

  await prisma.$executeRaw`
    INSERT INTO questionnaire_events (
      id, questionnaire_session_id, question_id, answer_id, ai_run_id, event_type,
      payload_json, occurred_at, created_at
    ) VALUES (
      ${createId()},
      ${bundle.session.id},
      NULL,
      NULL,
      NULL,
      'document_received',
      ${JSON.stringify({
        requiredDocumentId: input.requiredDocumentId,
        documentRecordId: created.documentRecordId,
      })},
      NOW(),
      CURRENT_TIMESTAMP
    )
  `;

  const portalState = await updatePortalSessionState({
    sessionId: bundle.session.id,
  });

  return {
    documentRecordId: created.documentRecordId,
    remainingPending: portalState.remainingPending,
    session: await getPortalQuestionnaireByToken(input.token),
  };
}

function limitText(value: string | null | undefined, maxLength = 2400) {
  const normalized = String(value ?? "").replace(/\s+/g, " ").trim();

  if (!normalized) {
    return "";
  }

  return normalized.length > maxLength
    ? `${normalized.slice(0, Math.max(0, maxLength - 3)).trim()}...`
    : normalized;
}

function normalizeAiAnswerValue(value: unknown) {
  const normalized = String(value ?? "").trim();

  if (!normalized) {
    return null;
  }

  if (
    /^(unknown|n\/a|na|none|null|not available|not provided|sem valor|nao informado)$/i.test(
      normalized,
    )
  ) {
    return null;
  }

  return normalized;
}

function normalizeDetailedDocumentSummary(value: unknown) {
  return limitText(String(value ?? "").replace(/\s+/g, " ").trim(), 1200);
}

function normalizeDetailedDocumentFactLabel(value: unknown) {
  return limitText(String(value ?? "").replace(/\s+/g, " ").trim(), 140);
}

function normalizeDetailedDocumentConfidence(value: unknown, fallback: number) {
  const parsed =
    typeof value === "number"
      ? value
      : typeof value === "string" && value.trim()
        ? Number(value)
        : Number.NaN;

  if (!Number.isFinite(parsed)) {
    return fallback;
  }

  return Math.max(0, Math.min(1, parsed));
}

function buildRepositoryDocumentKnowledgeBody(input: {
  summaryText: string;
  extractedData: Array<{ label: string; value: string }>;
  importantFacts: DetailedDocumentImportantFact[];
  rawText: string;
}) {
  const sections = [
    input.summaryText ? `AI extraction summary:\n${input.summaryText}` : "",
    input.extractedData.length
      ? `Structured extracted fields:\n${input.extractedData
          .map((entry) => `- ${entry.label}: ${entry.value}`)
          .join("\n")}`
      : "",
    input.importantFacts.length
      ? `Additional extracted facts:\n${input.importantFacts
          .map((entry) => `- ${entry.label}: ${entry.value}`)
          .join("\n")}`
      : "",
    input.rawText ? `Document text:\n${limitText(input.rawText, 18000)}` : "",
  ].filter(Boolean);

  return sections.join("\n\n");
}

function normalizeKnowledgeSearchText(value: string | null | undefined) {
  return String(value ?? "")
    .normalize("NFD")
    .replace(/\p{Diacritic}/gu, "")
    .toLowerCase();
}

function extractKnowledgeSearchTerms(value: string) {
  return Array.from(
    new Set(
      normalizeKnowledgeSearchText(value)
        .replace(/[^\p{L}\p{N}\s]/gu, " ")
        .split(/\s+/)
        .map((term) => term.trim())
        .filter((term) => term.length >= 3),
    ),
  ).slice(0, 16);
}

function scoreKnowledgeDocument(text: string, searchTerms: string[]) {
  if (!searchTerms.length) {
    return 0;
  }

  const normalized = normalizeKnowledgeSearchText(text);
  let score = 0;

  for (const term of searchTerms) {
    if (normalized.includes(term)) {
      score += term.length >= 6 ? 3 : 2;
    }
  }

  return score;
}

function selectRelevantKnowledgeDocuments<T extends { created_at: Date | string }>(
  rows: T[],
  searchTerms: string[],
  getText: (row: T) => string,
  limit: number,
) {
  const scored = rows.map((row) => {
    const timestamp = new Date(row.created_at).getTime();

    return {
      row,
      score: scoreKnowledgeDocument(getText(row), searchTerms),
      timestamp: Number.isNaN(timestamp) ? 0 : timestamp,
    };
  });

  return scored
    .sort((left, right) => right.score - left.score || right.timestamp - left.timestamp)
    .slice(0, limit)
    .map((item) => item.row);
}

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

function normalizeConsolidatedKnowledgeConfidence(value: unknown, fallback = 0.8) {
  const numericValue = Number(value);
  if (!Number.isFinite(numericValue)) {
    return fallback;
  }

  return Math.max(0, Math.min(1, numericValue));
}

function normalizeConsolidatedKnowledgeSourceTitles(value: unknown) {
  if (!Array.isArray(value)) {
    return [] as string[];
  }

  return Array.from(
    new Set(
      value
        .map((item) => normalizeConsolidatedKnowledgeText(item))
        .filter((item) => Boolean(item)),
    ),
  ).slice(0, 8);
}

function normalizeFieldAuditRationale(value: unknown, fallback: string) {
  const normalized = normalizeConsolidatedKnowledgeText(value);
  return normalized || fallback;
}

function normalizeFieldAuditConfidence(value: unknown, fallback: number | null = null) {
  if (value === null || value === undefined || value === "") {
    return fallback;
  }

  const numericValue = Number(value);
  if (!Number.isFinite(numericValue)) {
    return fallback;
  }

  return Math.max(0, Math.min(1, numericValue));
}

function normalizeConsolidatedKnowledgeFactKey(value: unknown, fallbackLabel: string) {
  const normalized = normalizeConsolidatedKnowledgeText(value || fallbackLabel)
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, "")
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, "_")
    .replace(/^_+|_+$/g, "");

  return normalized || "fact";
}

function isValidSynthesizedProfileValue(fieldKey: string, rawValue: string) {
  const value = normalizeConsolidatedKnowledgeText(rawValue);
  if (!value) {
    return false;
  }

  if (
    /^(unknown|n\/a|na|none|null|not available|not provided|sem valor|nao informado|does not apply)$/i.test(
      value,
    )
  ) {
    return false;
  }

  if (fieldKey === "client_email") {
    return /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/i.test(value);
  }

  if (fieldKey === "client_phone") {
    const digits = value.replace(/\D/g, "");
    return digits.length >= 10 && digits.length <= 15;
  }

  if (fieldKey === "client_date_of_birth") {
    return /^(?:\d{2}\/\d{2}\/\d{4}|\d{4}-\d{2}-\d{2})$/.test(value);
  }

  if (fieldKey === "passport_number") {
    return /^[A-Z0-9][A-Z0-9-]{4,}$/i.test(value);
  }

  if (
    fieldKey === "client_country_of_citizenship" ||
    fieldKey === "employer_name" ||
    fieldKey === "job_title"
  ) {
    return !/\b(and immigration services|or school|of the decision|contains| or$|^or$)\b/i.test(
      value,
    );
  }

  return true;
}

function scoreClientKnowledgeSource(input: {
  sourceKind: "document" | "note";
  title: string;
  typeCode: string | null;
  text: string;
}) {
  const haystack = `${input.title} ${input.typeCode ?? ""} ${input.text}`.toLowerCase();
  let score = input.sourceKind === "document" ? 40 : 10;

  const weightedPatterns: Array<[RegExp, number]> = [
    [/\bpassport\b/, 70],
    [/\bds[- ]?160\b/, 65],
    [/\bi[- ]?94\b/, 60],
    [/\bvisa\b/, 55],
    [/\bi[- ]?485\b/, 52],
    [/\bi[- ]?140\b/, 45],
    [/\bi[- ]?130\b/, 45],
    [/\bbirth certificate\b/, 52],
    [/\bmarriage certificate\b/, 48],
    [/\bapproval\b/, 36],
    [/\bemployment\b/, 28],
    [/\bresume\b|\bcurriculum vitae\b|\bcv\b/, 24],
    [/\btax\b|\bw-2\b|\b1040\b/, 24],
    [/\baddress\b|\bmailing\b/, 18],
  ];

  for (const [pattern, weight] of weightedPatterns) {
    if (pattern.test(haystack)) {
      score += weight;
    }
  }

  if (input.typeCode) {
    const typeCodeWeights: Record<string, number> = {
      passport: 65,
      birth_certificate: 50,
      marriage_certificate: 45,
      visa: 45,
      i94: 45,
      employment_letter: 24,
      tax_return: 20,
      pay_stub: 18,
      resume: 18,
    };
    score += typeCodeWeights[input.typeCode] ?? 0;
  }

  score += Math.min(Math.floor(input.text.length / 500), 12);
  return score;
}

function chunkClientKnowledgeSources<T extends { text: string }>(
  rows: T[],
  maxChars = 14000,
  maxItems = 8,
) {
  const chunks: T[][] = [];
  let currentChunk: T[] = [];
  let currentChars = 0;

  for (const row of rows) {
    const rowLength = row.text.length;
    if (
      currentChunk.length > 0 &&
      (currentChunk.length >= maxItems || currentChars + rowLength > maxChars)
    ) {
      chunks.push(currentChunk);
      currentChunk = [];
      currentChars = 0;
    }

    currentChunk.push(row);
    currentChars += rowLength;
  }

  if (currentChunk.length > 0) {
    chunks.push(currentChunk);
  }

  return chunks;
}

function buildClientKnowledgeSummary(input: {
  formName: string;
  summary: string;
  profileFacts: ConsolidatedKnowledgeProfileFact[];
  knowledgeFacts: ConsolidatedKnowledgeFact[];
}) {
  const lines = [
    `Canonical client knowledge for ${input.formName}`,
    input.summary.trim(),
    "",
    "Profile facts:",
    ...input.profileFacts.map(
      (fact) =>
        `- ${fact.label}: ${fact.value} (confidence ${fact.confidence.toFixed(2)}; evidence: ${fact.evidence})`,
    ),
    "",
    "Additional form facts:",
    ...input.knowledgeFacts.map(
      (fact) =>
        `- ${fact.label}: ${fact.value} (confidence ${fact.confidence.toFixed(2)}; evidence: ${fact.evidence})`,
    ),
  ].filter(Boolean);

  return lines.join("\n");
}

async function upsertClientKnowledgeRepositoryItem(input: {
  lawFirmId: string;
  clientId: string;
  actorUserId: string;
  formName: string;
  bodyText: string;
  summaryText: string;
  metadataJson: Record<string, unknown>;
}) {
  const [existing] = await prisma.$queryRaw<Array<{ id: string }>>`
    SELECT id
    FROM repository_items
    WHERE law_firm_id = ${input.lawFirmId}
      AND client_id = ${input.clientId}
      AND source_entity_type = 'client_knowledge_synthesis'
      AND source_entity_id = ${input.clientId}
    ORDER BY updated_at DESC
    LIMIT 1
  `;

  if (existing) {
    await prisma.$executeRaw`
      UPDATE repository_items
      SET
        item_type_code = 'client_profile_summary',
        channel_code = 'system',
        subject = ${`AI knowledge synthesis • ${input.formName}`},
        body_text = ${input.bodyText},
        summary_text = ${input.summaryText},
        metadata_json = ${JSON.stringify(input.metadataJson)},
        created_by_user_id = ${input.actorUserId},
        occurred_at = NOW(),
        updated_at = CURRENT_TIMESTAMP
      WHERE id = ${existing.id}
    `;

    return existing.id;
  }

  return createRepositoryItem({
    lawFirmId: input.lawFirmId,
    clientId: input.clientId,
    itemTypeCode: "client_profile_summary",
    channelCode: "system",
    sourceEntityType: "client_knowledge_synthesis",
    sourceEntityId: input.clientId,
    subject: `AI knowledge synthesis • ${input.formName}`,
    bodyText: input.bodyText,
    summaryText: input.summaryText,
    metadataJson: input.metadataJson,
    createdByUserId: input.actorUserId,
  });
}

export async function synthesizeClientKnowledgeBase(input: {
  lawFirmId: string;
  clientId: string;
  actorUserId: string;
  caseId?: string | null;
  formName: string;
  onProgress?: ((update: {
    stageCode: string;
    progressPercent: number;
    detailText?: string | null;
  }) => Promise<void> | void) | null;
}) {
  const dataFields = await getDataFields();
  const fieldByKey = new Map(dataFields.map((item) => [item.field_key, item]));
  const profileFieldKeys = dataFields
    .filter((field) => Boolean(clientFieldColumnMap[field.field_key]))
    .map((field) => field.field_key);

  const [clientProfile] = await prisma.$queryRaw<
    Array<{
      first_name: string;
      middle_name: string | null;
      last_name: string;
      preferred_name: string | null;
      date_of_birth: Date | null;
      email: string | null;
      phone: string | null;
      preferred_language: string | null;
      country_of_citizenship: string | null;
      immigration_status: string | null;
    }>
  >`
    SELECT
      first_name,
      middle_name,
      last_name,
      preferred_name,
      date_of_birth,
      email,
      phone,
      preferred_language,
      country_of_citizenship,
      immigration_status
    FROM clients
    WHERE id = ${input.clientId}
      AND law_firm_id = ${input.lawFirmId}
      AND deleted_at IS NULL
    LIMIT 1
  `;

  const documentRows = await prisma.$queryRaw<
    Array<{
      id: string;
      title: string;
      document_type_code: string;
      extracted_text: string | null;
      scope_code: string;
      created_at: Date;
    }>
  >`
    SELECT
      dr.id,
      dr.title,
      dr.document_type_code,
      dr.extracted_text,
      CASE
        WHEN dr.case_id IS NULL THEN 'client'
        WHEN ${input.caseId ?? null} IS NOT NULL AND dr.case_id = ${input.caseId ?? null} THEN 'case'
        ELSE 'client_case'
      END AS scope_code,
      dr.created_at
    FROM document_records dr
    WHERE dr.law_firm_id = ${input.lawFirmId}
      AND dr.client_id = ${input.clientId}
      AND dr.extracted_text IS NOT NULL
      AND dr.extracted_text <> ''
    ORDER BY dr.created_at DESC
    LIMIT 120
  `;

  const noteRows = await prisma.$queryRaw<
    Array<{
      id: string;
      item_type_code: string;
      subject: string | null;
      body_text: string | null;
      created_at: Date;
    }>
  >`
    SELECT id, item_type_code, subject, body_text, created_at
    FROM repository_items
    WHERE law_firm_id = ${input.lawFirmId}
      AND client_id = ${input.clientId}
      AND body_text IS NOT NULL
      AND body_text <> ''
      AND item_type_code NOT IN ('document', 'generated_form', 'final_packet')
      AND (
        source_entity_type IS NULL OR source_entity_type NOT IN (
          'client_knowledge_synthesis',
          'client_knowledge_synthesis_specialized'
        )
      )
    ORDER BY occurred_at DESC, created_at DESC
    LIMIT 120
  `;

  const selectedDocuments = [...documentRows]
    .sort((left, right) => {
      const leftScore = scoreClientKnowledgeSource({
        sourceKind: "document",
        title: left.title,
        typeCode: left.document_type_code,
        text: left.extracted_text ?? "",
      });
      const rightScore = scoreClientKnowledgeSource({
        sourceKind: "document",
        title: right.title,
        typeCode: right.document_type_code,
        text: right.extracted_text ?? "",
      });

      return (
        rightScore - leftScore ||
        new Date(right.created_at).getTime() - new Date(left.created_at).getTime()
      );
    })
    .slice(0, 48);

  const selectedNotes = [...noteRows]
    .sort((left, right) => {
      const leftScore = scoreClientKnowledgeSource({
        sourceKind: "note",
        title: left.subject ?? left.item_type_code,
        typeCode: left.item_type_code,
        text: left.body_text ?? "",
      });
      const rightScore = scoreClientKnowledgeSource({
        sourceKind: "note",
        title: right.subject ?? right.item_type_code,
        typeCode: right.item_type_code,
        text: right.body_text ?? "",
      });

      return (
        rightScore - leftScore ||
        new Date(right.created_at).getTime() - new Date(left.created_at).getTime()
      );
    })
    .slice(0, 24);
  const legacyKnowledge = buildLegacyEmploymentAdjustmentKnowledge(
    extractLegacyEmploymentAdjustmentArtifactsFromDocuments(
      selectedDocuments.map((document) => ({
        title: document.title,
        extracted_text: document.extracted_text,
      })),
    ),
  );

  const knowledgeSources = [
    ...selectedDocuments.map((document) => ({
      sourceKind: "document" as const,
      title: document.title,
      typeCode: document.document_type_code,
      text: limitText(document.extracted_text, 2400),
      scopeCode: document.scope_code,
      createdAt: document.created_at,
    })),
    ...selectedNotes.map((note) => ({
      sourceKind: "note" as const,
      title: note.subject ?? note.item_type_code,
      typeCode: note.item_type_code,
      text: limitText(note.body_text, 1400),
      scopeCode: "repository",
      createdAt: note.created_at,
    })),
  ];

  if (!knowledgeSources.length && !clientProfile) {
    return null;
  }

  if (input.onProgress) {
    await input.onProgress({
      stageCode: "gathering_evidence",
      progressPercent: 24,
      detailText: `Synthesizing canonical client facts from ${knowledgeSources.length} evidence source(s).`,
    });
  }

  const aiRun = await createAiRun({
    lawFirmId: input.lawFirmId,
    caseId: input.caseId ?? null,
    clientId: input.clientId,
    runType: "fact_consolidation",
  });

  try {
    const factReviewContext = await loadFactReviewContext({
      lawFirmId: input.lawFirmId,
      clientId: input.clientId,
      caseId: input.caseId ?? null,
    });
    const sourceChunks = chunkClientKnowledgeSources(knowledgeSources, 14000, 8);
    const chunkResults: Array<{
      profileFacts: Array<{
        fieldKey: string;
        value: string;
        confidence: number;
        evidence: string;
        sourceTitles: string[];
      }>;
      knowledgeFacts: Array<{
        factKey: string;
        label: string;
        value: string;
        confidence: number;
        evidence: string;
        sourceTitles: string[];
      }>;
      summary: string;
    }> = [];
    let totalInputTokens = 0;
    let totalOutputTokens = 0;

    for (const [chunkIndex, chunk] of sourceChunks.entries()) {
      if (input.onProgress) {
        const progressBase = 26;
        const progressRange = 18;
        const progressPercent =
          progressBase + Math.round(((chunkIndex + 1) / Math.max(sourceChunks.length, 1)) * progressRange);

        await input.onProgress({
          stageCode: "gathering_evidence",
          progressPercent,
          detailText: `Reading evidence chunk ${chunkIndex + 1} of ${sourceChunks.length} for canonical fact synthesis.`,
        });
      }

      const completion = await runJsonChatCompletion({
        lawFirmId: input.lawFirmId,
        systemPrompt: [
          `You are an immigration evidence analyst preparing a canonical client knowledge base for ${input.formName}.`,
          "Return JSON only with this shape:",
          '{"profileFacts":[{"fieldKey":"string","value":"string","confidence":0.0,"evidence":"string","sourceTitles":["string"]}],"knowledgeFacts":[{"factKey":"string","label":"string","value":"string","confidence":0.0,"evidence":"string","sourceTitles":["string"]}],"summary":"string"}',
          "profileFacts.fieldKey must be one of the allowed profile field keys supplied by the user.",
          "Only include values directly supported by the evidence. Never invent values.",
          "knowledgeFacts should capture reusable USCIS/immigration facts such as aliases, gender, marital status, place of birth, nationality, parents, addresses, passport details, immigration history, arrival details, A-number, SSN, and family members when explicitly supported.",
          "If a fact is contradictory or weak, omit it from profileFacts and mention the issue in summary.",
        ].join("\n"),
        userPrompt: JSON.stringify({
          formName: input.formName,
          allowedProfileFieldKeys: profileFieldKeys,
          clientProfile:
            clientProfile ?
              {
                firstName: clientProfile.first_name,
                middleName: clientProfile.middle_name,
                lastName: clientProfile.last_name,
                preferredName: clientProfile.preferred_name,
                dateOfBirth: clientProfile.date_of_birth,
                email: clientProfile.email,
                phone: clientProfile.phone,
                preferredLanguage: clientProfile.preferred_language,
                countryOfCitizenship: clientProfile.country_of_citizenship,
                immigrationStatus: clientProfile.immigration_status,
              }
            : null,
          humanReviewGuidance: factReviewContext.guidanceLines,
          sources: chunk.map((source) => ({
            sourceKind: source.sourceKind,
            title: source.title,
            typeCode: source.typeCode,
            scopeCode: source.scopeCode,
            text: source.text,
          })),
        }),
        maxCompletionTokens: 2600,
      });

      totalInputTokens += completion.usage.inputTokens;
      totalOutputTokens += completion.usage.outputTokens;

      chunkResults.push({
        profileFacts: Array.isArray(completion.json.profileFacts) ? completion.json.profileFacts : [],
        knowledgeFacts: Array.isArray(completion.json.knowledgeFacts) ? completion.json.knowledgeFacts : [],
        summary: normalizeConsolidatedKnowledgeText(completion.json.summary),
      });
    }

    const finalCompletion =
      chunkResults.length > 1 ?
        await runJsonChatCompletion({
          lawFirmId: input.lawFirmId,
          systemPrompt: [
            `You are finalizing a canonical client knowledge base for ${input.formName}.`,
            "Merge the chunk summaries into one consolidated answer.",
            "Return JSON only with this shape:",
            '{"profileFacts":[{"fieldKey":"string","value":"string","confidence":0.0,"evidence":"string","sourceTitles":["string"]}],"knowledgeFacts":[{"factKey":"string","label":"string","value":"string","confidence":0.0,"evidence":"string","sourceTitles":["string"]}],"summary":"string"}',
            "Keep only supported facts. Prefer the strongest, most specific, and most recent evidence when conflicts exist.",
          ].join("\n"),
          userPrompt: JSON.stringify({
            formName: input.formName,
            allowedProfileFieldKeys: profileFieldKeys,
            humanReviewGuidance: factReviewContext.guidanceLines,
            chunkResults,
          }),
          maxCompletionTokens: 3200,
        })
      : null;

    if (finalCompletion) {
      totalInputTokens += finalCompletion.usage.inputTokens;
      totalOutputTokens += finalCompletion.usage.outputTokens;
    }

    const finalPayload =
      finalCompletion ?
        {
          profileFacts: Array.isArray(finalCompletion.json.profileFacts) ? finalCompletion.json.profileFacts : [],
          knowledgeFacts:
            Array.isArray(finalCompletion.json.knowledgeFacts) ? finalCompletion.json.knowledgeFacts : [],
          summary: normalizeConsolidatedKnowledgeText(finalCompletion.json.summary),
        }
      : chunkResults[0] ?? { profileFacts: [], knowledgeFacts: [], summary: "" };

    const profileFacts = Array.from(
      new Map(
        finalPayload.profileFacts
          .map((fact) => {
            const fieldKey = normalizeConsolidatedKnowledgeText(
              (fact as { fieldKey?: unknown }).fieldKey,
            );
            const dataField = fieldByKey.get(fieldKey);
            const value = normalizeConsolidatedKnowledgeText((fact as { value?: unknown }).value);
            if (!dataField || !isValidSynthesizedProfileValue(fieldKey, value)) {
              return null;
            }

            return [
              fieldKey,
              {
                fieldKey,
                label: dataField.label,
                value,
                confidence: normalizeConsolidatedKnowledgeConfidence(
                  (fact as { confidence?: unknown }).confidence,
                  0.92,
                ),
                evidence:
                  normalizeConsolidatedKnowledgeText((fact as { evidence?: unknown }).evidence) ||
                  value,
                sourceTitles: normalizeConsolidatedKnowledgeSourceTitles(
                  (fact as { sourceTitles?: unknown }).sourceTitles,
                ),
              } satisfies ConsolidatedKnowledgeProfileFact,
            ] as const;
          })
          .filter((entry): entry is readonly [string, ConsolidatedKnowledgeProfileFact] => Boolean(entry)),
      ).values(),
    )
      .sort((left, right) => right.confidence - left.confidence)
      .slice(0, 32);

    const knowledgeFacts = Array.from(
      new Map(
        finalPayload.knowledgeFacts
          .map((fact) => {
            const label = normalizeConsolidatedKnowledgeText((fact as { label?: unknown }).label);
            const value = normalizeConsolidatedKnowledgeText((fact as { value?: unknown }).value);
            if (!label || !value) {
              return null;
            }

            const factKey = normalizeConsolidatedKnowledgeFactKey(
              (fact as { factKey?: unknown }).factKey,
              label,
            );

            return [
              factKey,
              {
                factKey,
                label,
                value,
                confidence: normalizeConsolidatedKnowledgeConfidence(
                  (fact as { confidence?: unknown }).confidence,
                  0.85,
                ),
                evidence:
                  normalizeConsolidatedKnowledgeText((fact as { evidence?: unknown }).evidence) ||
                  value,
                sourceTitles: normalizeConsolidatedKnowledgeSourceTitles(
                  (fact as { sourceTitles?: unknown }).sourceTitles,
                ),
              } satisfies ConsolidatedKnowledgeFact,
            ] as const;
          })
          .filter((entry): entry is readonly [string, ConsolidatedKnowledgeFact] => Boolean(entry)),
      ).values(),
    )
      .sort((left, right) => right.confidence - left.confidence || left.label.localeCompare(right.label))
      .slice(0, 180);

    const summaryText =
      normalizeConsolidatedKnowledgeText(finalPayload.summary) ||
      buildClientKnowledgeSummary({
        formName: input.formName,
        summary: `Synthesized ${profileFacts.length} profile facts and ${knowledgeFacts.length} additional facts from the client repository.`,
        profileFacts,
        knowledgeFacts: knowledgeFacts.slice(0, 40),
      });
    const mergedKnowledge =
      extendConsolidatedKnowledgeWithLegacyEmploymentAdjustment({
        consolidatedKnowledge: {
          aiRunId: aiRun.id,
          repositoryItemId: "",
          summaryText,
          profileFacts,
          knowledgeFacts,
          sourceDocumentCount: selectedDocuments.length,
          sourceNoteCount: selectedNotes.length,
        },
        legacyKnowledge,
      }) ?? {
        aiRunId: aiRun.id,
        repositoryItemId: "",
        summaryText,
        profileFacts,
        knowledgeFacts,
        sourceDocumentCount: selectedDocuments.length,
        sourceNoteCount: selectedNotes.length,
      };

    const bodyText = JSON.stringify({
      kind: "client_knowledge_synthesis",
      generatedAt: new Date().toISOString(),
      formName: input.formName,
      profileFacts: mergedKnowledge.profileFacts,
      knowledgeFacts: mergedKnowledge.knowledgeFacts,
      sourceDocumentCount: selectedDocuments.length,
      sourceNoteCount: selectedNotes.length,
      summaryText: mergedKnowledge.summaryText,
    });

    const repositoryItemId = await upsertClientKnowledgeRepositoryItem({
      lawFirmId: input.lawFirmId,
      clientId: input.clientId,
      actorUserId: input.actorUserId,
      formName: input.formName,
      bodyText,
      summaryText: mergedKnowledge.summaryText,
      metadataJson: {
        kind: "client_knowledge_synthesis",
        formName: input.formName,
        sourceDocumentCount: selectedDocuments.length,
        sourceNoteCount: selectedNotes.length,
      },
    });

    for (const profileFact of mergedKnowledge.profileFacts) {
      const dataField = fieldByKey.get(profileFact.fieldKey);
      if (!dataField) {
        continue;
      }

      const factId = await insertCaseFact({
        lawFirmId: input.lawFirmId,
        clientId: input.clientId,
        caseId: input.caseId ?? null,
        dataField,
        rawValue: profileFact.value,
        confidence: Math.max(profileFact.confidence, 0.9),
        aiRunId: aiRun.id,
        sourceContextCode: "ai_repository_synthesis",
        requiresHumanConfirmation: profileFact.confidence < 0.9,
        reviewContext: factReviewContext,
      });

      if (!factId) {
        continue;
      }

      await insertFactSource({
        caseFactId: factId,
        sourceType: "repository_item",
        sourceId: repositoryItemId,
        repositoryItemId,
        aiRunId: aiRun.id,
        evidenceExcerpt: profileFact.evidence.slice(0, 255),
        confidenceContribution: profileFact.confidence,
      });

      if (clientFieldColumnMap[profileFact.fieldKey]) {
        await syncClientStructuredData({
          clientId: input.clientId,
          fieldKey: profileFact.fieldKey,
          rawValue: profileFact.value,
        });
      }
    }

    await finishAiRun({
      aiRunId: aiRun.id,
      status: "completed",
      inputTokens: totalInputTokens,
      outputTokens: totalOutputTokens,
      estimatedCost: 0,
    });

    return {
      aiRunId: aiRun.id,
      repositoryItemId,
      summaryText: mergedKnowledge.summaryText,
      profileFacts: mergedKnowledge.profileFacts,
      knowledgeFacts: mergedKnowledge.knowledgeFacts,
      sourceDocumentCount: selectedDocuments.length,
      sourceNoteCount: selectedNotes.length,
    } satisfies ConsolidatedClientKnowledge;
  } catch (error) {
    await finishAiRun({
      aiRunId: aiRun.id,
      status: "failed",
      errorMessage: error instanceof Error ? error.message : "Unexpected error",
    });
    throw error;
  }
}

function buildI485FieldSearchText(input: {
  pdfFieldName: string;
  label: string;
  dataType?: string | null;
}) {
  return normalizeKnowledgeSearchText(
    `${input.pdfFieldName} ${input.label} ${input.dataType ?? ""}`,
  );
}

function isI485AdministrativeField(input: {
  pdfFieldName: string;
  label: string;
  dataType?: string | null;
}) {
  const haystack = buildI485FieldSearchText(input);
  return (
    haystack.includes("pdf417barcode") ||
    haystack.includes("read only field") ||
    /\b(attorney|accredited representative|g-28|interpreter|preparer|uscis officer|volag)\b/.test(
      haystack,
    ) ||
    /\b(signature|date of signature|signed|subscribed to and sworn|officer s signature)\b/.test(
      haystack,
    )
  );
}

const I485_SECTION_DEFINITIONS: I485SectionDefinition[] = [
  {
    code: "identity_biographic",
    title: "Identity and Biographic Profile",
    description:
      "Principal applicant legal name, aliases, date of birth, sex, place of birth, citizenship, A-number, USCIS account number, and Social Security details.",
    keywords: [
      "current legal name",
      "other names",
      "date of birth",
      "sex",
      "place of birth",
      "country of birth",
      "country of citizenship",
      "alien registration number",
      "uscis online account",
      "social security",
      "nationality",
      "ssn",
    ],
    matchesField: (field) => {
      const haystack = buildI485FieldSearchText(field);
      return (
        !isI485AdministrativeField(field) &&
        ((/part 1\./.test(haystack) &&
          /\b(current legal name|other names|dates? of birth|place of birth|sex|citizenship|nationality|alien registration number|uscis online account|social security)\b/.test(
            haystack,
          )) ||
          /\balien registration number\b/.test(haystack))
      );
    },
  },
  {
    code: "immigration_history",
    title: "Immigration and Travel History",
    description:
      "Passport, travel document, visa, port of entry, date of last arrival, I-94, class of admission, authorized stay, and current immigration status.",
    keywords: [
      "passport",
      "travel document",
      "visa",
      "arrival",
      "i-94",
      "class of admission",
      "date of last arrival",
      "authorized stay",
      "immigration status",
      "crewman",
      "seaman",
      "port of entry",
    ],
    matchesField: (field) => {
      const haystack = buildI485FieldSearchText(field);
      return (
        !isI485AdministrativeField(field) &&
        (/part 1\./.test(haystack) &&
          /\b(recent immigration history|passport|travel document|visa|arrival|i-94|immigration status|authorized stay|crewman|seaman|class of admission)\b/.test(
            haystack,
          ))
      );
    },
  },
  {
    code: "addresses_and_contact",
    title: "Addresses and Contact",
    description:
      "Current physical address, mailing address, prior addresses, address history, most recent foreign address, and other direct applicant contact fields.",
    keywords: [
      "physical address",
      "mailing address",
      "prior address",
      "address history",
      "most recent address outside the united states",
      "city",
      "state",
      "zip code",
      "province",
      "postal code",
      "country",
      "resided",
      "phone",
      "email",
    ],
    matchesField: (field) => {
      const haystack = buildI485FieldSearchText(field);
      return (
        !isI485AdministrativeField(field) &&
        ((field.pdfFieldName.includes("pt1line18") ||
          (/part 1\./.test(haystack) &&
          /\b(address|mailing|resided|city or town|street number and name|zip code|postal code|province|incareofname)\b/.test(
            haystack,
          ))) ||
          (/part 10\./.test(haystack) &&
            /\b(phone|mobile|email|contact information)\b/.test(haystack)))
      );
    },
  },
  {
    code: "application_basis",
    title: "Application Basis and Filing Category",
    description:
      "Immigrant category, filing basis, derivative status, public-charge exemptions, employment-based basis, and related category selections.",
    keywords: [
      "application type",
      "filing category",
      "immigrant category",
      "employment-based",
      "family-based",
      "derivative applicant",
      "i-140",
      "principal applicant",
      "public charge exemption",
      "affidavit of support exemption",
    ],
    matchesField: (field) => {
      const haystack = buildI485FieldSearchText(field);
      return (
        !isI485AdministrativeField(field) &&
        (/\bpart 2\./.test(haystack) ||
          /\bpart 3\./.test(haystack) ||
          /\bapplication type or filing category\b/.test(haystack) ||
          /\bemployment-based\b/.test(haystack) ||
          /\bfamily-based\b/.test(haystack) ||
          field.pdfFieldName.includes("pt2line") ||
          field.pdfFieldName.includes("pt3line"))
      );
    },
  },
  {
    code: "background_employment_and_consular",
    title: "Employment, School, and Consular History",
    description:
      "Consular processing history, prior employer or school details, and employer or school addresses relevant to the application.",
    keywords: [
      "employer",
      "school",
      "consulate",
      "embassy",
      "employment",
      "address of employer",
      "address of school",
      "outside of the united states",
    ],
    matchesField: (field) => {
      const haystack = buildI485FieldSearchText(field);
      return !isI485AdministrativeField(field) && /\bpart 4\./.test(haystack);
    },
  },
  {
    code: "parents",
    title: "Parents",
    description:
      "Parent names, names at birth, dates of birth, countries of birth, and other parent-specific identifying information.",
    keywords: [
      "parent 1",
      "parent 2",
      "name at birth",
      "parent date of birth",
      "parent country of birth",
      "mother",
      "father",
      "parents",
    ],
    matchesField: (field) => {
      const haystack = buildI485FieldSearchText(field);
      return !isI485AdministrativeField(field) && /\bpart 5\./.test(haystack);
    },
  },
  {
    code: "marital_history",
    title: "Marital History and Spouse",
    description:
      "Current marital status, spouse details, marriage place and date, prior marriages, and marriage termination details.",
    keywords: [
      "marital status",
      "spouse",
      "marriage",
      "prior spouse",
      "current spouse",
      "times married",
      "date of marriage",
      "place of marriage",
      "legally ended",
    ],
    matchesField: (field) => {
      const haystack = buildI485FieldSearchText(field);
      return !isI485AdministrativeField(field) && /\bpart 6\./.test(haystack);
    },
  },
  {
    code: "children",
    title: "Children and Dependents",
    description:
      "Number of children and detailed child information, including names, dates of birth, A-numbers, and countries of birth or citizenship.",
    keywords: [
      "children",
      "child 1",
      "child 2",
      "sons and daughters",
      "dependents",
      "date of birth",
      "country of birth",
      "a-number",
    ],
    matchesField: (field) => {
      const haystack = buildI485FieldSearchText(field);
      return !isI485AdministrativeField(field) && /\bpart 7\./.test(haystack);
    },
  },
  {
    code: "biographic_information",
    title: "Biographic Information",
    description:
      "Ethnicity, race, height, weight, eye color, hair color, and similar biographic attributes.",
    keywords: [
      "ethnicity",
      "race",
      "height",
      "weight",
      "eye color",
      "hair color",
      "biographic information",
    ],
    matchesField: (field) => {
      const haystack = buildI485FieldSearchText(field);
      return !isI485AdministrativeField(field) && /\bpart 8\./.test(haystack);
    },
  },
  {
    code: "inadmissibility",
    title: "General Eligibility and Inadmissibility",
    description:
      "Yes/No admissibility questions, criminal history, immigration violations, public charge, health, security, and related eligibility grounds.",
    keywords: [
      "inadmissibility",
      "general eligibility",
      "criminal acts",
      "immigration violations",
      "public charge",
      "security",
      "health",
      "drug",
      "removal",
      "yes no",
    ],
    matchesField: (field) => {
      const haystack = buildI485FieldSearchText(field);
      return !isI485AdministrativeField(field) && /\bpart 9\./.test(haystack);
    },
  },
];

function selectRelevantPlainItems<T>(
  rows: T[],
  searchTerms: string[],
  getText: (row: T) => string,
  limit: number,
) {
  return rows
    .map((row, index) => ({
      row,
      score: scoreKnowledgeDocument(getText(row), searchTerms),
      index,
    }))
    .sort((left, right) => right.score - left.score || left.index - right.index)
    .slice(0, limit)
    .map((item) => item.row);
}

async function loadClientFormAiEvidenceBundle(input: {
  lawFirmId: string;
  clientId: string;
  caseId?: string | null;
}) {
  const [clientProfile] = await prisma.$queryRaw<Array<ClientFormAiProfile>>`
    SELECT
      first_name,
      middle_name,
      last_name,
      preferred_name,
      date_of_birth,
      email,
      phone,
      preferred_language,
      country_of_citizenship,
      immigration_status
    FROM clients
    WHERE id = ${input.clientId}
      AND deleted_at IS NULL
    LIMIT 1
  `;

  const facts = await listBestClientFactsForAi({
    lawFirmId: input.lawFirmId,
    clientId: input.clientId,
    caseId: input.caseId ?? null,
  });

  const documents = await prisma.$queryRaw<Array<ClientFormAiDocumentEvidence>>`
    SELECT
      title,
      document_type_code,
      extracted_text,
      CASE
        WHEN case_id IS NULL THEN 'client'
        WHEN ${input.caseId ?? null} IS NOT NULL AND case_id = ${input.caseId ?? null} THEN 'case'
        ELSE 'client_case'
      END AS scope_code,
      created_at
    FROM document_records
    WHERE law_firm_id = ${input.lawFirmId}
      AND client_id = ${input.clientId}
      AND extracted_text IS NOT NULL
      AND extracted_text <> ''
    ORDER BY created_at DESC
    LIMIT 120
  `;

  const repositoryNotes = await prisma.$queryRaw<Array<ClientFormAiRepositoryNote>>`
    SELECT item_type_code, subject, body_text, created_at
    FROM repository_items
    WHERE law_firm_id = ${input.lawFirmId}
      AND client_id = ${input.clientId}
      AND body_text IS NOT NULL
      AND body_text <> ''
      AND item_type_code NOT IN ('document', 'generated_form', 'final_packet')
      AND (
        source_entity_type IS NULL OR source_entity_type NOT IN (
          'client_knowledge_synthesis',
          'client_knowledge_synthesis_specialized'
        )
      )
    ORDER BY occurred_at DESC, created_at DESC
    LIMIT 120
  `;

  return {
    clientProfile: clientProfile ?? null,
    facts,
    documents,
    repositoryNotes,
  } satisfies ClientFormAiEvidenceBundle;
}

async function upsertSpecializedClientKnowledgeRepositoryItem(input: {
  lawFirmId: string;
  clientId: string;
  actorUserId: string;
  formName: string;
  agentCode: string;
  bodyText: string;
  summaryText: string;
  metadataJson: Record<string, unknown>;
}) {
  const sourceEntityId = createHash("sha256")
    .update(`${input.clientId}:${input.agentCode}`)
    .digest("hex")
    .slice(0, 36);
  const [existing] = await prisma.$queryRaw<Array<{ id: string }>>`
    SELECT id
    FROM repository_items
    WHERE law_firm_id = ${input.lawFirmId}
      AND client_id = ${input.clientId}
      AND source_entity_type = 'client_knowledge_synthesis_specialized'
      AND source_entity_id = ${sourceEntityId}
    ORDER BY updated_at DESC
    LIMIT 1
  `;

  if (existing) {
    await prisma.$executeRaw`
      UPDATE repository_items
      SET
        item_type_code = 'client_profile_summary',
        channel_code = 'system',
        subject = ${`AI canonical dossier • ${input.formName}`},
        body_text = ${input.bodyText},
        summary_text = ${input.summaryText},
        metadata_json = ${JSON.stringify(input.metadataJson)},
        created_by_user_id = ${input.actorUserId},
        occurred_at = NOW(),
        updated_at = CURRENT_TIMESTAMP
      WHERE id = ${existing.id}
    `;

    return existing.id;
  }

  return createRepositoryItem({
    lawFirmId: input.lawFirmId,
    clientId: input.clientId,
    itemTypeCode: "client_profile_summary",
    channelCode: "system",
    sourceEntityType: "client_knowledge_synthesis_specialized",
    sourceEntityId,
    subject: `AI canonical dossier • ${input.formName}`,
    bodyText: input.bodyText,
    summaryText: input.summaryText,
    metadataJson: input.metadataJson,
    createdByUserId: input.actorUserId,
  });
}

function normalizeI485StringList(value: unknown, limit = 16) {
  if (!Array.isArray(value)) {
    return [] as string[];
  }

  return Array.from(
    new Set(
      value
        .map((item) => normalizeConsolidatedKnowledgeText(item))
        .filter(Boolean),
    ),
  ).slice(0, limit);
}

function normalizeI485SectionFacts(value: unknown) {
  if (!Array.isArray(value)) {
    return [] as I485SectionFact[];
  }

  return value
    .map((entry) => {
      if (!entry || typeof entry !== "object") {
        return null;
      }

      const label = normalizeConsolidatedKnowledgeText((entry as { label?: unknown }).label);
      const factValue = normalizeConsolidatedKnowledgeText((entry as { value?: unknown }).value);

      if (!label || !factValue) {
        return null;
      }

      return {
        factKey: normalizeConsolidatedKnowledgeFactKey(
          (entry as { factKey?: unknown }).factKey,
          label,
        ),
        label,
        value: factValue,
        confidence: normalizeConsolidatedKnowledgeConfidence(
          (entry as { confidence?: unknown }).confidence,
          0.82,
        ),
        evidence:
          normalizeConsolidatedKnowledgeText((entry as { evidence?: unknown }).evidence) ||
          factValue,
        sourceTitles: normalizeConsolidatedKnowledgeSourceTitles(
          (entry as { sourceTitles?: unknown }).sourceTitles,
        ),
      } satisfies I485SectionFact;
    })
    .filter((entry): entry is I485SectionFact => Boolean(entry))
    .slice(0, 36);
}

function buildI485SectionSearchTerms(
  definition: I485SectionDefinition,
  fields: Array<{
    pdfFieldName: string;
    label: string;
    dataType: string;
    instructions?: string | null;
    sectionName?: string | null;
    pageNumber?: number | null;
  }>,
) {
  return extractKnowledgeSearchTerms(
    `${definition.title} ${definition.description} ${definition.keywords.join(" ")} ${fields
      .map((field) => `${field.label} ${field.instructions ?? ""}`)
      .join(" ")}`,
  );
}

function selectRelevantConsolidatedProfileFacts(
  facts: ConsolidatedKnowledgeProfileFact[],
  searchTerms: string[],
  limit: number,
) {
  return selectRelevantPlainItems(
    facts,
    searchTerms,
    (fact) => `${fact.fieldKey} ${fact.label} ${fact.value} ${fact.evidence}`,
    limit,
  );
}

function selectRelevantConsolidatedKnowledgeFacts(
  facts: ConsolidatedKnowledgeFact[],
  searchTerms: string[],
  limit: number,
) {
  return selectRelevantPlainItems(
    facts,
    searchTerms,
    (fact) => `${fact.factKey} ${fact.label} ${fact.value} ${fact.evidence} ${fact.sourceTitles.join(" ")}`,
    limit,
  );
}

function selectRelevantClientFacts(
  facts: ClientFormAiFact[],
  searchTerms: string[],
  limit: number,
) {
  return selectRelevantPlainItems(
    facts,
    searchTerms,
    (fact) => `${fact.fieldKey} ${fact.label} ${fact.rawValue ?? ""}`,
    limit,
  );
}

function buildI485CanonicalDossierFallbackOverview(sections: I485SectionDossier[]) {
  const sectionSummaries = sections
    .map((section) => {
      const summary = normalizeConsolidatedKnowledgeText(section.summary);
      if (!summary) {
        return null;
      }
      return `${section.title}: ${summary}`;
    })
    .filter((entry): entry is string => Boolean(entry));

  if (!sectionSummaries.length) {
    return "Canonical I-485 dossier prepared from the client repository.";
  }

  return sectionSummaries.join(" ");
}

function normalizeI485SourceText(value: string | null | undefined) {
  return String(value ?? "")
    .replace(/\r/g, "\n")
    .replace(/[ \t]+/g, " ")
    .replace(/\n{3,}/g, "\n\n")
    .trim();
}

function getI485SectionPartNumber(sectionCode: string) {
  switch (sectionCode) {
    case "identity_biographic":
    case "immigration_history":
    case "addresses_and_contact":
      return 1;
    case "application_basis":
      return 2;
    case "background_employment_and_consular":
      return 3;
    case "parents":
      return 5;
    case "marital_history":
      return 6;
    case "children":
      return 7;
    case "biographic_information":
      return 8;
    case "inadmissibility":
      return 9;
    default:
      return null;
  }
}

function extractSectionAwareTextWindows(input: {
  rawText: string | null | undefined;
  section: I485SectionDefinition;
  fieldLabels: string[];
  maxChars?: number;
}) {
  const normalizedText = normalizeI485SourceText(input.rawText);
  if (!normalizedText) {
    return "";
  }

  if (normalizedText.length <= 3200) {
    return limitText(normalizedText, input.maxChars ?? 3200);
  }

  const lowerText = normalizedText.toLowerCase();
  const keywordPool = [
    ...input.section.keywords,
    ...input.fieldLabels.flatMap((label) =>
      String(label ?? "")
        .split(/[.:()]/)
        .map((item) => item.trim())
        .filter((item) => item.length >= 8),
    ),
  ]
    .map((item) => item.toLowerCase().trim())
    .filter((item) => item.length >= 4)
    .slice(0, 48);

  const windows: Array<{ start: number; end: number }> = [];
  for (const keyword of keywordPool) {
    let startIndex = 0;
    let hits = 0;
    while (hits < 2) {
      const matchIndex = lowerText.indexOf(keyword, startIndex);
      if (matchIndex < 0) {
        break;
      }
      const start = Math.max(0, matchIndex - 650);
      const end = Math.min(normalizedText.length, matchIndex + keyword.length + 1350);
      windows.push({ start, end });
      startIndex = matchIndex + keyword.length;
      hits += 1;
    }
  }

  const partNumber = getI485SectionPartNumber(input.section.code);
  if (partNumber) {
    const startPattern = new RegExp(`\\bPart\\s+${partNumber}\\b`, "i");
    const startMatch = startPattern.exec(normalizedText);
    if (startMatch && startMatch.index >= 0) {
      const start = startMatch.index;
      const tail = normalizedText.slice(start + startMatch[0].length);
      let end = normalizedText.length;
      for (let nextPart = partNumber + 1; nextPart <= 14; nextPart += 1) {
        const nextPattern = new RegExp(`\\bPart\\s+${nextPart}\\b`, "i");
        const nextIndex = tail.search(nextPattern);
        if (nextIndex >= 0) {
          end = start + startMatch[0].length + nextIndex;
          break;
        }
      }
      windows.push({ start, end: Math.min(end, start + 9000) });
    }
  }

  if (!windows.length) {
    return limitText(normalizedText, input.maxChars ?? 3200);
  }

  windows.sort((left, right) => left.start - right.start);
  const merged: Array<{ start: number; end: number }> = [];
  for (const window of windows) {
    const previous = merged.at(-1);
    if (!previous || window.start > previous.end + 120) {
      merged.push({ ...window });
      continue;
    }
    previous.end = Math.max(previous.end, window.end);
  }

  const excerpts: string[] = [];
  let totalLength = 0;
  for (const window of merged) {
    const excerpt = normalizedText.slice(window.start, window.end).trim();
    if (!excerpt) {
      continue;
    }
    excerpts.push(excerpt);
    totalLength += excerpt.length;
    if (totalLength >= (input.maxChars ?? 3200) * 1.5) {
      break;
    }
  }

  return limitText(excerpts.join("\n\n---\n\n"), input.maxChars ?? 3200);
}

function buildI485SectionDocumentExcerpt(input: {
  document: ClientFormAiDocumentEvidence;
  section: I485SectionDefinition;
  fieldLabels: string[];
  maxChars?: number;
}) {
  if (shouldUseSectionAwareI485Extraction(input.document)) {
    return extractSectionAwareTextWindows({
      rawText: input.document.extracted_text,
      section: input.section,
      fieldLabels: input.fieldLabels,
      maxChars: input.maxChars ?? 4200,
    });
  }

  return limitText(input.document.extracted_text, input.maxChars ?? 2400);
}

function decodeI485DocumentTextForParsing(value: string | null | undefined) {
  return String(value ?? "")
    .replace(/\\r\\n/g, "\n")
    .replace(/\\n/g, "\n")
    .replace(/\\t/g, "\t")
    .replace(/\r/g, "\n")
    .replace(/\u00a0/g, " ")
    .replace(/[ \t]+/g, " ")
    .replace(/\n{3,}/g, "\n\n")
    .trim();
}

function normalizeI485DateInput(value: string | null | undefined) {
  return String(value ?? "")
    .replace(/[–—]/g, "-")
    .replace(/\s+/g, " ")
    .replace(/\s*,\s*/g, ", ")
    .trim();
}

function formatI485UsDate(value: string | null | undefined) {
  const raw = normalizeI485DateInput(value);
  if (!raw) {
    return null;
  }

  const numericMatch = raw.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/);
  if (numericMatch) {
    const [, month, day, year] = numericMatch;
    return `${month.padStart(2, "0")}/${day.padStart(2, "0")}/${year}`;
  }

  const isoMatch = raw.match(/^(\d{4})-(\d{2})-(\d{2})$/);
  if (isoMatch) {
    const [, year, month, day] = isoMatch;
    return `${month}/${day}/${year}`;
  }

  const monthMap: Record<string, string> = {
    january: "01",
    february: "02",
    march: "03",
    april: "04",
    may: "05",
    june: "06",
    july: "07",
    august: "08",
    september: "09",
    october: "10",
    november: "11",
    december: "12",
    jan: "01",
    feb: "02",
    mar: "03",
    apr: "04",
    jun: "06",
    jul: "07",
    aug: "08",
    sep: "09",
    sept: "09",
    oct: "10",
    nov: "11",
    dec: "12",
  };

  const monthFirst = raw.match(/^([A-Za-z]+)\s+(\d{1,2}),?\s*(\d{4})$/i);
  if (monthFirst) {
    const month = monthMap[monthFirst[1].toLowerCase()];
    if (month) {
      return `${month}/${monthFirst[2].padStart(2, "0")}/${monthFirst[3]}`;
    }
  }

  const dayFirst = raw.match(/^(\d{1,2})\s+([A-Za-z]+)\s+(\d{4})$/i);
  if (dayFirst) {
    const month = monthMap[dayFirst[2].toLowerCase()];
    if (month) {
      return `${month}/${dayFirst[1].padStart(2, "0")}/${dayFirst[3]}`;
    }
  }

  return null;
}

function extractI485DateRange(value: string | null | undefined) {
  const normalized = normalizeI485DateInput(value);
  if (!normalized) {
    return { from: null, to: null };
  }

  const parts = normalized.split(/\s*-\s*/).map((item) => item.trim()).filter(Boolean);
  if (parts.length < 2) {
    return { from: formatI485UsDate(normalized), to: null };
  }

  const from = formatI485UsDate(parts[0]);
  const toPart = parts[1].replace(/^till\s+/i, "").trim();
  const to = /^(present|current)$/i.test(toPart) ? null : formatI485UsDate(toPart);
  return { from, to };
}

function splitI485StructuredName(fullName: string | null | undefined): I485StructuredName | null {
  const normalized = normalizeConsolidatedKnowledgeText(fullName);
  if (!normalized) {
    return null;
  }

  if (normalized.includes(",")) {
    const [familyPart, restPart] = normalized.split(",", 2).map((item) => item.trim());
    const restTokens = restPart.split(/\s+/).filter(Boolean);
    return {
      familyName: familyPart || null,
      givenName: restTokens[0] ?? null,
      middleName: restTokens.slice(1).join(" ") || null,
    };
  }

  const tokens = normalized.split(/\s+/).filter(Boolean);
  if (tokens.length === 1) {
    return {
      familyName: tokens[0],
      givenName: null,
      middleName: null,
    };
  }

  if (tokens.length === 2) {
    return {
      familyName: tokens[1],
      givenName: tokens[0],
      middleName: null,
    };
  }

  const hasInitials = tokens.slice(0, -1).some((token) => /^[A-Z](?:\.[A-Z])+\.?$|^[A-Z]\.?$/i.test(token));
  if (tokens.length >= 3 && hasInitials) {
    return {
      familyName: tokens.at(-1) ?? null,
      givenName: tokens[0] ?? null,
      middleName: tokens.slice(1, -1).join(" ") || null,
    };
  }

  if (tokens.length > 3) {
    return {
      familyName: tokens.at(-1) ?? null,
      givenName: tokens.slice(0, -1).join(" ") || null,
      middleName: null,
    };
  }

  return {
    familyName: tokens[2] ?? null,
    givenName: tokens[0] ?? null,
    middleName: tokens[1] ?? null,
  };
}

function isI20Document(input: {
  title: string;
  document_type_code?: string;
  extracted_text?: string | null;
}) {
  const normalizedTitle = normalizeKnowledgeSearchText(
    `${input.title} ${input.document_type_code ?? ""}`,
  );
  const normalizedText = normalizeKnowledgeSearchText(input.extracted_text ?? "");

  return (
    normalizedTitle.includes("i 20") ||
    normalizedTitle.includes("i-20") ||
    normalizedText.includes("certificate of eligibility for nonimmigrant student status") ||
    normalizedText.includes("form i-20")
  );
}

function isNvcImmigrantVisaCaseDocument(input: {
  title: string;
  extracted_text?: string | null;
}) {
  const normalizedTitle = normalizeKnowledgeSearchText(input.title);
  const normalizedText = normalizeKnowledgeSearchText(input.extracted_text ?? "");

  return (
    normalizedTitle.includes("nvc") ||
    normalizedTitle.includes("iv fee") ||
    normalizedText.includes("national visa center") ||
    normalizedText.includes("immigrant visa case") ||
    normalizedText.includes("ceac.state.gov")
  );
}

function isSecondaryAttorneyPreparedImmigrationFormDocument(input: {
  title: string;
  document_type_code: string;
  extracted_text?: string | null;
}) {
  const normalizedTitle = normalizeKnowledgeSearchText(
    `${input.title} ${input.document_type_code}`,
  );
  const normalizedText = normalizeKnowledgeSearchText(input.extracted_text ?? "");

  if (isPrimaryI485FormDocument(input) || isSupplementJDocument(input) || isI20Document(input)) {
    return false;
  }

  return (
    normalizedTitle.includes("form g-28") ||
    normalizedTitle.includes("g 28") ||
    normalizedTitle.includes("i-765") ||
    normalizedTitle.includes("i 765") ||
    normalizedTitle.includes("i-539") ||
    normalizedTitle.includes("i 539") ||
    normalizedTitle.includes("i-539a") ||
    normalizedTitle.includes("i 539a") ||
    normalizedTitle.includes("i-907") ||
    normalizedTitle.includes("i 907") ||
    normalizedTitle.includes("g-1145") ||
    normalizedTitle.includes("g 1145") ||
    normalizedTitle.includes("cover letter") ||
    normalizedTitle.includes("retainer") ||
    normalizedTitle.includes("case sent out") ||
    normalizedText.includes("notice of entry of appearance") ||
    normalizedText.includes("application for employment authorization") ||
    normalizedText.includes("application to extend/change nonimmigrant status") ||
    normalizedText.includes("request for premium processing service")
  );
}

function extractPotentialPersonTokensFromDocumentTitle(title: string) {
  const baseTitle = String(title ?? "")
    .replace(/\.[a-z0-9]{2,5}$/i, "")
    .replace(/[_|]/g, " ")
    .replace(/[–—]/g, "-");

  const genericTokens = new Set([
    "a",
    "about",
    "adjustment",
    "aff",
    "agreement",
    "airplane",
    "approval",
    "authorization",
    "background",
    "bank",
    "based",
    "bc",
    "beneficiary",
    "biometrics",
    "birth",
    "booking",
    "case",
    "certificate",
    "change",
    "city",
    "conf",
    "confirmation",
    "consular",
    "copy",
    "cover",
    "current",
    "day",
    "dhk",
    "document",
    "draft",
    "eb3",
    "employee",
    "employer",
    "employment",
    "evidence",
    "f1",
    "f2",
    "family",
    "fd",
    "fedex",
    "fee",
    "file",
    "financial",
    "florida",
    "form",
    "g",
    "gmail",
    "headletter",
    "history",
    "hotel",
    "i",
    "iv",
    "job",
    "letter",
    "level",
    "means",
    "new",
    "notice",
    "of",
    "official",
    "order",
    "other",
    "out",
    "payment",
    "perm",
    "personal",
    "petitioner",
    "proof",
    "questionnaire",
    "receipt",
    "recruitment",
    "request",
    "residence",
    "response",
    "retainer",
    "rfe",
    "scan",
    "se",
    "sent",
    "service",
    "signed",
    "social",
    "statement",
    "status",
    "step",
    "supplement",
    "tax",
    "ticket",
    "tracking",
    "transfer",
    "travel",
    "u",
    "usa",
    "uscis",
    "v",
    "version",
    "visa",
    "visas",
    "website",
  ]);

  const segments = baseTitle
    .split(/\s*-\s*|\(|\)|,/)
    .map((segment) => segment.trim())
    .filter(Boolean);

  const tokenSet = new Set<string>();
  for (const segment of segments) {
    const rawTokens = segment
      .split(/\s+/)
      .map((token) =>
        token
          .replace(/^[^A-Za-z]+|[^A-Za-z.]+$/g, "")
          .trim()
          .toLowerCase(),
      )
      .filter(Boolean);

    const candidateTokens = rawTokens.filter((token) => {
      if (genericTokens.has(token)) {
        return false;
      }
      if (/^\d+$/.test(token)) {
        return false;
      }
      if (/^[igf]\d+[a-z]?$/i.test(token)) {
        return false;
      }
      if (token.length === 1 && !/^[a-z]\.$/i.test(token)) {
        return false;
      }
      return /[a-z]/i.test(token);
    });

    if (!candidateTokens.length || candidateTokens.length > 4) {
      continue;
    }

    for (const token of candidateTokens) {
      tokenSet.add(token.replace(/\.+$/g, ""));
    }
  }

  return Array.from(tokenSet);
}

function isLikelyPrincipalApplicantDocument(
  input: ClientFormAiDocumentEvidence,
  clientProfile: ClientFormAiProfile | null,
) {
  if (!clientProfile) {
    return true;
  }

  const normalizedTitle = normalizeKnowledgeSearchText(input.title);
  const normalizedText = normalizeKnowledgeSearchText(input.extracted_text ?? "").slice(0, 6000);
  const firstName = normalizeKnowledgeSearchText(clientProfile.first_name ?? "");
  const middleName = normalizeKnowledgeSearchText(clientProfile.middle_name ?? "");
  const lastName = normalizeKnowledgeSearchText(clientProfile.last_name ?? "");
  const fullName = [firstName, middleName, lastName].filter(Boolean).join(" ");
  const givenLast = [firstName, lastName].filter(Boolean).join(" ");
  const lastGiven = [lastName, firstName].filter(Boolean).join(" ");
  const applicantTokens = new Set(
    [firstName, middleName, lastName, givenLast, lastGiven]
      .flatMap((value) => value.split(/\s+/))
      .map((value) => value.trim())
      .filter(Boolean),
  );
  const titlePersonTokens = extractPotentialPersonTokensFromDocumentTitle(input.title);

  const titleMentionsApplicant =
    (firstName && normalizedTitle.includes(firstName)) ||
    (lastName && normalizedTitle.includes(lastName)) ||
    (givenLast && normalizedTitle.includes(givenLast)) ||
    (lastGiven && normalizedTitle.includes(lastGiven));

  const textMentionsApplicant =
    (fullName && normalizedText.includes(fullName)) ||
    (givenLast && normalizedText.includes(givenLast)) ||
    (lastGiven && normalizedText.includes(lastGiven)) ||
    (firstName && lastName && normalizedText.includes(`${lastName} ${firstName}`));

  if (
    titlePersonTokens.length &&
    !titleMentionsApplicant &&
    !titlePersonTokens.some((token) => applicantTokens.has(token))
  ) {
    return false;
  }

  if (titleMentionsApplicant || textMentionsApplicant) {
    return true;
  }

  if (
    /wafi|child 1|derivative applicant|dependent applicant/i.test(input.title) &&
    firstName &&
    !new RegExp(firstName, "i").test(input.title)
  ) {
    return false;
  }

  return !isSecondaryAttorneyPreparedImmigrationFormDocument(input);
}

function parseI485StructuredAddress(
  rawAddress: string | null | undefined,
  fallbackCountry: string | null = null,
): I485StructuredAddress | null {
  const normalized = normalizeConsolidatedKnowledgeText(rawAddress);
  if (!normalized) {
    return null;
  }

  let street = normalized;
  let unitType: I485StructuredAddress["unitType"] = null;
  let unitNumber: string | null = null;
  let city: string | null = null;
  let state: string | null = null;
  let zipCode: string | null = null;
  let province: string | null = null;
  let postalCode: string | null = null;
  let country: string | null = fallbackCountry;

  const usMatch = normalized.match(
    /^(.*?)(?:,\s*(Apt|Apartment|Unit|Ste|Suite|Fl|Floor)\s*([A-Z0-9-]+))?,\s*([^,]+),\s*([A-Z]{2})\s*(\d{5}(?:-\d{4})?)$/i,
  );
  if (usMatch) {
    street = normalizeConsolidatedKnowledgeText(usMatch[1]);
    const rawUnitType = normalizeConsolidatedKnowledgeText(usMatch[2]);
    unitNumber = normalizeConsolidatedKnowledgeText(usMatch[3]) || null;
    city = normalizeConsolidatedKnowledgeText(usMatch[4]) || null;
    state = normalizeConsolidatedKnowledgeText(usMatch[5]) || null;
    zipCode = normalizeConsolidatedKnowledgeText(usMatch[6]) || null;
    if (/^apt|apartment$/i.test(rawUnitType)) {
      unitType = "Apartment";
    } else if (/^ste|suite$/i.test(rawUnitType)) {
      unitType = "Suite";
    } else if (/^fl|floor$/i.test(rawUnitType)) {
      unitType = "Floor";
    }

    return {
      street,
      unitType,
      unitNumber,
      city,
      state,
      zipCode,
      province: null,
      postalCode: null,
      country: country || "United States",
      inCareOfName: null,
      dateFrom: null,
      dateTo: null,
    };
  }

  const parts = normalized.split(",").map((item) => item.trim()).filter(Boolean);
  if (parts.length >= 2) {
    country = country || parts.at(-1) || null;
    if (parts.length >= 3) {
      city = parts.at(-2) || null;
      street = parts.slice(0, -2).join(", ") || parts[0];
    } else {
      street = parts[0];
    }
  }

  return {
    street,
    unitType,
    unitNumber,
    city,
    state,
    zipCode,
    province,
    postalCode,
    country,
    inCareOfName: null,
    dateFrom: null,
    dateTo: null,
  };
}

function isI485QuestionnaireDocument(document: ClientFormAiDocumentEvidence) {
  const normalizedTitle = normalizeKnowledgeSearchText(document.title);
  return (
    normalizedTitle.includes("questionnaire") ||
    normalizedTitle.includes("background information") ||
    normalizedTitle.includes("personal and background")
  );
}

function isI140Document(document: ClientFormAiDocumentEvidence) {
  const normalizedTitle = normalizeKnowledgeSearchText(document.title);
  const normalizedText = normalizeKnowledgeSearchText(document.extracted_text ?? "");
  return (
    ((normalizedTitle.includes("i 140") || normalizedTitle.includes("i-140")) &&
      !isSupplementJDocument(document)) ||
    normalizedText.includes("form i 140")
  );
}

function pickLatestI485EvidenceDocument(
  documents: ClientFormAiDocumentEvidence[],
  predicate: (document: ClientFormAiDocumentEvidence) => boolean,
) {
  return (
    documents
      .filter(predicate)
      .sort(
        (left, right) =>
          new Date(right.created_at).getTime() - new Date(left.created_at).getTime(),
      )
      .at(0) ?? null
  );
}

function matchI485Text(text: string, patterns: RegExp[]) {
  for (const pattern of patterns) {
    const match = pattern.exec(text);
    if (match?.[1]) {
      return normalizeConsolidatedKnowledgeText(match[1]);
    }
  }
  return null;
}

function isPrimaryI485FormDocument(input: {
  title: string;
  document_type_code: string;
  extracted_text?: string | null;
}) {
  const normalizedTitle = normalizeKnowledgeSearchText(
    `${input.title} ${input.document_type_code}`,
  );
  const normalizedText = normalizeKnowledgeSearchText(input.extracted_text ?? "");
  const looksLikePrimaryI485Title =
    (/\bi[\s-]?485\b/i.test(input.title) && !/\bi[\s-]?485\s*[- ]?j\b/i.test(input.title)) ||
    normalizedTitle.includes("case sent out") ||
    normalizedTitle.includes("adjustment of status") ||
    normalizedTitle.includes("register permanent residence");
  const looksLikePrimaryI485Text =
    (normalizedText.includes("form i-485") || normalizedText.includes("form i 485")) &&
    !normalizedText.includes("form i-485 sup j") &&
    !normalizedText.includes("form i 485 sup j");

  return looksLikePrimaryI485Title && looksLikePrimaryI485Text;
}

function isSupplementJDocument(input: {
  title: string;
  extracted_text?: string | null;
}) {
  const normalizedTitle = normalizeKnowledgeSearchText(input.title);
  const normalizedText = normalizeKnowledgeSearchText(input.extracted_text ?? "");

  return (
    /\bi[\s-]?485\s*[- ]?j\b/i.test(input.title) ||
    normalizedTitle.includes("supplement j") ||
    normalizedText.includes("form i-485 sup j") ||
    normalizedText.includes("form i 485 sup j")
  );
}

function shouldUseSectionAwareI485Extraction(input: {
  title: string;
  document_type_code: string;
  extracted_text?: string | null;
}) {
  const normalizedTitle = normalizeKnowledgeSearchText(
    `${input.title} ${input.document_type_code}`,
  );
  const textLength = String(input.extracted_text ?? "").length;

  return (
    textLength > 2400 &&
    (
      isPrimaryI485FormDocument(input) ||
      isSupplementJDocument(input) ||
      normalizedTitle.includes("ds 160") ||
      normalizedTitle.includes("questionnaire") ||
      normalizedTitle.includes("background information") ||
      normalizedTitle.includes("personal and background history") ||
      normalizedTitle.includes("i94") ||
      normalizedTitle.includes("passport")
    )
  );
}

function isAuthoritativeI485SourceDocument(
  input: ClientFormAiDocumentEvidence,
  section: I485SectionDefinition,
) {
  if (isPrimaryI485FormDocument(input)) {
    return false;
  }
  if (isSecondaryAttorneyPreparedImmigrationFormDocument(input)) {
    return false;
  }

  const normalizedTitle = normalizeKnowledgeSearchText(
    `${input.title} ${input.document_type_code}`,
  );
  const normalizedText = normalizeKnowledgeSearchText(input.extracted_text ?? "");

  switch (section.code) {
    case "identity_biographic":
    case "immigration_history":
      return (
        input.document_type_code === "i94" ||
        input.document_type_code === "visa" ||
        input.document_type_code === "passport" ||
        input.document_type_code === "birth_certificate" ||
        normalizedTitle.includes("passport") ||
        normalizedTitle.includes("ds 160") ||
        isI20Document(input) ||
        normalizedText.includes("passport/travel document information") ||
        normalizedText.includes("form i-94")
      );
    case "addresses_and_contact":
      return (
        normalizedTitle.includes("questionnaire") ||
        normalizedTitle.includes("background information") ||
        normalizedTitle.includes("personal and background history") ||
        normalizedTitle.includes("ds 160") ||
        normalizedTitle.includes("residence abroad") ||
        normalizedTitle.includes("passport") ||
        normalizedTitle.includes("driver") ||
        normalizedTitle.includes("license") ||
        isI20Document(input) ||
        input.document_type_code === "visa"
      );
    case "application_basis":
    case "background_employment_and_consular":
      return (
        isSupplementJDocument(input) ||
        normalizedTitle.includes("i 140") ||
        normalizedTitle.includes("approval") ||
        isI20Document(input) ||
        isNvcImmigrantVisaCaseDocument(input) ||
        normalizedTitle.includes("questionnaire") ||
        normalizedTitle.includes("background information") ||
        normalizedTitle.includes("employment") ||
        normalizedTitle.includes("offer")
      );
    case "parents":
    case "marital_history":
    case "children":
      return (
        normalizedTitle.includes("questionnaire") ||
        normalizedTitle.includes("background information") ||
        normalizedTitle.includes("marriage") ||
        normalizedTitle.includes("birth") ||
        normalizedTitle.includes("passport") ||
        input.document_type_code === "birth_certificate" ||
        normalizedTitle.includes("family")
      );
    case "biographic_information":
      return (
        normalizedTitle.includes("questionnaire") ||
        normalizedTitle.includes("ds 160") ||
        input.document_type_code === "visa"
      );
    case "inadmissibility":
      return (
        normalizedTitle.includes("questionnaire") ||
        normalizedTitle.includes("background information") ||
        normalizedTitle.includes("ds 160") ||
        input.document_type_code === "visa" ||
        normalizedTitle.includes("arrest") ||
        normalizedTitle.includes("court") ||
        normalizedTitle.includes("police")
      );
    default:
      return false;
  }
}

function selectRelevantAuthoritativeI485SourceDocuments(
  documents: ClientFormAiDocumentEvidence[],
  clientProfile: ClientFormAiProfile | null,
  section: I485SectionDefinition,
  fields: Array<{
    pdfFieldName: string;
    label: string;
    dataType: string;
    instructions?: string | null;
    sectionName?: string | null;
    pageNumber?: number | null;
  }>,
  limit: number,
) {
  const partNumber = getI485SectionPartNumber(section.code);
  const searchTerms = buildI485SectionSearchTerms(section, fields);

  return documents
    .filter(
      (document) =>
        isAuthoritativeI485SourceDocument(document, section) &&
        (section.code === "children" ||
          section.code === "marital_history" ||
          isLikelyPrincipalApplicantDocument(document, clientProfile)),
    )
    .map((document) => {
      const timestamp = new Date(document.created_at).getTime();
      const extractedText = String(document.extracted_text ?? "");
      let score =
        scoreKnowledgeDocument(
          `${document.title} ${document.document_type_code} ${document.extracted_text ?? ""}`,
          searchTerms,
        ) + 10;

      if (document.document_type_code === "i94") {
        score += 16;
      }
      if (document.document_type_code === "visa") {
        score += 14;
      }
      if (isSupplementJDocument(document)) {
        score += 15;
      }
      if (/questionnaire|background information|personal and background history/i.test(document.title)) {
        score += 14;
      }
      if (/passport/i.test(document.title)) {
        score += 12;
      }
      if (partNumber && new RegExp(`\\bpart\\s+${partNumber}\\b`, "i").test(extractedText)) {
        score += 8;
      }
      if (/name provided|passport\/travel document information|country of citizenship|date of birth/i.test(extractedText)) {
        score += 6;
      }

      return {
        document,
        score,
        timestamp: Number.isNaN(timestamp) ? 0 : timestamp,
      };
    })
    .sort((left, right) => right.score - left.score || right.timestamp - left.timestamp)
    .slice(0, limit)
    .map((entry) => entry.document);
}

function mergeUniqueI485EvidenceDocuments(
  primary: ClientFormAiDocumentEvidence[],
  secondary: ClientFormAiDocumentEvidence[],
  limit: number,
) {
  const seen = new Set<string>();
  const merged: ClientFormAiDocumentEvidence[] = [];

  for (const document of [...primary, ...secondary]) {
    const key = `${document.title}::${document.document_type_code}::${new Date(document.created_at).toISOString()}`;
    if (seen.has(key)) {
      continue;
    }
    seen.add(key);
    merged.push(document);
    if (merged.length >= limit) {
      break;
    }
  }

  return merged;
}

function buildI485DeterministicSourceProfile(input: {
  evidence: ClientFormAiEvidenceBundle;
  clientId: string;
  legacyArtifacts?: LegacyEmploymentAdjustmentArtifacts | null;
}) {
  const principalDocuments = input.evidence.documents.filter((document) =>
    isLikelyPrincipalApplicantDocument(document, input.evidence.clientProfile),
  );
  const ds160Document = pickLatestI485EvidenceDocument(
    principalDocuments,
    (document) => {
      const normalizedTitle = normalizeKnowledgeSearchText(document.title);
      return (
        document.document_type_code === "visa" ||
        normalizedTitle.includes("ds 160") ||
        normalizedTitle.includes("ds160")
      );
    },
  );
  const i94Document = pickLatestI485EvidenceDocument(
    principalDocuments,
    (document) =>
      document.document_type_code === "i94" ||
      normalizeKnowledgeSearchText(document.title).includes("i94"),
  );
  const questionnaireDocument = pickLatestI485EvidenceDocument(
    principalDocuments,
    (document) => isI485QuestionnaireDocument(document),
  );
  const permQuestionnaireDocument = pickLatestI485EvidenceDocument(
    principalDocuments,
    (document) =>
      normalizeKnowledgeSearchText(document.title).includes("employee questionnaire") ||
      normalizeKnowledgeSearchText(document.title).includes("perm employee questionnaire"),
  );
  const supplementJDocument = pickLatestI485EvidenceDocument(
    principalDocuments,
    (document) => isSupplementJDocument(document),
  );
  const i140Document = pickLatestI485EvidenceDocument(
    principalDocuments,
    (document) => isI140Document(document),
  );
  const passportDocument = pickLatestI485EvidenceDocument(
    principalDocuments,
    (document) =>
      document.document_type_code === "passport" ||
      normalizeKnowledgeSearchText(document.title).includes("passport"),
  );
  const birthCertificateDocument = pickLatestI485EvidenceDocument(
    principalDocuments,
    (document) => document.document_type_code === "birth_certificate",
  );
  const i20Document = pickLatestI485EvidenceDocument(
    principalDocuments,
    (document) => isI20Document(document),
  );
  const nvcDocument = pickLatestI485EvidenceDocument(
    principalDocuments,
    (document) => isNvcImmigrantVisaCaseDocument(document),
  );

  const ds160Text = decodeI485DocumentTextForParsing(ds160Document?.extracted_text);
  const i94Text = decodeI485DocumentTextForParsing(i94Document?.extracted_text);
  const questionnaireText = decodeI485DocumentTextForParsing(questionnaireDocument?.extracted_text);
  const permQuestionnaireText = decodeI485DocumentTextForParsing(permQuestionnaireDocument?.extracted_text);
  const supplementJText = decodeI485DocumentTextForParsing(supplementJDocument?.extracted_text);
  const i140Text = decodeI485DocumentTextForParsing(i140Document?.extracted_text);
  const passportText = decodeI485DocumentTextForParsing(passportDocument?.extracted_text);
  const birthCertificateText = decodeI485DocumentTextForParsing(birthCertificateDocument?.extracted_text);
  const i20Text = decodeI485DocumentTextForParsing(i20Document?.extracted_text);
  const nvcText = decodeI485DocumentTextForParsing(nvcDocument?.extracted_text);

  const currentName =
    splitI485StructuredName(
      matchI485Text(ds160Text, [
        /\bSurname\s*[:\-]?\s*([A-Z ,.'-]+)/i,
        /\bFull Name\s*[:\-]?\s*([A-Z ,.'-]+)/i,
        /\b([A-Z][A-Z ,.'-]+,\s*[A-Z][A-Z .'-]+)/,
      ]),
    ) ??
    splitI485StructuredName(
      matchI485Text(passportText, [
        /\bName\s*\/\s*Nom.*?\n([A-Z ]+)\n([A-Z ]+)/i,
        /\bHOSSAIN\s*\nHOMAIRA BINTA\b/i,
      ]),
    ) ??
    splitI485StructuredName(
      `${input.evidence.clientProfile?.first_name ?? ""} ${input.evidence.clientProfile?.middle_name ?? ""} ${input.evidence.clientProfile?.last_name ?? ""}`,
    );

  const normalizedProfilePhone =
    normalizeConsolidatedKnowledgeText(input.evidence.clientProfile?.phone) || null;
  const normalizedProfileEmail =
    normalizeConsolidatedKnowledgeText(input.evidence.clientProfile?.email) || null;

  const currentPhone =
    matchI485Text(supplementJText, [/(\d{3}-\d{3}-\d{4})/, /(\d{3}\s*\d{3}\s*\d{4})/]) ??
    normalizedProfilePhone ??
    matchI485Text(ds160Text, [/(\d{10,15})/]);

  const currentEmail =
    matchI485Text(supplementJText, [/\b([A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,})\b/i]) ??
    matchI485Text(ds160Text, [/\b([A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,})\b/i]) ??
    normalizedProfileEmail ??
    null;

  const profile: I485DeterministicSourceProfile = {
    principalName: currentName,
    hasOtherNamesUsed: /\bOther Names Used:\s*NO\b/i.test(ds160Text) ? false : null,
    hasOtherDatesOfBirth:
      /\bHave you ever used any other date of birth\??\s*No\b/i.test(questionnaireText) ? false : null,
    alienNumber: null,
    dateOfBirth:
      formatI485UsDate(
        matchI485Text(ds160Text, [/\bDate of Birth\s*[:\-]?\s*([A-Za-z0-9, ]+)/i]),
      ) ??
      formatI485UsDate(
        matchI485Text(i94Text, [/\bDate of Birth\s*[:\-]?\s*([A-Za-z0-9, ]+)/i]),
      ) ??
      formatI485UsDate(
        matchI485Text(passportText, [/\bDate of Birth\b.*?\b(\d{2}\s+[A-Z]{3,9}\s+\d{4})\b/i]),
      ) ??
      formatI485UsDate(
        matchI485Text(birthCertificateText, [/\bDate of Birth\b[:;\s]*([0-9.\-\/]{8,12})/i]),
      ) ??
      formatI485UsDate(
        matchI485Text(i140Text, [
          /\bDate of Birth \(mm\/dd\/yyyy\)\s*\n([0-9/]{8,10})/i,
          /\b(\d{2}\/\d{2}\/\d{4})\b/,
        ]),
      ),
    sex:
      /female/i.test(ds160Text) ? "Female"
      : /male/i.test(ds160Text) ? "Male"
      : /sex\b.*?\bf\b/i.test(passportText) ? "Female"
      : /sex\b.*?\bm\b/i.test(passportText) ? "Male"
      : null,
    birthCity:
      matchI485Text(ds160Text, [/\bPlace of Birth\s*[:\-]?\s*([^,\n]+),/i]) ??
      matchI485Text(passportText, [/\bPlace of Birth\b.*?\b([A-Z][A-Z ]+)\b/i]) ??
      matchI485Text(i140Text, [/\bCity\/Town\/Village of Birth\s*\n([A-Za-z .'-]+)/i]),
    birthCountry:
      matchI485Text(ds160Text, [
        /\bPlace of Birth\s*[:\-]?\s*[^,\n]+,\s*([A-Za-z .'-]+)/i,
        /\bCountry\/Region of Origin \(Nationality\)\s*[:\-]?\s*([A-Za-z .'-]+)/i,
      ]) ??
      matchI485Text(passportText, [/\bNationality\b\s*([A-Za-z .'-]+)/i]) ??
      matchI485Text(i140Text, [/\bCountry of Birth\s*\n([A-Za-z .'-]+)/i]),
    countryOfCitizenship:
      matchI485Text(ds160Text, [
        /\bCountry\/Region of Origin \(Nationality\)\s*[:\-]?\s*([A-Za-z .'-]+)/i,
      ]) ??
      matchI485Text(i94Text, [/\bCountry of Citizenship\s*[:\-]?\s*([A-Za-z .'-]+)/i]) ??
      matchI485Text(i20Text, [/\bCOUNTRY OF CITIZENSHIP\s*\n([A-Za-z .'-]+)/i]) ??
      matchI485Text(passportText, [/\bNationality\b\s*([A-Za-z .'-]+)/i]) ??
      matchI485Text(i140Text, [/\bCountry of Citizenship or Nationality\s*\n([A-Za-z .'-]+)/i]),
    passportNumber:
      matchI485Text(ds160Text, [/\bPassport\/Travel Document Number\s*[:\-]?\s*([A-Z0-9-]+)/i]) ??
      matchI485Text(i94Text, [
        /\bDocument Number\s*[:\-]?\s*([A-Z0-9-]+)/i,
        /\bPassport Number\s*[:\-]?\s*([A-Z0-9-]+)/i,
      ]) ??
      matchI485Text(passportText, [/\bNo\. of passport\b.*?\b([A-Z0-9]{6,12})\b/i]) ??
      matchI485Text(i140Text, [/\bPassport Number\s*\n([A-Z0-9-]+)/i]),
    passportExpiryDate:
      formatI485UsDate(
        matchI485Text(ds160Text, [/\bExpiration Date\s*[:\-]?\s*([A-Za-z0-9, ]+)/i]),
      ) ??
      formatI485UsDate(
        matchI485Text(i140Text, [/\bExpiration Date for Passport or Travel Document\s*\n([0-9/]{8,10})/i]),
      ),
    passportIssuingCountry:
      matchI485Text(ds160Text, [
        /\bCountry\/Authority that Issued Passport\/Travel Document\s*[:\-]?\s*([A-Za-z .'-]+)/i,
      ]) ??
      (/government of the people's republic of bangladesh/i.test(passportText) ? "Bangladesh" : null) ??
      matchI485Text(i140Text, [/\bCountry of Issuance for Passport or Travel Document\s*\n([A-Za-z .'-]+)/i]),
    passportIssuingCity: matchI485Text(ds160Text, [/\bCity Where Issued\s*[:\-]?\s*([A-Za-z .'-]+)/i]),
    visaNumber: matchI485Text(ds160Text, [/\bVisa Number\s*[:\-]?\s*([A-Z0-9-]+)/i]),
    i94Number:
      matchI485Text(i94Text, [/\b(?:I-?94|Form I-94|Arrival-Departure Record Number)\s*[:\-]?\s*([A-Z0-9]+)/i]) ??
      (matchI485Text(i140Text, [/\bForm I-94 Arrival-Departure Record Number\s*\n([A-Z0-9 ]{8,})/i])?.replace(/\s+/g, "") ?? null),
    classOfAdmission:
      matchI485Text(i94Text, [/\bClass of Admission\s*[:\-]?\s*([A-Z0-9-]+)/i]) ??
      matchI485Text(i140Text, [/\bStatus on Form I-94.*?\n([A-Z0-9-]+)/i]),
    currentImmigrationStatus:
      matchI485Text(i20Text, [/\bClass of Admission\s*\n([A-Z0-9-]+)/i]) ??
      matchI485Text(permQuestionnaireText, [/\bType of Visa:\s*([A-Z0-9\/-]+)/i]),
    lastArrivalDate:
      formatI485UsDate(
        matchI485Text(i94Text, [/\bMost Recent Date of Entry\s*[:\-]?\s*([A-Za-z0-9, ]+)/i]),
      ) ??
      formatI485UsDate(
        matchI485Text(i140Text, [/\bDate of Last Arrival \(mm\/dd\/yyyy\)\s*\n([0-9/]{8,10})/i]),
      ),
    wasInspectedAndAdmitted: i94Text ? true : null,
    wasInspectedAndParoled: i94Text ? false : null,
    authorizedStayUntil:
      formatI485UsDate(
        matchI485Text(i94Text, [/\bAdmit Until Date\s*[:\-]?\s*([A-Za-z0-9, ]+)/i]),
      ) ??
      formatI485UsDate(
        matchI485Text(i140Text, [/\bExpiration Date of Authorized Stay Shown on Form I-94\s*\n([0-9/]{8,10})/i]),
      ),
    currentUsAddress: null,
    priorUsAddress: null,
    mostRecentForeignAddress: null,
    currentAddressFromDate: null,
    hasResidedAtCurrentAddressForFiveYears: null,
    currentPhone: currentPhone || null,
    currentMobilePhone: currentPhone || null,
    email: currentEmail,
    hasSsn: /social security number\s*[:\-]?\s*does not apply/i.test(ds160Text) ? false : null,
    ssn: matchI485Text(ds160Text, [/\bSocial Security Number\s*[:\-]?\s*([0-9-]+)/i]),
    employmentBasedCategory:
      /\bskilled worker\b/i.test(i140Text) ? "skilled_worker"
      : /\bprofessional\b/i.test(i140Text) ? "professional"
      : /\bother worker\b/i.test(i140Text) ? "other_worker"
      : /\badvanced degree\b/i.test(i140Text) ? "advanced_degree"
      : null,
    i140ReceiptNumber:
      matchI485Text(supplementJText, [/\bForm I-140 Receipt Number\s*([A-Z0-9]+)/i]) ??
      matchI485Text(supplementJText, [/\b(IOE[0-9]{10,})\b/i]),
    currentEmploymentOrSchool: null,
    mostRecentOutsideUsEmploymentOrSchool: null,
    parent1: null,
    parent2: null,
    maritalStatus:
      /marital status\s*[:\-]?\s*married/i.test(ds160Text) || /spouse information/i.test(questionnaireText)
        ? "Married"
        : null,
    spouseMilitaryStatus: null,
    currentSpouse: null,
    marriageDate: null,
    marriageCity: null,
    marriageStateOrProvince: null,
    marriageCountry: null,
    spouseApplyingWithApplicant: null,
    numberOfChildren: null,
    child1: null,
    child1ApplyingWithApplicant: null,
    child1Relationship: null,
    hasAppliedImmigrantVisaAbroad: nvcText ? true : null,
    ethnicityHispanic: null,
    raceAsian: /\basian\b/i.test(questionnaireText) ? true : null,
    heightFeet: null,
    heightInches: null,
    weightPounds: null,
    eyeColor:
      /\beye color:\s*brown\b/i.test(questionnaireText) ? "Brown"
      : /\beye color:\s*black\b/i.test(questionnaireText) ? "Black"
      : null,
    hairColor:
      /\bhair color:\s*black\b/i.test(questionnaireText) ? "Black"
      : /\bhair color:\s*brown\b/i.test(questionnaireText) ? "Brown"
      : null,
  };

  if (questionnaireText) {
    const currentUsAddressMatch = questionnaireText.match(
      /Current U\.S\. address\s*[–-]\s*(.+?)\.\s*\((.+?)\s+till present\)/i,
    );
    if (currentUsAddressMatch) {
      profile.currentUsAddress = parseI485StructuredAddress(currentUsAddressMatch[1], "United States");
      profile.currentAddressFromDate = formatI485UsDate(currentUsAddressMatch[2]);
      if (profile.currentUsAddress) {
        profile.currentUsAddress.dateFrom = profile.currentAddressFromDate;
      }
    }

    const previousAddresses = Array.from(
      questionnaireText.matchAll(
        /\n\s*\d+\.\s*([A-Za-z0-9, ]+?)\s*[–-]\s*([A-Za-z0-9, ]+?)\s*\n+\s*(.+?)(?=\n\s*\d+\.|\nMost recent address outside the U\.S|\nEmployment & Education History|$)/gi,
      ),
    );
    const sortedPriorAddresses = previousAddresses
      .map((match) => {
        const range = extractI485DateRange(`${match[1]} - ${match[2]}`);
        const address = parseI485StructuredAddress(match[3], "United States");
        if (address) {
          address.dateFrom = range.from;
          address.dateTo = range.to;
        }
        return address;
      })
      .filter((address): address is I485StructuredAddress => Boolean(address))
      .sort((left, right) => {
        const leftTime = Date.parse(left.dateTo ?? left.dateFrom ?? "1900-01-01");
        const rightTime = Date.parse(right.dateTo ?? right.dateFrom ?? "1900-01-01");
        return rightTime - leftTime;
      });
    profile.priorUsAddress = sortedPriorAddresses[0] ?? null;

    const mostRecentForeignMatch = questionnaireText.match(
      /Most recent address outside the U\.S\. .*?[–-]\s*(.+?)\n+\s*([A-Za-z0-9, ]+)\s*-\s*([A-Za-z0-9, ]+)/i,
    );
    if (mostRecentForeignMatch) {
      profile.mostRecentForeignAddress = parseI485StructuredAddress(
        mostRecentForeignMatch[1],
        "Bangladesh",
      );
      if (profile.mostRecentForeignAddress) {
        const range = extractI485DateRange(`${mostRecentForeignMatch[2]} - ${mostRecentForeignMatch[3]}`);
        profile.mostRecentForeignAddress.dateFrom = range.from;
        profile.mostRecentForeignAddress.dateTo = range.to;
      }
    }

    if (profile.currentAddressFromDate) {
      const currentDate = new Date();
      const fromDate = new Date(profile.currentAddressFromDate);
      if (!Number.isNaN(fromDate.getTime())) {
        const years = (currentDate.getTime() - fromDate.getTime()) / (365.25 * 24 * 60 * 60 * 1000);
        profile.hasResidedAtCurrentAddressForFiveYears = years >= 5;
      }
    }

    const timelineEntries = Array.from(
      questionnaireText.matchAll(
        /Name:\s*(.+?)\s+Address:\s*(.+?)\s+(Study|Position):\s*(.+?)\s+Started:\s*(.+?)\s+End:\s*(.+?)(?=\s+Name:|\s+Most recent employment\/school outside the U\.S\.|$)/gis,
      ),
    ).map((match) => {
      const range = extractI485DateRange(`${match[5]} - ${match[6]}`);
      return {
        name: normalizeConsolidatedKnowledgeText(match[1]) || null,
        occupation: normalizeConsolidatedKnowledgeText(match[4]) || null,
        address: parseI485StructuredAddress(match[2], "United States"),
        dateFrom: range.from,
        dateTo: range.to,
        sourceTitles: questionnaireDocument ? [questionnaireDocument.title] : [],
      } satisfies I485EmploymentEducationEntry;
    });
    if (timelineEntries.length) {
      profile.currentEmploymentOrSchool =
        timelineEntries.find((entry) => entry.dateTo === null) ??
        timelineEntries
          .sort(
            (left, right) =>
              Date.parse(right.dateFrom ?? "1900-01-01") -
              Date.parse(left.dateFrom ?? "1900-01-01"),
          )
          .at(0) ??
        null;
    }

    const outsideUsEmploymentMatch = questionnaireText.match(
      /Most recent employment\/school outside the U\.S\.\s+Name:\s*(.+?)\s+Address:\s*(.+?)\s+Position:\s*(.+?)\s+Started:\s*(.+?)\s+End:\s*(.+?)(?=\s+Family Information|$)/is,
    );
    if (outsideUsEmploymentMatch) {
      const range = extractI485DateRange(`${outsideUsEmploymentMatch[4]} - ${outsideUsEmploymentMatch[5]}`);
      profile.mostRecentOutsideUsEmploymentOrSchool = {
        name: normalizeConsolidatedKnowledgeText(outsideUsEmploymentMatch[1]) || null,
        occupation: normalizeConsolidatedKnowledgeText(outsideUsEmploymentMatch[3]) || null,
        address: parseI485StructuredAddress(outsideUsEmploymentMatch[2], "Bangladesh"),
        dateFrom: range.from,
        dateTo: range.to,
        sourceTitles: questionnaireDocument ? [questionnaireDocument.title] : [],
      };
    }

    const fatherMatch = questionnaireText.match(
      /Father:\s*(.+?)\s+DOB:\s*(.+?)\s+Country of Birth:\s*(.+?)(?=\s+Mother:)/is,
    );
    if (fatherMatch) {
      profile.parent1 = {
        name: splitI485StructuredName(fatherMatch[1].replace(/\(Deceased\)/i, "").trim()),
        dateOfBirth: formatI485UsDate(fatherMatch[2]),
        countryOfBirth: normalizeConsolidatedKnowledgeText(fatherMatch[3]) || null,
        address: null,
        deceased: /\(Deceased\)/i.test(fatherMatch[1]),
        sourceTitles: questionnaireDocument ? [questionnaireDocument.title] : [],
      };
    }

    const motherMatch = questionnaireText.match(
      /Mother:\s*(.+?)\s+DOB:\s*(.+?)\s+Country of Birth:\s*(.+?)(?=\s+Spouse information|\s+Biographic Information|$)/is,
    );
    if (motherMatch) {
      profile.parent2 = {
        name: splitI485StructuredName(motherMatch[1]),
        dateOfBirth: formatI485UsDate(motherMatch[2]),
        countryOfBirth: normalizeConsolidatedKnowledgeText(motherMatch[3]) || null,
        address: null,
        deceased: false,
        sourceTitles: questionnaireDocument ? [questionnaireDocument.title] : [],
      };
    }

    const spouseMatch = questionnaireText.match(
      /Spouse information.*?\n\s*Full name, date of birth, country of birth\s+(.+?)\s+DOB:\s*(.+?)\s+Country of birth:\s*(.+?)\s+Current address:\s*(.+?)\s+Date and place of marriage\s+(.+?)\((.+?)\)/is,
    );
    if (spouseMatch) {
      const marriagePlace = normalizeConsolidatedKnowledgeText(spouseMatch[6]);
      const marriagePlaceParts = marriagePlace.split(",").map((item) => item.trim()).filter(Boolean);
      profile.currentSpouse = {
        name: splitI485StructuredName(spouseMatch[1]),
        dateOfBirth: formatI485UsDate(spouseMatch[2]),
        countryOfBirth: normalizeConsolidatedKnowledgeText(spouseMatch[3]) || null,
        address: parseI485StructuredAddress(spouseMatch[4], "Bangladesh"),
        deceased: false,
        sourceTitles: questionnaireDocument ? [questionnaireDocument.title] : [],
      };
      profile.marriageDate = formatI485UsDate(spouseMatch[5]);
      profile.marriageCity = marriagePlaceParts[0] ?? null;
      profile.marriageStateOrProvince = marriagePlaceParts.length > 2 ? marriagePlaceParts[1] : null;
      profile.marriageCountry = marriagePlaceParts.at(-1) ?? null;
    }

    const childMatch = questionnaireText.match(
      /Children\/Dependents.*?\s+(.+?)\s+DOB:\s*(.+?)\s+Country of birth:\s*(.+?)(?=\s+Biographic Information|$)/is,
    );
    if (childMatch) {
      profile.numberOfChildren = "1";
      profile.child1 = {
        name: splitI485StructuredName(childMatch[1]),
        dateOfBirth: formatI485UsDate(childMatch[2]),
        countryOfBirth: normalizeConsolidatedKnowledgeText(childMatch[3]) || null,
        address: null,
        deceased: false,
        sourceTitles: questionnaireDocument ? [questionnaireDocument.title] : [],
      };
    }

    const heightMatch = questionnaireText.match(/\bHeight:\s*(\d+)\s*ft\s*(\d+)\s*inches/i);
    if (heightMatch) {
      profile.heightFeet = heightMatch[1];
      profile.heightInches = heightMatch[2];
    }

    const weightMatch = questionnaireText.match(/\bWeight:\s*(\d{2,3})\s*pounds/i);
    if (weightMatch) {
      profile.weightPounds = weightMatch[1];
    }
  }

  if (permQuestionnaireText) {
    if (!profile.currentUsAddress) {
      const permCurrentAddress = matchI485Text(permQuestionnaireText, [
        /\bCurrent U\.S\. address\s*[–-]\s*(.+?)\./i,
        /\bAddress:\s*USA:\s*(.+?)\s+City,\s*State,\s*zip code:/i,
      ]);
      profile.currentUsAddress = parseI485StructuredAddress(permCurrentAddress, "United States");
    }

    if (!profile.currentPhone) {
      profile.currentPhone = matchI485Text(permQuestionnaireText, [/\bHome telephone no:\s*\(?([0-9-]{10,15})\)?/i]);
    }
    if (!profile.currentMobilePhone) {
      profile.currentMobilePhone = matchI485Text(permQuestionnaireText, [/\bCell:\s*\(?([0-9-]{10,15})\)?/i]) ?? profile.currentPhone;
    }
    if (!profile.email) {
      profile.email = matchI485Text(permQuestionnaireText, [/\bEmail:\s*([A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,})/i]);
    }

    if (!profile.mostRecentOutsideUsEmploymentOrSchool) {
      const permJobMatch = permQuestionnaireText.match(
        /JOB\s*1\s+Name and full address of Company:\s*(.+?)\s+Type of Business:.*?Started.*?:\s*([0-9/]+),\s*left.*?:\s*([0-9/]+).*?Position:\s*(.+?)(?=\s+Job Responsibilities:|\s+JOB\s*2|\s+1st Step Dept of Labor|$)/is,
      );
      if (permJobMatch) {
        const range = extractI485DateRange(`${permJobMatch[2]} - ${permJobMatch[3]}`);
        profile.mostRecentOutsideUsEmploymentOrSchool = {
          name: normalizeConsolidatedKnowledgeText(permJobMatch[1]) || null,
          occupation: normalizeConsolidatedKnowledgeText(permJobMatch[4]) || null,
          address: parseI485StructuredAddress(permJobMatch[1], "Bangladesh"),
          dateFrom: range.from,
          dateTo: range.to,
          sourceTitles: permQuestionnaireDocument ? [permQuestionnaireDocument.title] : [],
        };
      }
    }

    const childApplyingMatch = permQuestionnaireText.match(
      /\bWafi Bin Hassan\b.*?\bApplying with you yes\b/i,
    );
    if (childApplyingMatch) {
      profile.child1ApplyingWithApplicant = true;
      profile.numberOfChildren = profile.numberOfChildren ?? "1";
    }
  }

  if (!profile.currentUsAddress) {
    const fallbackUsAddress =
      parseI485StructuredAddress(
        matchI485Text(supplementJText, [/\b24151 Beatrix Blvd.*?Port Charlotte FL 33954\b/i]),
        "United States",
      ) ?? null;
    profile.currentUsAddress = fallbackUsAddress;
  }

  if (!profile.mostRecentForeignAddress) {
    const passportAddress = matchI485Text(passportText, [
      /\bAddress.*?SANMAR SILVER SPRING.*?CHITTAGONG\b/i,
    ]);
    profile.mostRecentForeignAddress = parseI485StructuredAddress(
      passportAddress ??
        matchI485Text(permQuestionnaireText, [/\bAddress Abroad:\s*(.+?)\s+City,\s*State,\s*zip code:/i]),
      "Bangladesh",
    );
  }

  if (!profile.currentEmploymentOrSchool && i20Text) {
    const i20SchoolName = matchI485Text(i20Text, [
      /\bSCHOOL NAME\s*\n([A-Za-z0-9 &.-]+)\n([A-Za-z0-9 &.-]+)/i,
      /\bSCHOOL NAME\s*\n([A-Za-z0-9 &.-]+)/i,
    ]);
    const i20SchoolAddress = matchI485Text(i20Text, [/\bSCHOOL ADDRESS\s*\n(.+?)\nSCHOOL OFFICIAL TO CONTACT UPON ARRIVAL/is]);
    const i20Range = extractI485DateRange(
      `${matchI485Text(i20Text, [/\bPROGRAM START\/END DATE\s*\n([A-Za-z0-9\/ ]+)\s*-\s*([A-Za-z0-9\/ ]+)/i]) ?? ""}`,
    );
    profile.currentEmploymentOrSchool = {
      name: normalizeConsolidatedKnowledgeText(i20SchoolName) || null,
      occupation: "Student F-1",
      address: parseI485StructuredAddress(i20SchoolAddress, "United States"),
      dateFrom: i20Range.from,
      dateTo: i20Range.to,
      sourceTitles: i20Document ? [i20Document.title] : [],
    };
  }

  return mergeI485DeterministicSourceProfileWithLegacyCaseData({
    profile,
    caseData: input.legacyArtifacts?.caseData ?? null,
  });
}

function buildI485DeterministicAuditEntry(input: {
  sectionTitle: string;
  field: {
    pdfFieldName: string;
    label: string;
  };
  value: string;
  rationale: string;
  sourceTitles: string[];
}) {
  return {
    sectionTitle: input.sectionTitle,
    label: input.field.label,
    pdfFieldName: input.field.pdfFieldName,
    value: input.value,
    rationale: input.rationale,
    sourceTitles: input.sourceTitles,
    evidenceExcerpt: null,
    confidence: 0.97,
  } satisfies FilledFormAuditEntry;
}

function isLockedI485DeterministicAuditEntry(entry: FilledFormAuditEntry | undefined) {
  if (!entry) {
    return false;
  }

  if ((entry.confidence ?? 0) < 0.95) {
    return false;
  }

  return /extracted from|derived from|pulled from/i.test(entry.rationale);
}

function isUsableI485Scalar(value: string | null | undefined) {
  const normalized = normalizeConsolidatedKnowledgeText(value);
  if (!normalized) {
    return false;
  }

  if (normalized.length === 1) {
    return false;
  }

  if (/^(number|unknown|n\/a|na|none|null|tbd)$/i.test(normalized)) {
    return false;
  }

  if (/^_+$/.test(normalized)) {
    return false;
  }

  return true;
}

function pickFirstUsableI485Value(...values: Array<string | null | undefined>) {
  for (const value of values) {
    const normalized = normalizeConsolidatedKnowledgeText(value);
    if (isUsableI485Scalar(normalized)) {
      return normalized;
    }
  }

  return null;
}

function hasUsableI485Address(address: I485StructuredAddress | null | undefined) {
  if (!address) {
    return false;
  }

  return [
    address.street,
    address.city,
    address.state,
    address.zipCode,
    address.province,
    address.postalCode,
    address.country,
  ].some((value) => isUsableI485Scalar(value));
}

function pickPreferredI485Address(
  ...addresses: Array<I485StructuredAddress | null | undefined>
) {
  for (const address of addresses) {
    if (hasUsableI485Address(address)) {
      return address;
    }
  }

  return null;
}

function assignI485DeterministicFieldValue(
  values: Record<string, string>,
  fieldAuditEntries: Map<string, FilledFormAuditEntry>,
  field: {
    pdfFieldName: string;
    label: string;
    sectionName?: string | null;
  },
  value: string | null,
  rationale: string,
  sourceTitles: string[],
) {
  if (!value || values[field.pdfFieldName]) {
    return;
  }

  values[field.pdfFieldName] = value;
  fieldAuditEntries.set(
    field.pdfFieldName,
    buildI485DeterministicAuditEntry({
      sectionTitle: field.sectionName || "Deterministic source extraction",
      field,
      value,
      rationale,
      sourceTitles,
    }),
  );
}

function forceAssignI485DeterministicFieldValue(
  values: Record<string, string>,
  fieldAuditEntries: Map<string, FilledFormAuditEntry>,
  field: {
    pdfFieldName: string;
    label: string;
    sectionName?: string | null;
  },
  value: string | null,
  rationale: string,
  sourceTitles: string[],
) {
  if (!value) {
    return;
  }

  values[field.pdfFieldName] = value;
  fieldAuditEntries.set(
    field.pdfFieldName,
    buildI485DeterministicAuditEntry({
      sectionTitle: field.sectionName || "Deterministic source extraction",
      field,
      value,
      rationale,
      sourceTitles,
    }),
  );
}

function seedI485FieldValuesFromDeterministicSources(input: {
  fields: Array<{
    pdfFieldName: string;
    label: string;
    dataType: string;
    value: string | null;
    instructions?: string | null;
    sectionName?: string | null;
    pageNumber?: number | null;
  }>;
  values: Record<string, string>;
  fieldAuditEntries: Map<string, FilledFormAuditEntry>;
  sourceProfile: I485DeterministicSourceProfile;
  evidence: ClientFormAiEvidenceBundle;
}) {
  const principalDocuments = input.evidence.documents.filter(
    (document) =>
      isLikelyPrincipalApplicantDocument(document, input.evidence.clientProfile) &&
      !isPrimaryI485FormDocument(document) &&
      !isSecondaryAttorneyPreparedImmigrationFormDocument(document),
  );
  const questionnaireDocument = pickLatestI485EvidenceDocument(
    principalDocuments,
    (document) => isI485QuestionnaireDocument(document),
  );
  const permQuestionnaireDocument = pickLatestI485EvidenceDocument(
    principalDocuments,
    (document) =>
      normalizeKnowledgeSearchText(document.title).includes("employee questionnaire") ||
      normalizeKnowledgeSearchText(document.title).includes("perm employee questionnaire"),
  );
  const ds160Document = pickLatestI485EvidenceDocument(
    principalDocuments,
    (document) => {
      const normalizedTitle = normalizeKnowledgeSearchText(document.title);
      return document.document_type_code === "visa" || normalizedTitle.includes("ds 160");
    },
  );
  const i94Document = pickLatestI485EvidenceDocument(
    principalDocuments,
    (document) =>
      document.document_type_code === "i94" ||
      normalizeKnowledgeSearchText(document.title).includes("i94"),
  );
  const passportDocument = pickLatestI485EvidenceDocument(
    principalDocuments,
    (document) =>
      document.document_type_code === "passport" ||
      normalizeKnowledgeSearchText(document.title).includes("passport"),
  );
  const birthCertificateDocument = pickLatestI485EvidenceDocument(
    principalDocuments,
    (document) =>
      document.document_type_code === "birth_certificate" ||
      normalizeKnowledgeSearchText(document.title).includes("birth certificate"),
  );
  const questionnaireText = decodeI485DocumentTextForParsing(questionnaireDocument?.extracted_text);
  const permQuestionnaireText = decodeI485DocumentTextForParsing(permQuestionnaireDocument?.extracted_text);
  const ds160Text = decodeI485DocumentTextForParsing(ds160Document?.extracted_text);
  const i94Text = decodeI485DocumentTextForParsing(i94Document?.extracted_text);
  const passportText = decodeI485DocumentTextForParsing(passportDocument?.extracted_text);
  const birthCertificateText = decodeI485DocumentTextForParsing(birthCertificateDocument?.extracted_text);

  const questionnaireCurrentUsAddressMatch = questionnaireText.match(
    /Current U\.S\. address\s*[–-]\s*(.+?)\.\s*\((.+?)\s+till present\)/i,
  );
  const resolvedCurrentUsAddress =
    pickPreferredI485Address(
      parseI485StructuredAddress(questionnaireCurrentUsAddressMatch?.[1], "United States"),
      parseI485StructuredAddress(
        matchI485Text(permQuestionnaireText, [
          /\bAddress:\s*USA:\s*(.+?)\s+City,\s*State,\s*zip code:/i,
        ]),
        "United States",
      ),
      input.sourceProfile.currentUsAddress,
    );
  const resolvedCurrentAddressFromDate =
    pickFirstUsableI485Value(
      formatI485UsDate(questionnaireCurrentUsAddressMatch?.[2]),
      input.sourceProfile.currentAddressFromDate,
    );
  if (resolvedCurrentUsAddress && !resolvedCurrentUsAddress.dateFrom && resolvedCurrentAddressFromDate) {
    resolvedCurrentUsAddress.dateFrom = resolvedCurrentAddressFromDate;
  }

  const priorUsAddressMatches = Array.from(
    questionnaireText.matchAll(
      /\n\s*\d+\.\s*([A-Za-z0-9, ]+?)\s*[–-]\s*([A-Za-z0-9, ]+?)\s*\n+\s*(.+?)(?=\n\s*\d+\.|\nMost recent address outside the U\.S|\nEmployment & Education History|$)/gi,
    ),
  );
  const resolvedPriorUsAddress =
    pickPreferredI485Address(
      priorUsAddressMatches
        .map((match) => {
          const range = extractI485DateRange(`${match[1]} - ${match[2]}`);
          const address = parseI485StructuredAddress(match[3], "United States");
          if (!address) {
            return null;
          }
          address.dateFrom = range.from;
          address.dateTo = range.to;
          return address;
        })
        .filter((address): address is I485StructuredAddress => Boolean(address))
        .sort((left, right) => {
          const leftTime = Date.parse(left.dateTo ?? left.dateFrom ?? "1900-01-01");
          const rightTime = Date.parse(right.dateTo ?? right.dateFrom ?? "1900-01-01");
          return rightTime - leftTime;
        })
        .at(0) ?? null,
      input.sourceProfile.priorUsAddress,
    );

  const foreignAddressMatch = questionnaireText.match(
    /Most recent address outside the U\.S\. .*?[–-]\s*(.+?)\n+\s*([A-Za-z0-9, ]+)\s*-\s*([A-Za-z0-9, ]+)/i,
  );
  const resolvedMostRecentForeignAddress =
    pickPreferredI485Address(
      (() => {
        const address = parseI485StructuredAddress(foreignAddressMatch?.[1], "Bangladesh");
        if (!address) {
          return null;
        }
        const range = extractI485DateRange(`${foreignAddressMatch?.[2] ?? ""} - ${foreignAddressMatch?.[3] ?? ""}`);
        address.dateFrom = range.from;
        address.dateTo = range.to;
        return address;
      })(),
      input.sourceProfile.mostRecentForeignAddress,
    );

  const resolvedHasResidedAtCurrentAddressForFiveYears =
    input.sourceProfile.hasResidedAtCurrentAddressForFiveYears ??
    (() => {
      if (!resolvedCurrentAddressFromDate) {
        return null;
      }
      const fromDate = new Date(resolvedCurrentAddressFromDate);
      if (Number.isNaN(fromDate.getTime())) {
        return null;
      }
      const years =
        (Date.now() - fromDate.getTime()) / (365.25 * 24 * 60 * 60 * 1000);
      return years >= 5;
    })();

  const employmentTimelineMatches = Array.from(
    questionnaireText.matchAll(
      /Name:\s*(.+?)\s+Address:\s*(.+?)\s+(Study|Position):\s*(.+?)\s+Started:\s*(.+?)\s+End:\s*(.+?)(?=\s+Name:|\s+Most recent employment\/school outside the U\.S\.|$)/gis,
    ),
  );
  const resolvedCurrentEmploymentOrSchool =
    employmentTimelineMatches
      .map((match) => {
        const range = extractI485DateRange(`${match[5]} - ${match[6]}`);
        return {
          name: normalizeConsolidatedKnowledgeText(match[1]) || null,
          occupation: normalizeConsolidatedKnowledgeText(match[4]) || null,
          address: parseI485StructuredAddress(match[2], "United States"),
          dateFrom: range.from,
          dateTo: range.to,
          sourceTitles: questionnaireDocument ? [questionnaireDocument.title] : [],
        } satisfies I485EmploymentEducationEntry;
      })
      .find((entry) => entry.dateTo === null && isUsableI485Scalar(entry.name)) ??
    input.sourceProfile.currentEmploymentOrSchool ??
    null;

  const outsideUsEmploymentMatch = questionnaireText.match(
    /Most recent employment\/school outside the U\.S\.\s+Name:\s*(.+?)\s+Address:\s*(.+?)\s+Position:\s*(.+?)\s+Started:\s*(.+?)\s+End:\s*(.+?)(?=\s+Family Information|$)/is,
  );
  const resolvedMostRecentOutsideUsEmploymentOrSchool =
    (outsideUsEmploymentMatch
      ? (() => {
          const range = extractI485DateRange(
            `${outsideUsEmploymentMatch[4]} - ${outsideUsEmploymentMatch[5]}`,
          );
          return {
            name: normalizeConsolidatedKnowledgeText(outsideUsEmploymentMatch[1]) || null,
            occupation: normalizeConsolidatedKnowledgeText(outsideUsEmploymentMatch[3]) || null,
            address: parseI485StructuredAddress(outsideUsEmploymentMatch[2], "Bangladesh"),
            dateFrom: range.from,
            dateTo: range.to,
            sourceTitles: questionnaireDocument ? [questionnaireDocument.title] : [],
          };
        })()
      : null) ??
    (() => {
      const sourceEntry = input.sourceProfile.mostRecentOutsideUsEmploymentOrSchool;
      if (!sourceEntry) {
        return null;
      }
      if (/united states/i.test(sourceEntry.address?.country ?? "")) {
        return null;
      }
      return sourceEntry;
    })();

  const fatherMatch = questionnaireText.match(
    /Father:\s*(.+?)\s+DOB:\s*(.+?)\s+Country of Birth:\s*(.+?)(?=\s+Mother:)/is,
  );
  const motherMatch = questionnaireText.match(
    /Mother:\s*(.+?)\s+DOB:\s*(.+?)\s+Country of Birth:\s*(.+?)(?=\s+Spouse information|\s+Biographic Information|$)/is,
  );
  const resolvedParent1 =
    (fatherMatch
      ? {
          name: splitI485StructuredName(fatherMatch[1].replace(/\(Deceased\)/i, "").trim()),
          dateOfBirth: formatI485UsDate(fatherMatch[2]),
          countryOfBirth: normalizeConsolidatedKnowledgeText(fatherMatch[3]) || null,
          address: null,
          deceased: /\(Deceased\)/i.test(fatherMatch[1]),
          sourceTitles: questionnaireDocument ? [questionnaireDocument.title] : [],
        }
      : null) ??
    input.sourceProfile.parent1;
  const resolvedParent2 =
    (motherMatch
      ? {
          name: splitI485StructuredName(motherMatch[1]),
          dateOfBirth: formatI485UsDate(motherMatch[2]),
          countryOfBirth: normalizeConsolidatedKnowledgeText(motherMatch[3]) || null,
          address: null,
          deceased: false,
          sourceTitles: questionnaireDocument ? [questionnaireDocument.title] : [],
        }
      : null) ??
    input.sourceProfile.parent2;

  const spouseMatch = questionnaireText.match(
    /Spouse information.*?\n\s*Full name, date of birth, country of birth\s+(.+?)\s+DOB:\s*(.+?)\s+Country of birth:\s*(.+?)\s+Current address:\s*(.+?)\s+Date and place of marriage\s+(.+?)\((.+?)\)/is,
  );
  const marriagePlaceParts = normalizeConsolidatedKnowledgeText(spouseMatch?.[6])
    .split(",")
    .map((item) => item.trim())
    .filter(Boolean);
  const resolvedCurrentSpouse =
    (spouseMatch
      ? {
          name: splitI485StructuredName(spouseMatch[1]),
          dateOfBirth: formatI485UsDate(spouseMatch[2]),
          countryOfBirth: normalizeConsolidatedKnowledgeText(spouseMatch[3]) || null,
          address: parseI485StructuredAddress(spouseMatch[4], "Bangladesh"),
          deceased: false,
          sourceTitles: questionnaireDocument ? [questionnaireDocument.title] : [],
        }
      : null) ??
    input.sourceProfile.currentSpouse;
  const resolvedMarriageDate =
    pickFirstUsableI485Value(formatI485UsDate(spouseMatch?.[5]), input.sourceProfile.marriageDate);
  const resolvedMarriageCity =
    pickFirstUsableI485Value(marriagePlaceParts[0] ?? null, input.sourceProfile.marriageCity);
  const resolvedMarriageStateOrProvince =
    pickFirstUsableI485Value(
      marriagePlaceParts.length > 2 ? marriagePlaceParts[1] : null,
      input.sourceProfile.marriageStateOrProvince,
    );
  const resolvedMarriageCountry =
    pickFirstUsableI485Value(marriagePlaceParts.at(-1) ?? null, input.sourceProfile.marriageCountry);

  const childMatch = questionnaireText.match(
    /Children\/Dependents.*?full names, dates of birth, and countries of birth:\s+(.+?)\s+DOB:\s*(.+?)\s+Country of birth:\s*(.+?)(?=\s+Biographic Information|$)/is,
  );
  const resolvedChild1 =
    (childMatch
      ? {
          name: splitI485StructuredName(childMatch[1]),
          dateOfBirth: formatI485UsDate(childMatch[2]),
          countryOfBirth: normalizeConsolidatedKnowledgeText(childMatch[3]) || null,
          address: null,
          deceased: false,
          sourceTitles: questionnaireDocument ? [questionnaireDocument.title] : [],
        }
      : null) ??
    input.sourceProfile.child1;
  const resolvedChild1ApplyingWithApplicant =
    (/\bWafi Bin Hassan\b.*?\bApplying with you yes\b/i.test(permQuestionnaireText) ? true : null) ??
    input.sourceProfile.child1ApplyingWithApplicant;
  const resolvedNumberOfChildren =
    pickFirstUsableI485Value(resolvedChild1 ? "1" : null, input.sourceProfile.numberOfChildren);
  const resolvedChild1Relationship =
    pickFirstUsableI485Value(resolvedChild1 ? "Child" : null, input.sourceProfile.child1Relationship);

  const resolvedHeightFeet =
    pickFirstUsableI485Value(
      questionnaireText.match(/\bHeight:\s*(\d+)\s*ft\s*(\d+)\s*inches/i)?.[1] ?? null,
      input.sourceProfile.heightFeet,
    );
  const resolvedHeightInches =
    pickFirstUsableI485Value(
      questionnaireText.match(/\bHeight:\s*(\d+)\s*ft\s*(\d+)\s*inches/i)?.[2] ?? null,
      input.sourceProfile.heightInches,
    );
  const resolvedWeightPounds =
    pickFirstUsableI485Value(
      questionnaireText.match(/\bWeight:\s*(\d{2,3})\s*pounds/i)?.[1] ?? null,
      input.sourceProfile.weightPounds,
    );
  const resolvedEyeColor =
    pickFirstUsableI485Value(
      matchI485Text(questionnaireText, [/\bEye color:\s*([A-Za-z ]+)/i]),
      input.sourceProfile.eyeColor,
    );
  const resolvedHairColor =
    pickFirstUsableI485Value(
      matchI485Text(questionnaireText, [/\bHair color:\s*([A-Za-z ]+)/i]),
      input.sourceProfile.hairColor,
    );
  const resolvedCurrentPhone =
    pickFirstUsableI485Value(
      matchI485Text(ds160Text, [/\bPrimary Phone Number:\s*([0-9()+ -]{7,20})/i]),
      matchI485Text(permQuestionnaireText, [/\bHome telephone no:\s*\(?([0-9-]{10,15})\)?/i]),
      input.sourceProfile.currentPhone,
    );
  const resolvedCurrentMobilePhone =
    pickFirstUsableI485Value(input.sourceProfile.currentMobilePhone, resolvedCurrentPhone);
  const resolvedEmail =
    pickFirstUsableI485Value(
      matchI485Text(ds160Text, [/\bEmail Address:\s*([A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,})/i]),
      matchI485Text(permQuestionnaireText, [/\bEmail:\s*([A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,})/i]),
      input.sourceProfile.email,
    );
  const resolvedPassportExpiryDate =
    pickFirstUsableI485Value(
      formatI485UsDate(
        matchI485Text(ds160Text, [/\bExpiration Date:\s*([A-Za-z0-9, ]+)/i]) ??
          matchI485Text(passportText, [/\b(?:expiry|expiration).*?\b(\d{2}\s+[A-Z]{3,9}\s+\d{4})\b/i]),
      ),
      input.sourceProfile.passportExpiryDate,
    );
  const resolvedVisaIssuanceDate =
    formatI485UsDate(matchI485Text(ds160Text, [/\bIssuance Date:\s*([A-Za-z0-9, ]+)/i]));
  const resolvedBirthCity =
    pickFirstUsableI485Value(
      matchI485Text(ds160Text, [/\bPlace of Birth\s*[:\-]?\s*([^,\n]+),/i]),
      matchI485Text(passportText, [/\bPlace of Birth\b.*?\b([A-Z][A-Z ]+)\b/i]),
      matchI485Text(permQuestionnaireText, [/\bDate of Birth:.*?City\/State:\s*([^,;]+),\s*Country:/i]),
      ...principalDocuments
        .filter(
          (document) =>
            document.document_type_code === "birth_certificate" ||
            document.document_type_code === "passport" ||
            document.document_type_code === "visa",
        )
        .map((document) =>
          matchI485Text(decodeI485DocumentTextForParsing(document.extracted_text), [
            /\bPlace of Birth\s*[:\-]?\s*([^,\n]+),/i,
            /\bCity\/State:\s*([^,;]+),\s*Country:/i,
          ]),
        ),
      input.sourceProfile.birthCity,
    );
  const resolvedBirthCountry =
    pickFirstUsableI485Value(
      matchI485Text(ds160Text, [
        /\bPlace of Birth\s*[:\-]?\s*[^,\n]+,\s*([A-Za-z .'-]+)/i,
        /\bCountry\/Region of Origin \(Nationality\)\s*[:\-]?\s*([A-Za-z .'-]+)/i,
      ]),
      matchI485Text(permQuestionnaireText, [/\bDate of Birth:.*?Country:\s*([A-Za-z .'-]+)/i]),
      matchI485Text(birthCertificateText, [/\bCountry of Birth\b[:;\s]*([A-Za-z .'-]+)/i]),
      matchI485Text(passportText, [/\bNationality\b\s*([A-Za-z .'-]+)/i]),
      ...principalDocuments
        .filter(
          (document) =>
            document.document_type_code === "birth_certificate" ||
            document.document_type_code === "passport" ||
            document.document_type_code === "visa",
        )
        .map((document) =>
          matchI485Text(decodeI485DocumentTextForParsing(document.extracted_text), [
            /\bPlace of Birth\s*[:\-]?\s*[^,\n]+,\s*([A-Za-z .'-]+)/i,
            /\bCountry\/Region of Origin \(Nationality\)\s*[:\-]?\s*([A-Za-z .'-]+)/i,
            /\bCountry:\s*([A-Za-z .'-]+)\b/i,
          ]),
        ),
      input.sourceProfile.birthCountry,
    );
  const resolvedCountryOfCitizenship =
    pickFirstUsableI485Value(
      matchI485Text(ds160Text, [/\bCountry\/Region of Origin \(Nationality\)\s*[:\-]?\s*([A-Za-z .'-]+)/i]),
      matchI485Text(permQuestionnaireText, [/\bCountry of Citizenship:\s*([A-Za-z .'-]+)/i]),
      matchI485Text(passportText, [/\bNationality\b\s*([A-Za-z .'-]+)/i]),
      input.sourceProfile.countryOfCitizenship,
    );
  const resolvedPassportNumber =
    pickFirstUsableI485Value(
      matchI485Text(ds160Text, [/\bPassport\/Travel Document Number\s*[:\-]?\s*([A-Z0-9-]+)/i]),
      matchI485Text(i94Text, [
        /\bDocument Number\s*[:\-]?\s*([A-Z0-9-]+)/i,
        /\bPassport Number\s*[:\-]?\s*([A-Z0-9-]+)/i,
      ]),
      matchI485Text(passportText, [/\bNo\. of passport\b.*?\b([A-Z0-9]{6,12})\b/i]),
      input.sourceProfile.passportNumber,
    );
  const resolvedPassportIssuingCountry =
    pickFirstUsableI485Value(
      matchI485Text(ds160Text, [/\bCountry\/Authority that Issued Passport\/Travel Document\s*[:\-]?\s*([A-Za-z .'-]+)/i]),
      /government of the people's republic of bangladesh/i.test(passportText) ? "Bangladesh" : null,
      input.sourceProfile.passportIssuingCountry,
    );
  const resolvedPassportIssuingCity =
    pickFirstUsableI485Value(
      matchI485Text(ds160Text, [/\bCity Where Issued\s*[:\-]?\s*([A-Za-z .'-]+)/i]),
      input.sourceProfile.passportIssuingCity,
    );
  const resolvedLastArrivalDate =
    pickFirstUsableI485Value(
      formatI485UsDate(matchI485Text(i94Text, [/\bMost Recent Date of Entry\s*[:\-]?\s*([A-Za-z0-9, ]+)/i])),
      input.sourceProfile.lastArrivalDate,
    );
  const resolvedI94Number =
    pickFirstUsableI485Value(
      matchI485Text(i94Text, [
        /\bArrival-Departure Record Number\s*[:\-]?\s*([A-Z0-9]{8,})/i,
        /\bRecord Number\s*[:\-]?\s*([A-Z0-9]{8,})/i,
        /\bI-?94\s*(?:Number)?\s*[:\-]?\s*([A-Z0-9]{8,})/i,
      ]),
      ...principalDocuments
        .filter(
          (document) =>
            document.document_type_code === "i94" ||
            normalizeKnowledgeSearchText(document.title).includes("i94"),
        )
        .map((document) =>
          matchI485Text(decodeI485DocumentTextForParsing(document.extracted_text), [
            /\bAdmission \(I-94\) Record Number\s*:\s*([A-Z0-9]{8,})/i,
            /\bArrival-Departure Record Number\s*[:\-]?\s*([A-Z0-9]{8,})/i,
            /\bMost Recent I-94[\s\S]*?Record Number\s*:\s*([A-Z0-9]{8,})/i,
          ]),
        ),
      input.sourceProfile.i94Number,
    );
  const resolvedClassOfAdmission =
    pickFirstUsableI485Value(
      matchI485Text(i94Text, [/\bClass of Admission\s*[:\-]?\s*([A-Z0-9-]+)/i]),
      input.sourceProfile.classOfAdmission,
    );
  const resolvedCurrentImmigrationStatus =
    pickFirstUsableI485Value(
      matchI485Text(permQuestionnaireText, [/\bType of Visa:\s*([A-Z0-9\/-]+)/i]),
      input.sourceProfile.currentImmigrationStatus,
    );

  const defaultSourceTitles = Array.from(
    new Set(
      principalDocuments
        .filter(
          (document) =>
            (document.document_type_code === "i94" ||
              document.document_type_code === "visa" ||
              document.document_type_code === "passport" ||
              document.document_type_code === "birth_certificate" ||
              isSupplementJDocument(document) ||
              isI140Document(document) ||
              isI20Document(document) ||
              isNvcImmigrantVisaCaseDocument(document) ||
              isI485QuestionnaireDocument(document)),
        )
        .map((document) => document.title),
    ),
  ).slice(0, 8);

  for (const field of input.fields) {
    if (field.value?.trim() || input.values[field.pdfFieldName] || isI485AdministrativeField(field)) {
      continue;
    }

    const pdfFieldName = field.pdfFieldName;
    const assign = (value: string | null, rationale: string, sourceTitles = defaultSourceTitles) =>
      assignI485DeterministicFieldValue(
        input.values,
        input.fieldAuditEntries,
        field,
        value,
        rationale,
        sourceTitles,
      );

    if (/AlienNumber\[0\]/.test(pdfFieldName) || /Pt1Line4_AlienNumber/.test(pdfFieldName)) {
      assign(input.sourceProfile.alienNumber, "Alien number was extracted only when directly supported by non-I-485 source evidence.");
    }
    else if (/Pt1Line4_YN\[0\]/.test(pdfFieldName) && input.sourceProfile.alienNumber) assign("true", "A-number is directly supported by source evidence.");
    else if (/Pt1Line4_YN\[1\]/.test(pdfFieldName) && input.sourceProfile.alienNumber === null) assign(null, ""); // keep blank when unsupported
    else if (/Pt1Line2_FamilyName/.test(pdfFieldName) || /Pt1Line2a_FamilyName/.test(pdfFieldName)) assign(input.sourceProfile.principalName?.familyName ?? null, "Filled from authoritative identity documents.");
    else if (/Pt1Line2_GivenName/.test(pdfFieldName) || /Pt1Line2a_GivenName/.test(pdfFieldName)) assign(input.sourceProfile.principalName?.givenName ?? null, "Filled from authoritative identity documents.");
    else if (/Pt1Line2_MiddleName/.test(pdfFieldName) || /Pt1Line2a_MiddleName/.test(pdfFieldName)) assign(input.sourceProfile.principalName?.middleName ?? null, "Filled from authoritative identity documents.");
    else if (/Pt1Line2_YN\[0\]/.test(pdfFieldName) && input.sourceProfile.hasOtherNamesUsed === true) assign("true", "Source evidence shows the applicant has used other names.");
    else if (/Pt1Line2_YN\[1\]/.test(pdfFieldName) && input.sourceProfile.hasOtherNamesUsed === false) assign("true", "DS-160 explicitly states the applicant has not used other names.");
    else if (/Pt1Line1_FamilyName/.test(pdfFieldName)) assign(input.sourceProfile.principalName?.familyName ?? null, "Filled from authoritative identity documents.");
    else if (/Pt1Line1_GivenName/.test(pdfFieldName)) assign(input.sourceProfile.principalName?.givenName ?? null, "Filled from authoritative identity documents.");
    else if (/Pt1Line1_MiddleName/.test(pdfFieldName)) assign(input.sourceProfile.principalName?.middleName ?? null, "Filled from authoritative identity documents.");
    else if (/Pt1Line3_DOB/.test(pdfFieldName)) assign(input.sourceProfile.dateOfBirth, "Filled from DS-160, I-94, and supporting immigration records.");
    else if (/Pt1Line3_YN\[0\]/.test(pdfFieldName) && input.sourceProfile.hasOtherDatesOfBirth === true) assign("true", "Source evidence shows the applicant used another date of birth.");
    else if (/Pt1Line3_YN\[1\]/.test(pdfFieldName) && input.sourceProfile.hasOtherDatesOfBirth === false) assign("true", "Source evidence does not show any other date of birth.");
    else if (/Pt1Line6_CB_Sex\[0\]/.test(pdfFieldName) && input.sourceProfile.sex === "Female") assign("true", "Applicant sex is explicitly identified as female in source documents.");
    else if (/Pt1Line6_CB_Sex\[1\]/.test(pdfFieldName) && input.sourceProfile.sex === "Male") assign("true", "Applicant sex is explicitly identified as male in source documents.");
    else if (/Pt1Line7_CityTownOfBirth/.test(pdfFieldName)) assign(resolvedBirthCity, "Place of birth city was extracted from direct identity evidence.");
    else if (/Pt1Line7_CountryOfBirth/.test(pdfFieldName)) assign(resolvedBirthCountry, "Country of birth was extracted from direct identity evidence.");
    else if (/Pt1Line8_CountryofCitizenshipNationality/.test(pdfFieldName)) assign(resolvedCountryOfCitizenship, "Country of citizenship was extracted from DS-160, passport, and questionnaire evidence.");
    else if (/Pt1Line10_PassportNum/.test(pdfFieldName)) assign(resolvedPassportNumber, "Passport number used at last arrival was extracted from I-94, DS-160, or passport evidence.");
    else if (/Pt1Line10_ExpDate/.test(pdfFieldName)) assign(resolvedPassportExpiryDate, "Passport expiration date was extracted from DS-160 or passport evidence.");
    else if (/Pt1Line10_Passport\[0\]/.test(pdfFieldName)) assign(resolvedPassportIssuingCountry, "Passport issuing country was extracted from DS-160 or passport evidence.");
    else if (/Pt1Line10_CityTown/.test(pdfFieldName)) assign(resolvedPassportIssuingCity, "Passport issuing city was extracted from DS-160 evidence.");
    else if (/Pt1Line10_DateofArrival/.test(pdfFieldName)) assign(resolvedLastArrivalDate, "Last arrival date was extracted from I-94 evidence.");
    else if (/Pt1Line10_VisaNum/.test(pdfFieldName)) assign(input.sourceProfile.visaNumber, "Visa number was extracted only when explicitly present in source evidence.");
    else if (/Pt1Line10_NonImmDate/.test(pdfFieldName)) assign(resolvedVisaIssuanceDate, "Visa issuance date was extracted from the DS-160.");
    else if (/Pt1Line11_Admitted\[0\]/.test(pdfFieldName) && input.sourceProfile.wasInspectedAndAdmitted === true) assign("true", "I-94 evidence confirms the applicant was inspected and admitted at last arrival.");
    else if (/Pt1Line12_Status/.test(pdfFieldName)) assign(resolvedClassOfAdmission, "Class of admission/status on Form I-94 was extracted from I-94 evidence.");
    else if (/P1Line12_I94/.test(pdfFieldName)) assign(resolvedI94Number, "I-94 number was extracted from the I-94 evidence.");
    else if (/Pt1Line14_Status/.test(pdfFieldName)) assign(resolvedCurrentImmigrationStatus, "Current immigration status was extracted from current student-status or visa evidence.");
    else if (/Pt1Line15_Date/.test(pdfFieldName) || /Pt1Line10_NonImmDate/.test(pdfFieldName)) assign(input.sourceProfile.authorizedStayUntil, "Authorized stay expiration was extracted from I-94 evidence.");
    else if (/Pt1Line18_(?:StreetNumberName|CurrentStreetNumberName)/.test(pdfFieldName)) assign(resolvedCurrentUsAddress?.street ?? null, "Current U.S. physical address was extracted from the AOS questionnaire.");
    else if (/(?:Pt1Line18US_Unit|Pt1Line18_CurrentUnit)\[0\]/.test(pdfFieldName) && resolvedCurrentUsAddress?.unitType === "Suite") assign("true", "Address unit type was extracted from the current U.S. address.");
    else if (/(?:Pt1Line18US_Unit|Pt1Line18_CurrentUnit)\[1\]/.test(pdfFieldName) && resolvedCurrentUsAddress?.unitType === "Floor") assign("true", "Address unit type was extracted from the current U.S. address.");
    else if (/(?:Pt1Line18US_Unit|Pt1Line18_CurrentUnit)\[2\]/.test(pdfFieldName) && resolvedCurrentUsAddress?.unitType === "Apartment") assign("true", "Address unit type was extracted from the current U.S. address.");
    else if (/(?:Pt1Line18US_AptSteFlrNumber|Pt1Line18_CurrentAptSteFlrNumber)/.test(pdfFieldName)) assign(resolvedCurrentUsAddress?.unitNumber ?? null, "Address unit number was extracted from the current U.S. address.");
    else if (/Pt1Line18_(?:CityOrTown|CurrentCityOrTown)/.test(pdfFieldName)) assign(resolvedCurrentUsAddress?.city ?? null, "Current U.S. address city was extracted from the AOS questionnaire.");
    else if (/Pt1Line18_(?:State|CurrentState)/.test(pdfFieldName)) assign(resolvedCurrentUsAddress?.state ?? null, "Current U.S. address state was extracted from the AOS questionnaire.");
    else if (/Pt1Line18_(?:ZipCode|CurrentZipCode)/.test(pdfFieldName)) assign(resolvedCurrentUsAddress?.zipCode ?? null, "Current U.S. address ZIP code was extracted from the AOS questionnaire.");
    else if (/Pt1Line18_Date/.test(pdfFieldName)) assign(resolvedCurrentAddressFromDate, "The date first resided at the current address was extracted from the AOS questionnaire.");
    else if (/Pt1Line18_last5yrs_YN\[0\]/.test(pdfFieldName) && resolvedHasResidedAtCurrentAddressForFiveYears === true) assign("true", "Current address residence duration exceeds five years based on the documented move-in date.");
    else if (/Pt1Line18_last5yrs_YN\[1\]/.test(pdfFieldName) && resolvedHasResidedAtCurrentAddressForFiveYears === false) assign("true", "Current address residence duration is less than five years based on the documented move-in date.");
    else if (/Pt1Line18_PriorStreetName/.test(pdfFieldName)) assign(resolvedPriorUsAddress?.street ?? null, "The most recent prior U.S. address was extracted from the AOS questionnaire.");
    else if (/Pt1Line18_PriorAddress_Unit\[0\]/.test(pdfFieldName) && resolvedPriorUsAddress?.unitType === "Apartment") assign("true", "Prior address unit type was extracted from the AOS questionnaire.");
    else if (/Pt1Line18_PriorAddress_Unit\[1\]/.test(pdfFieldName) && resolvedPriorUsAddress?.unitType === "Suite") assign("true", "Prior address unit type was extracted from the AOS questionnaire.");
    else if (/Pt1Line18_PriorAddress_Unit\[2\]/.test(pdfFieldName) && resolvedPriorUsAddress?.unitType === "Floor") assign("true", "Prior address unit type was extracted from the AOS questionnaire.");
    else if (/Pt1Line18_PriorAddress_Number/.test(pdfFieldName)) assign(resolvedPriorUsAddress?.unitNumber ?? null, "Prior address unit number was extracted from the AOS questionnaire.");
    else if (/Pt1Line18_PriorCity/.test(pdfFieldName)) assign(resolvedPriorUsAddress?.city ?? null, "The most recent prior U.S. address city was extracted from the AOS questionnaire.");
    else if (/Pt1Line18_PriorState/.test(pdfFieldName)) assign(resolvedPriorUsAddress?.state ?? null, "The most recent prior U.S. address state was extracted from the AOS questionnaire.");
    else if (/Pt1Line18_PriorZipCode/.test(pdfFieldName)) assign(resolvedPriorUsAddress?.zipCode ?? null, "The most recent prior U.S. address ZIP code was extracted from the AOS questionnaire.");
    else if (/Pt1Line18_PriorProvince/.test(pdfFieldName)) assign(resolvedPriorUsAddress?.province ?? null, "Prior address province was extracted from the AOS questionnaire.");
    else if (/Pt1Line18_PriorPostalCode/.test(pdfFieldName)) assign(resolvedPriorUsAddress?.postalCode ?? null, "Prior address postal code was extracted from the AOS questionnaire.");
    else if (/Pt1Line18_PriorCountry/.test(pdfFieldName)) assign(resolvedPriorUsAddress?.country ?? null, "Prior address country was extracted from the AOS questionnaire.");
    else if (/Pt1Line18_PriorDateFrom/.test(pdfFieldName)) assign(resolvedPriorUsAddress?.dateFrom ?? null, "Prior address residence dates were extracted from the AOS questionnaire.");
    else if (/Pt1Line18PriorDateTo/.test(pdfFieldName)) assign(resolvedPriorUsAddress?.dateTo ?? null, "Prior address residence dates were extracted from the AOS questionnaire.");
    else if (/Pt1Line18_RecentStreetName/.test(pdfFieldName)) assign(resolvedMostRecentForeignAddress?.street ?? null, "Most recent foreign address was extracted from the AOS questionnaire.");
    else if (/Pt1Line18_RecentUnit\[0\]/.test(pdfFieldName) && resolvedMostRecentForeignAddress?.unitType === "Apartment") assign("true", "Most recent foreign address unit type was extracted from the AOS questionnaire.");
    else if (/Pt1Line18_RecentUnit\[1\]/.test(pdfFieldName) && resolvedMostRecentForeignAddress?.unitType === "Suite") assign("true", "Most recent foreign address unit type was extracted from the AOS questionnaire.");
    else if (/Pt1Line18_RecentUnit\[2\]/.test(pdfFieldName) && resolvedMostRecentForeignAddress?.unitType === "Floor") assign("true", "Most recent foreign address unit type was extracted from the AOS questionnaire.");
    else if (/Pt1Line18_RecentNumber/.test(pdfFieldName)) assign(resolvedMostRecentForeignAddress?.unitNumber ?? null, "Most recent foreign address unit number was extracted from the AOS questionnaire.");
    else if (/Pt1Line18_RecentCity/.test(pdfFieldName)) assign(resolvedMostRecentForeignAddress?.city ?? null, "Most recent foreign address city was extracted from the AOS questionnaire.");
    else if (/Pt1Line18_RecentState/.test(pdfFieldName)) assign(resolvedMostRecentForeignAddress?.state ?? resolvedMostRecentForeignAddress?.province ?? null, "Most recent foreign address province/state was extracted from the AOS questionnaire.");
    else if (/Pt1Line18_RecentZipCode/.test(pdfFieldName)) assign(resolvedMostRecentForeignAddress?.zipCode ?? null, "Most recent foreign address ZIP code was extracted from the AOS questionnaire.");
    else if (/Pt1Line18_RecentProvince/.test(pdfFieldName)) assign(resolvedMostRecentForeignAddress?.province ?? null, "Most recent foreign address province was extracted from the AOS questionnaire.");
    else if (/Pt1Line18_RecentPostalCode/.test(pdfFieldName)) assign(resolvedMostRecentForeignAddress?.postalCode ?? null, "Most recent foreign address postal code was extracted from the AOS questionnaire.");
    else if (/Pt1Line18_RecentCountry/.test(pdfFieldName)) assign(resolvedMostRecentForeignAddress?.country ?? null, "Most recent foreign address country was extracted from the AOS questionnaire.");
    else if (/Pt1Line18_RecentDateFrom/.test(pdfFieldName)) assign(resolvedMostRecentForeignAddress?.dateFrom ?? null, "Most recent foreign address dates were extracted from the AOS questionnaire.");
    else if (/Pt1Line18_RecentDateTo/.test(pdfFieldName)) assign(resolvedMostRecentForeignAddress?.dateTo ?? null, "Most recent foreign address dates were extracted from the AOS questionnaire.");
    else if (/Pt1Line19_YN\[0\]/.test(pdfFieldName) && input.sourceProfile.hasSsn === false) assign("true", "Source evidence indicates the applicant does not have a Social Security number.");
    else if (/Pt1Line19_YN\[1\]/.test(pdfFieldName) && input.sourceProfile.hasSsn === true) assign("true", "Source evidence indicates the applicant has an issued Social Security number.");
    else if (/Pt1Line19_SSN/.test(pdfFieldName)) assign(input.sourceProfile.ssn, "Social Security number was extracted from source evidence.");
    else if (/Pt2Line3b_CB140\[5\]/.test(pdfFieldName) && input.sourceProfile.employmentBasedCategory === "skilled_worker") assign("true", "I-140 evidence explicitly classifies the applicant as a skilled worker.");
    else if (/Pt2Line3b_CB140\[4\]/.test(pdfFieldName) && input.sourceProfile.employmentBasedCategory === "professional") assign("true", "I-140 evidence explicitly classifies the applicant as a professional.");
    else if (/Pt2Line3b_CB140\[6\]/.test(pdfFieldName) && input.sourceProfile.employmentBasedCategory === "other_worker") assign("true", "I-140 evidence explicitly classifies the applicant as another worker.");
    else if (/Pt2Line3b_CB140\[3\]/.test(pdfFieldName) && input.sourceProfile.employmentBasedCategory === "advanced_degree") assign("true", "I-140 evidence explicitly classifies the applicant in the advanced degree / exceptional ability category.");
    else if (/Pt3Line1_CB\[4\]/.test(pdfFieldName) && input.sourceProfile.employmentBasedCategory) assign("true", "Employment-based adjustment generally does not require an Affidavit of Support under INA section 213A.");
    else if (/Pt4Line7_EmployerName\[0\]/.test(pdfFieldName) || /Pt4Line7_EmployerName\[2\]/.test(pdfFieldName)) assign(resolvedCurrentEmploymentOrSchool?.name ?? null, "Current school or employment information was extracted from the AOS questionnaire.");
    else if (/Pt4Line7_EmployerName\[1\]/.test(pdfFieldName)) assign(resolvedCurrentEmploymentOrSchool?.occupation ?? null, "Current occupation or school status was extracted from the AOS questionnaire.");
    else if (/Part4Line7_StreetName|P4Line7_/.test(pdfFieldName)) {
      const address = resolvedCurrentEmploymentOrSchool?.address;
      if (/StreetName/.test(pdfFieldName)) assign(address?.street ?? null, "Current school/employer address was extracted from the AOS questionnaire.");
      else if (/Unit\[0\]/.test(pdfFieldName) && address?.unitType === "Apartment") assign("true", "Current school/employer address unit type was extracted from the AOS questionnaire.");
      else if (/Unit\[1\]/.test(pdfFieldName) && address?.unitType === "Suite") assign("true", "Current school/employer address unit type was extracted from the AOS questionnaire.");
      else if (/Unit\[2\]/.test(pdfFieldName) && address?.unitType === "Floor") assign("true", "Current school/employer address unit type was extracted from the AOS questionnaire.");
      else if (/Number/.test(pdfFieldName)) assign(address?.unitNumber ?? null, "Current school/employer address unit number was extracted from the AOS questionnaire.");
      else if (/City/.test(pdfFieldName)) assign(address?.city ?? null, "Current school/employer address city was extracted from the AOS questionnaire.");
      else if (/State/.test(pdfFieldName)) assign(address?.state ?? null, "Current school/employer address state was extracted from the AOS questionnaire.");
      else if (/ZipCode/.test(pdfFieldName)) assign(address?.zipCode ?? null, "Current school/employer address ZIP code was extracted from the AOS questionnaire.");
      else if (/PostalCode/.test(pdfFieldName)) assign(address?.postalCode ?? null, "Current school/employer address postal code was extracted from the AOS questionnaire.");
      else if (/Province/.test(pdfFieldName)) assign(address?.province ?? null, "Current school/employer address province was extracted from the AOS questionnaire.");
      else if (/Country/.test(pdfFieldName)) assign(address?.country ?? null, "Current school/employer address country was extracted from the AOS questionnaire.");
    }
    else if (/Pt4Line7_DateFrom/.test(pdfFieldName)) assign(resolvedCurrentEmploymentOrSchool?.dateFrom ?? null, "Current school/employer dates were extracted from the AOS questionnaire.");
    else if (/Pt4Line7_DateTo/.test(pdfFieldName)) assign(resolvedCurrentEmploymentOrSchool?.dateTo ?? null, "Current school/employer dates were extracted from the AOS questionnaire.");
    else if (/Pt4Line1_YN\[1\]/.test(pdfFieldName) && input.sourceProfile.hasAppliedImmigrantVisaAbroad === true) assign("true", "National Visa Center / immigrant-visa case records show the applicant pursued immigrant visa processing abroad.");
    else if (/Pt4Line8_EmployerName/.test(pdfFieldName)) assign(resolvedMostRecentOutsideUsEmploymentOrSchool?.name ?? null, "Most recent employer or school outside the U.S. was extracted from the AOS questionnaire.");
    else if (/Pt4Line8_Occupation/.test(pdfFieldName)) assign(resolvedMostRecentOutsideUsEmploymentOrSchool?.occupation ?? null, "Most recent occupation outside the U.S. was extracted from the AOS questionnaire.");
    else if (/P4Line8_/.test(pdfFieldName)) {
      const address = resolvedMostRecentOutsideUsEmploymentOrSchool?.address;
      if (/StreetName/.test(pdfFieldName)) assign(address?.street ?? null, "Most recent outside-U.S. employer/school address was extracted from the AOS questionnaire.");
      else if (/Unit\[0\]/.test(pdfFieldName) && address?.unitType === "Apartment") assign("true", "Most recent outside-U.S. employer/school address unit type was extracted from the AOS questionnaire.");
      else if (/Unit\[1\]/.test(pdfFieldName) && address?.unitType === "Suite") assign("true", "Most recent outside-U.S. employer/school address unit type was extracted from the AOS questionnaire.");
      else if (/Unit\[2\]/.test(pdfFieldName) && address?.unitType === "Floor") assign("true", "Most recent outside-U.S. employer/school address unit type was extracted from the AOS questionnaire.");
      else if (/Number/.test(pdfFieldName)) assign(address?.unitNumber ?? null, "Most recent outside-U.S. employer/school address unit number was extracted from the AOS questionnaire.");
      else if (/City/.test(pdfFieldName)) assign(address?.city ?? null, "Most recent outside-U.S. employer/school address city was extracted from the AOS questionnaire.");
      else if (/State/.test(pdfFieldName)) assign(address?.state ?? address?.province ?? null, "Most recent outside-U.S. employer/school address state/province was extracted from the AOS questionnaire.");
      else if (/ZipCode/.test(pdfFieldName)) assign(address?.zipCode ?? null, "Most recent outside-U.S. employer/school address ZIP code was extracted from the AOS questionnaire.");
      else if (/PostalCode/.test(pdfFieldName)) assign(address?.postalCode ?? null, "Most recent outside-U.S. employer/school address postal code was extracted from the AOS questionnaire.");
      else if (/Province/.test(pdfFieldName)) assign(address?.province ?? null, "Most recent outside-U.S. employer/school address province was extracted from the AOS questionnaire.");
      else if (/Country/.test(pdfFieldName)) assign(address?.country ?? null, "Most recent outside-U.S. employer/school address country was extracted from the AOS questionnaire.");
    }
    else if (/Pt5Line1_FamilyName/.test(pdfFieldName)) assign(resolvedParent1?.name?.familyName ?? null, "Parent 1 information was extracted from the AOS questionnaire.");
    else if (/Pt5Line1_GivenName/.test(pdfFieldName)) assign(resolvedParent1?.name?.givenName ?? null, "Parent 1 information was extracted from the AOS questionnaire.");
    else if (/Pt5Line1_MiddleName/.test(pdfFieldName)) assign(resolvedParent1?.name?.middleName ?? null, "Parent 1 information was extracted from the AOS questionnaire.");
    else if (/Pt5Line3_DateofBirth/.test(pdfFieldName)) assign(resolvedParent1?.dateOfBirth ?? null, "Parent 1 date of birth was extracted from the AOS questionnaire.");
    else if (/Pt5Line5_CityTownOfBirth/.test(pdfFieldName)) assign(resolvedParent1?.countryOfBirth ?? null, "Parent 1 country of birth was extracted from the AOS questionnaire.");
    else if (/Pt5Line6_FamilyName/.test(pdfFieldName)) assign(resolvedParent2?.name?.familyName ?? null, "Parent 2 information was extracted from the AOS questionnaire.");
    else if (/Pt5Line6_GivenName/.test(pdfFieldName)) assign(resolvedParent2?.name?.givenName ?? null, "Parent 2 information was extracted from the AOS questionnaire.");
    else if (/Pt5Line6_MiddleName/.test(pdfFieldName)) assign(resolvedParent2?.name?.middleName ?? null, "Parent 2 information was extracted from the AOS questionnaire.");
    else if (/Pt5Line8_DateofBirth\[0\]/.test(pdfFieldName)) assign(resolvedParent2?.dateOfBirth ?? null, "Parent 2 date of birth was extracted from the AOS questionnaire.");
    else if (/Pt5Line10_CityTownOfBirth/.test(pdfFieldName)) assign(resolvedParent2?.countryOfBirth ?? null, "Parent 2 country of birth was extracted from the AOS questionnaire.");
    else if (/Pt6Line1_MaritalStatus\[3\]/.test(pdfFieldName) && input.sourceProfile.maritalStatus === "Married") assign("true", "Current marital status is explicitly stated as married in source evidence.");
    else if (/Pt6Line1_MaritalStatus\[0\]/.test(pdfFieldName) && input.sourceProfile.maritalStatus === "Divorced") assign("true", "Current marital status is explicitly stated as divorced in source evidence.");
    else if (/Pt6Line1_MaritalStatus\[1\]/.test(pdfFieldName) && input.sourceProfile.maritalStatus === "Single, Never Married") assign("true", "Current marital status is explicitly stated as single in source evidence.");
    else if (/Pt6Line1_MaritalStatus\[2\]/.test(pdfFieldName) && input.sourceProfile.maritalStatus === "Widowed") assign("true", "Current marital status is explicitly stated as widowed in source evidence.");
    else if (/Pt6Line4_FamilyName/.test(pdfFieldName)) assign(resolvedCurrentSpouse?.name?.familyName ?? null, "Current spouse information was extracted from the AOS questionnaire.");
    else if (/Pt6Line4_GivenName/.test(pdfFieldName)) assign(resolvedCurrentSpouse?.name?.givenName ?? null, "Current spouse information was extracted from the AOS questionnaire.");
    else if (/Pt6Line4_MiddleName/.test(pdfFieldName)) assign(resolvedCurrentSpouse?.name?.middleName ?? null, "Current spouse information was extracted from the AOS questionnaire.");
    else if (/Pt5Line8_DateofBirth\[1\]/.test(pdfFieldName)) assign(resolvedCurrentSpouse?.dateOfBirth ?? null, "Current spouse date of birth was extracted from the AOS questionnaire.");
    else if (/Pt6Line7_Country/.test(pdfFieldName)) assign(resolvedCurrentSpouse?.countryOfBirth ?? null, "Current spouse country of birth was extracted from the AOS questionnaire.");
    else if (/Part6Line8_StreetName/.test(pdfFieldName)) assign(resolvedCurrentSpouse?.address?.street ?? null, "Current spouse address was extracted from the AOS questionnaire.");
    else if (/P6Line8_City/.test(pdfFieldName)) assign(resolvedCurrentSpouse?.address?.city ?? null, "Current spouse address city was extracted from the AOS questionnaire.");
    else if (/P6Line8_State/.test(pdfFieldName)) assign(resolvedCurrentSpouse?.address?.state ?? resolvedCurrentSpouse?.address?.province ?? null, "Current spouse address province/state was extracted from the AOS questionnaire.");
    else if (/P6Line8_PostalCode/.test(pdfFieldName)) assign(resolvedCurrentSpouse?.address?.postalCode ?? null, "Current spouse address postal code was extracted from the AOS questionnaire.");
    else if (/P6Line8_Province/.test(pdfFieldName)) assign(resolvedCurrentSpouse?.address?.province ?? null, "Current spouse address province was extracted from the AOS questionnaire.");
    else if (/P6Line8_Country/.test(pdfFieldName)) assign(resolvedCurrentSpouse?.address?.country ?? null, "Current spouse address country was extracted from the AOS questionnaire.");
    else if (/Pt6Line10_CityTownOfBirth\[0\]/.test(pdfFieldName)) assign(resolvedMarriageCity, "Place of marriage was extracted from the AOS questionnaire.");
    else if (/Pt6Line10_State\[0\]/.test(pdfFieldName)) assign(resolvedMarriageStateOrProvince, "Place of marriage was extracted from the AOS questionnaire.");
    else if (/Pt6Line10_Country\[0\]/.test(pdfFieldName)) assign(resolvedMarriageCountry, "Place of marriage was extracted from the AOS questionnaire.");
    else if (/Pt5Line8_DateofBirth\[2\]/.test(pdfFieldName)) assign(resolvedMarriageDate, "Date of marriage was extracted from the AOS questionnaire.");
    else if (/Pt7Line1_TotalChildren/.test(pdfFieldName)) assign(resolvedNumberOfChildren, "The total number of living children was extracted from the AOS questionnaire.");
    else if (/Pt7Line2_FamilyName/.test(pdfFieldName)) assign(resolvedChild1?.name?.familyName ?? null, "Child information was extracted from the AOS questionnaire.");
    else if (/Pt7Line2_GivenName/.test(pdfFieldName)) assign(resolvedChild1?.name?.givenName ?? null, "Child information was extracted from the AOS questionnaire.");
    else if (/Pt7Line2_MiddleName/.test(pdfFieldName)) assign(resolvedChild1?.name?.middleName ?? null, "Child information was extracted from the AOS questionnaire.");
    else if (/Pt7Line2_DateofBirth/.test(pdfFieldName)) assign(resolvedChild1?.dateOfBirth ?? null, "Child date of birth was extracted from the AOS questionnaire.");
    else if (/Pt7Line2_Country/.test(pdfFieldName)) assign(resolvedChild1?.countryOfBirth ?? null, "Child country of birth was extracted from the AOS questionnaire.");
    else if (/Pt7Line2_YN\[1\]/.test(pdfFieldName) && resolvedChild1ApplyingWithApplicant === true) assign("true", "Supporting questionnaire states this child is also applying with the applicant.");
    else if (/Pt7Line2_YN\[0\]/.test(pdfFieldName) && resolvedChild1ApplyingWithApplicant === false) assign("true", "Supporting questionnaire states this child is not applying with the applicant.");
    else if (/Pt7Line2_Relationship/.test(pdfFieldName)) assign(resolvedChild1Relationship, "Child relationship was extracted from the AOS questionnaire.");
    else if (/Pt7Line1_Ethnicity\[1\]/.test(pdfFieldName) && input.sourceProfile.ethnicityHispanic === false) assign("true", "The applicant's documented race/ethnicity evidence supports a non-Hispanic classification.");
    else if (/Pt7Line1_Ethnicity\[0\]/.test(pdfFieldName) && input.sourceProfile.ethnicityHispanic === true) assign("true", "The applicant's documented race/ethnicity evidence supports a Hispanic classification.");
    else if (/Pt7Line2_Race\[0\]/.test(pdfFieldName) && input.sourceProfile.raceAsian === true) assign("true", "Race is explicitly documented as Asian in the AOS questionnaire.");
    else if (/Pt7Line3_HeightFeet/.test(pdfFieldName)) assign(resolvedHeightFeet, "Height was extracted from the AOS questionnaire.");
    else if (/Pt7Line3_HeightInches/.test(pdfFieldName)) assign(resolvedHeightInches, "Height was extracted from the AOS questionnaire.");
    else if (/Pt7Line4_Weight1/.test(pdfFieldName)) assign(resolvedWeightPounds?.padStart(3, "0")[0] ?? null, "Weight was extracted from the AOS questionnaire.");
    else if (/Pt7Line4_Weight2/.test(pdfFieldName)) assign(resolvedWeightPounds?.padStart(3, "0")[1] ?? null, "Weight was extracted from the AOS questionnaire.");
    else if (/Pt7Line4_Weight3/.test(pdfFieldName)) assign(resolvedWeightPounds?.padStart(3, "0")[2] ?? null, "Weight was extracted from the AOS questionnaire.");
    else if (/Pt7Line5_Eyecolor/.test(pdfFieldName) && resolvedEyeColor) {
      const labelMatch = field.label.match(/Select (.+)\./i);
      if (labelMatch && normalizeConsolidatedKnowledgeText(labelMatch[1]) === resolvedEyeColor) assign("true", "Eye color was extracted from the AOS questionnaire.");
    }
    else if (/Pt7Line6_Haircolor/.test(pdfFieldName) && resolvedHairColor) {
      const labelMatch = field.label.match(/Select (.+)\./i);
      if (labelMatch && normalizeConsolidatedKnowledgeText(labelMatch[1]) === resolvedHairColor) assign("true", "Hair color was extracted from the AOS questionnaire.");
    }
    else if (/Pt3Line3_DaytimePhoneNumber1/.test(pdfFieldName)) assign(resolvedCurrentPhone, "Applicant phone number was extracted from recent applicant documents.");
    else if (/Pt3Line4_MobileNumber1/.test(pdfFieldName)) assign(resolvedCurrentMobilePhone, "Applicant mobile phone number was extracted from recent applicant documents.");
    else if (/Pt3Line5_Email/.test(pdfFieldName)) assign(resolvedEmail, "Applicant email address was extracted from recent applicant documents.");
  }

  for (const field of input.fields) {
    if (isI485AdministrativeField(field)) {
      continue;
    }

    const pdfFieldName = field.pdfFieldName;
    const forceAssign = (value: string | null, rationale: string, sourceTitles = defaultSourceTitles) =>
      forceAssignI485DeterministicFieldValue(
        input.values,
        input.fieldAuditEntries,
        field,
        value,
        rationale,
        sourceTitles,
      );

    if (/Pt1Line7_CountryOfBirth/.test(pdfFieldName)) {
      forceAssign(resolvedBirthCountry, "Country of birth was extracted directly from identity source documents.");
    } else if (/P1Line12_I94/.test(pdfFieldName)) {
      forceAssign(resolvedI94Number, "I-94 record number was extracted directly from the applicant's I-94 document.");
    } else if (/Pt1Line18_(?:StreetNumberName|CurrentStreetNumberName)/.test(pdfFieldName)) {
      forceAssign(resolvedCurrentUsAddress?.street ?? null, "Current U.S. address was extracted directly from the AOS questionnaire.");
    } else if (/(?:Pt1Line18US_AptSteFlrNumber|Pt1Line18_CurrentAptSteFlrNumber)/.test(pdfFieldName)) {
      forceAssign(resolvedCurrentUsAddress?.unitNumber ?? null, "Current U.S. address unit number was extracted directly from the AOS questionnaire.");
    } else if (/Pt1Line18_(?:CityOrTown|CurrentCityOrTown)/.test(pdfFieldName)) {
      forceAssign(resolvedCurrentUsAddress?.city ?? null, "Current U.S. address city was extracted directly from the AOS questionnaire.");
    } else if (/Pt1Line18_(?:State|CurrentState)/.test(pdfFieldName)) {
      forceAssign(resolvedCurrentUsAddress?.state ?? null, "Current U.S. address state was extracted directly from the AOS questionnaire.");
    } else if (/Pt1Line18_(?:ZipCode|CurrentZipCode)/.test(pdfFieldName)) {
      forceAssign(resolvedCurrentUsAddress?.zipCode ?? null, "Current U.S. address ZIP code was extracted directly from the AOS questionnaire.");
    } else if (/P4Line8_StreetName/.test(pdfFieldName)) {
      forceAssign(
        resolvedMostRecentOutsideUsEmploymentOrSchool?.address?.street ?? null,
        "Most recent employer or school outside the U.S. address was extracted directly from the AOS questionnaire.",
      );
    } else if (/P4Line8_City/.test(pdfFieldName)) {
      forceAssign(
        resolvedMostRecentOutsideUsEmploymentOrSchool?.address?.city ?? null,
        "Most recent employer or school outside the U.S. city was extracted directly from the AOS questionnaire.",
      );
    } else if (/P4Line8_State/.test(pdfFieldName)) {
      forceAssign(
        resolvedMostRecentOutsideUsEmploymentOrSchool?.address?.state ??
          resolvedMostRecentOutsideUsEmploymentOrSchool?.address?.province ??
          null,
        "Most recent employer or school outside the U.S. province/state was extracted directly from the AOS questionnaire.",
      );
    } else if (/P4Line8_PostalCode/.test(pdfFieldName)) {
      forceAssign(
        resolvedMostRecentOutsideUsEmploymentOrSchool?.address?.postalCode ??
          resolvedMostRecentOutsideUsEmploymentOrSchool?.address?.zipCode ??
          null,
        "Most recent employer or school outside the U.S. postal code was extracted directly from the AOS questionnaire.",
      );
    } else if (/P4Line8_Country/.test(pdfFieldName)) {
      forceAssign(
        resolvedMostRecentOutsideUsEmploymentOrSchool?.address?.country ?? null,
        "Most recent employer or school outside the U.S. country was extracted directly from the AOS questionnaire.",
      );
    } else if (/Pt4Line8_EmployerName/.test(pdfFieldName)) {
      forceAssign(
        resolvedMostRecentOutsideUsEmploymentOrSchool?.name ?? null,
        "Most recent employer or school outside the U.S. was extracted directly from the AOS questionnaire.",
      );
    } else if (/Pt4Line8_Occupation/.test(pdfFieldName)) {
      forceAssign(
        resolvedMostRecentOutsideUsEmploymentOrSchool?.occupation ?? null,
        "Most recent employer or school outside the U.S. occupation was extracted directly from the AOS questionnaire.",
      );
    } else if (/Pt7Line3_HeightFeet/.test(pdfFieldName)) {
      forceAssign(resolvedHeightFeet, "Height was extracted directly from the AOS questionnaire.");
    } else if (/Pt7Line3_HeightInches/.test(pdfFieldName)) {
      forceAssign(resolvedHeightInches, "Height was extracted directly from the AOS questionnaire.");
    } else if (/Pt7Line4_Weight1/.test(pdfFieldName)) {
      forceAssign(resolvedWeightPounds?.padStart(3, "0")[0] ?? null, "Weight was extracted directly from the AOS questionnaire.");
    } else if (/Pt7Line4_Weight2/.test(pdfFieldName)) {
      forceAssign(resolvedWeightPounds?.padStart(3, "0")[1] ?? null, "Weight was extracted directly from the AOS questionnaire.");
    } else if (/Pt7Line4_Weight3/.test(pdfFieldName)) {
      forceAssign(resolvedWeightPounds?.padStart(3, "0")[2] ?? null, "Weight was extracted directly from the AOS questionnaire.");
    }
  }
}

async function seedI485FieldValuesFromAuthoritativeSources(input: {
  lawFirmId: string;
  formName: string;
  basePrompt: string;
  section: I485SectionDefinition;
  unresolvedFields: Array<{
    pdfFieldName: string;
    label: string;
    dataType: string;
    value: string | null;
    instructions?: string | null;
    sectionName?: string | null;
    pageNumber?: number | null;
  }>;
  knownFieldValues: Array<{
    pdfFieldName: string;
    label: string;
    value: string | null | undefined;
  }>;
  sourceDocuments: ClientFormAiDocumentEvidence[];
  dossier: I485CanonicalDossier | null;
}) {
  if (!input.unresolvedFields.length || !input.sourceDocuments.length) {
    return {
      values: {} as Record<string, string>,
      warnings: [] as string[],
      fieldAuditEntries: [] as FilledFormAuditEntry[],
      inputTokens: 0,
      outputTokens: 0,
    };
  }

  const completion = await runJsonChatCompletion({
    lawFirmId: input.lawFirmId,
    systemPrompt: [
      input.basePrompt.trim(),
      `You are extracting direct answers for USCIS Form I-485 from authoritative source documents for the principal applicant.`,
      `Focus only on the "${input.section.title}" section.`,
      'Return JSON only in the shape {"answers":[{"pdfFieldName":"string","value":"string","rationale":"string","evidence":"string","sourceTitles":["string"],"confidence":0.0}],"warnings":["string"]}',
      "Use only authoritative source documents such as passport, I-94, DS-160, employment questionnaire, I-485 Supplement J, birth or marriage records when they are relevant.",
      "Do not use any prior filled I-485 as evidence. Ignore any document that is itself the target I-485 form.",
      "Answer only when the source documents clearly support the exact field.",
    "Do not answer attorney, interpreter, preparer, signature, barcode, or USCIS-only fields.",
    "If the source evidence is unclear or conflicts with canonical dossier facts, leave the field unanswered and mention it in warnings.",
    "Known field values are consistency context only. They are not evidence and must never appear in sourceTitles or be treated as the reason to fill a field.",
    "Dates must use MM/DD/YYYY when the source supports a date.",
      "Checkbox answers must use true or false.",
      "Dropdown answers must use the exact option value only when clearly supported.",
    ].join("\n"),
    userPrompt: JSON.stringify({
      formName: input.formName,
      sectionCode: input.section.code,
      sectionTitle: input.section.title,
      unresolvedFields: input.unresolvedFields.map((field) => ({
        pdfFieldName: field.pdfFieldName,
        label: field.label,
        dataType: field.dataType,
        customInstructions: field.instructions ?? null,
        pageNumber: field.pageNumber ?? null,
      })),
      knownFieldValues: input.knownFieldValues
        .filter((field) => field.value?.trim())
        .slice(0, 220),
      canonicalDossier:
        input.dossier ?
          {
            overview: input.dossier.overview,
            unresolvedTopics: input.dossier.unresolvedTopics,
            conflictTopics: input.dossier.conflictTopics,
            sections: input.dossier.sections
              .filter(
                (section) =>
                  section.sectionCode === input.section.code ||
                  section.sectionCode === "identity_biographic" ||
                  section.sectionCode === "immigration_history",
              )
              .map((section) => ({
                sectionCode: section.sectionCode,
                title: section.title,
                summary: section.summary,
                facts: section.facts,
              })),
          }
        : null,
      authoritativeSourceDocuments: input.sourceDocuments.map((document) => ({
        title: document.title,
        documentTypeCode: document.document_type_code,
        scopeCode: document.scope_code,
        extractedText: buildI485SectionDocumentExcerpt({
          document,
          section: input.section,
          fieldLabels: input.unresolvedFields.map((field) => field.label),
          maxChars: 5200,
        }),
      })),
    }),
    maxCompletionTokens: 2200,
  });

  const values: Record<string, string> = {};
  const fieldAuditEntries: FilledFormAuditEntry[] = [];
  const rawAnswers = Array.isArray(completion.json.answers) ? completion.json.answers : [];

  for (const answer of rawAnswers) {
    if (!answer || typeof answer !== "object") {
      continue;
    }

    const pdfFieldName = String((answer as { pdfFieldName?: unknown }).pdfFieldName ?? "").trim();
    if (!pdfFieldName) {
      continue;
    }

    const normalizedValue = normalizeAiAnswerValue((answer as { value?: unknown }).value);
    if (!normalizedValue) {
      continue;
    }

    const matchingField = input.unresolvedFields.find((field) => field.pdfFieldName === pdfFieldName);
    values[pdfFieldName] = normalizedValue;
    fieldAuditEntries.push({
      sectionTitle: input.section.title,
      label: matchingField?.label ?? pdfFieldName,
      pdfFieldName,
      value: normalizedValue,
      rationale: normalizeFieldAuditRationale(
        (answer as { rationale?: unknown }).rationale,
        `Extracted from authoritative source documents for the ${input.section.title} section.`,
      ),
      sourceTitles: normalizeConsolidatedKnowledgeSourceTitles(
        (answer as { sourceTitles?: unknown }).sourceTitles,
      ),
      evidenceExcerpt:
        limitText(
          normalizeConsolidatedKnowledgeText((answer as { evidence?: unknown }).evidence),
          400,
        ) || null,
      confidence: normalizeFieldAuditConfidence(
        (answer as { confidence?: unknown }).confidence,
        0.92,
      ),
    });
  }

  return {
    values,
    warnings: normalizeI485StringList(completion.json.warnings, 16),
    fieldAuditEntries,
    inputTokens: completion.usage.inputTokens,
    outputTokens: completion.usage.outputTokens,
  };
}

function buildI485SectionDossierSystemPrompt(input: {
  basePrompt: string;
  sectionTitle: string;
  sectionDescription: string;
}) {
  return [
    input.basePrompt.trim(),
    `You are preparing the canonical dossier section "${input.sectionTitle}" for USCIS Form I-485.`,
    `Focus only on this section: ${input.sectionDescription}`,
    "This dossier is for the principal applicant only.",
    "Return JSON only in this shape:",
    '{"summary":"string","facts":[{"factKey":"string","label":"string","value":"string","confidence":0.0,"evidence":"string","sourceTitles":["string"]}],"missingInformation":["string"],"conflicts":["string"]}',
    "Only include facts directly supported by the evidence.",
    "When a field includes customInstructions, treat them as field-specific guidance about how to interpret the question and where to search for evidence.",
    "If the evidence is contradictory, do not force a fact; explain the issue in conflicts.",
    "Do not include attorney, interpreter, preparer, signature, barcode, or USCIS officer facts.",
  ].join("\n");
}

function buildI485SectionFillSystemPrompt(input: {
  basePrompt: string;
  sectionTitle: string;
  sectionDescription: string;
}) {
  return [
    input.basePrompt.trim(),
    `You are filling only the "${input.sectionTitle}" section of USCIS Form I-485.`,
    `Section focus: ${input.sectionDescription}`,
    "Use the canonical dossier section as the primary source of truth, then the supporting evidence.",
    'Return JSON only in the shape {"answers":[{"pdfFieldName":"string","value":"string","rationale":"string","evidence":"string","sourceTitles":["string"],"confidence":0.0}],"warnings":["string"]}',
    "Only answer the fields included in unresolvedFields.",
    "Only fill principal-applicant fields that are explicitly supported by the evidence.",
    "Never invent facts.",
    "Leave unsupported or conflicting fields unanswered.",
    "Dates must use MM/DD/YYYY when the evidence supports a date.",
    "Checkbox answers must use true or false.",
    "Dropdown answers must use the exact option value only when clearly supported.",
    "When a field includes customInstructions, follow them as field-specific guidance for where to search and how to interpret the answer, unless that would conflict with evidence or safety rules.",
    "Known field values are consistency context only. They are not evidence and must never appear in sourceTitles or be treated as the reason to fill a field.",
    "Do not fill attorney, interpreter, preparer, signature, barcode, or certification fields.",
    "Each answer must explain why the value is supported and cite the source document titles used.",
  ].join("\n");
}

function buildI485FinalValidationSystemPrompt(basePrompt: string) {
  return [
    basePrompt.trim(),
    "You are validating a completed USCIS Form I-485 field map for the principal applicant.",
    'Return JSON only in the shape {"corrections":[{"pdfFieldName":"string","value":"string"}],"blankOut":["string"],"warnings":["string"]}',
    "The canonical dossier is the source of truth.",
    "Blank any answer that is unsupported, contradictory, or obviously copied into the wrong related-person section.",
    "Correct obvious consistency issues across repeated principal-applicant fields.",
    "Do not create new facts that are not grounded in the dossier or evidence.",
    "Keep attorney, interpreter, preparer, signature, barcode, and USCIS officer fields blank.",
  ].join("\n");
}

function uniqueLimitedStrings(items: string[], limit: number) {
  return Array.from(
    new Set(items.map((item) => normalizeConsolidatedKnowledgeText(item)).filter(Boolean)),
  ).slice(0, limit);
}

function buildI485CompletionSuggestionsFallback(input: {
  unresolvedFields: string[];
  validationWarnings: string[];
  canonicalDossier: I485CanonicalDossier | null;
}): FilledFormCompletionSuggestions {
  const attorneyQuestions: string[] = [];
  const clientQuestions: string[] = [];
  const sponsorQuestions: string[] = [];
  const requestedDocuments: string[] = [];

  const unresolved = input.unresolvedFields.map((item) => String(item ?? "").trim()).filter(Boolean);
  const warnings = input.validationWarnings.map((item) => String(item ?? "").trim()).filter(Boolean);
  const unresolvedTopics = input.canonicalDossier?.unresolvedTopics ?? [];
  const conflictTopics = input.canonicalDossier?.conflictTopics ?? [];

  const combinedTopics = [...unresolved, ...unresolvedTopics, ...conflictTopics, ...warnings];

  for (const topic of combinedTopics) {
    const normalized = normalizeKnowledgeSearchText(topic);

    if (
      normalized.includes("unauthorized work") ||
      normalized.includes("violated status") ||
      normalized.includes("arrest") ||
      normalized.includes("criminal") ||
      normalized.includes("inadmiss") ||
      normalized.includes("immigration violation")
    ) {
      attorneyQuestions.push(`Review and confirm the correct legal answer for: ${topic}`);
      clientQuestions.push(`Provide a detailed timeline and explanation for: ${topic}`);
      continue;
    }

    if (
      normalized.includes("parent") ||
      normalized.includes("mother") ||
      normalized.includes("father") ||
      normalized.includes("birth")
    ) {
      clientQuestions.push(`Confirm the exact parent information needed for: ${topic}`);
      requestedDocuments.push("Birth certificate or civil record confirming parent names and place of birth.");
      continue;
    }

    if (
      normalized.includes("spouse") ||
      normalized.includes("married") ||
      normalized.includes("marital")
    ) {
      clientQuestions.push(`Confirm the complete marital history details needed for: ${topic}`);
      requestedDocuments.push("Marriage certificate, divorce decree, or spouse identity documents as applicable.");
      continue;
    }

    if (
      normalized.includes("child") ||
      normalized.includes("children") ||
      normalized.includes("dependent")
    ) {
      clientQuestions.push(`Confirm the dependent or child details needed for: ${topic}`);
      requestedDocuments.push("Birth certificate or passport for each child/dependent mentioned in the filing.");
      continue;
    }

    if (
      normalized.includes("address") ||
      normalized.includes("mailing") ||
      normalized.includes("residence")
    ) {
      clientQuestions.push(`Confirm the full address history needed for: ${topic}`);
      requestedDocuments.push("Lease, utility bill, ID, or other address evidence covering the missing period.");
      continue;
    }

    if (
      normalized.includes("passport") ||
      normalized.includes("i-94") ||
      normalized.includes("travel") ||
      normalized.includes("entry") ||
      normalized.includes("visa")
    ) {
      clientQuestions.push(`Confirm the immigration/travel detail needed for: ${topic}`);
      requestedDocuments.push("Passport biographic page, visa page, entry stamp, and I-94 travel record.");
      continue;
    }

    if (
      normalized.includes("employ") ||
      normalized.includes("job") ||
      normalized.includes("position") ||
      normalized.includes("wage") ||
      normalized.includes("offer")
    ) {
      sponsorQuestions.push(`Confirm the employment-based sponsorship detail needed for: ${topic}`);
      requestedDocuments.push("Employment verification letter, job offer confirmation, or recent sponsor support letter.");
      continue;
    }

    if (
      normalized.includes("category") ||
      normalized.includes("petition") ||
      normalized.includes("priority date") ||
      normalized.includes("basis")
    ) {
      attorneyQuestions.push(`Confirm the correct adjustment category or petition basis for: ${topic}`);
      requestedDocuments.push("Underlying petition approval notice, labor certification, or attorney filing memo.");
      continue;
    }

    clientQuestions.push(`Clarify the missing detail for: ${topic}`);
  }

  return {
    summary:
      unresolved.length || unresolvedTopics.length || conflictTopics.length
        ? "The following items should be clarified or documented before finalizing the I-485."
        : "No additional completion suggestions were generated.",
    attorneyQuestions: uniqueLimitedStrings(attorneyQuestions, 16),
    clientQuestions: uniqueLimitedStrings(clientQuestions, 24),
    sponsorQuestions: uniqueLimitedStrings(sponsorQuestions, 16),
    requestedDocuments: uniqueLimitedStrings(requestedDocuments, 20),
  };
}

async function buildI485CompletionSuggestions(input: {
  lawFirmId: string;
  formName: string;
  basePrompt: string;
  unresolvedFields: string[];
  validationWarnings: string[];
  canonicalDossier: I485CanonicalDossier | null;
  fieldAuditEntries: FilledFormAuditEntry[];
}) {
  const fallback = buildI485CompletionSuggestionsFallback({
    unresolvedFields: input.unresolvedFields,
    validationWarnings: input.validationWarnings,
    canonicalDossier: input.canonicalDossier,
  });

  if (
    !input.unresolvedFields.length &&
    !(input.canonicalDossier?.unresolvedTopics.length ?? 0) &&
    !(input.canonicalDossier?.conflictTopics.length ?? 0)
  ) {
    return fallback;
  }

  try {
    const completion = await runJsonChatCompletion({
      lawFirmId: input.lawFirmId,
      systemPrompt: [
        input.basePrompt.trim(),
        `You are reviewing USCIS Form I-485 completion gaps for ${input.formName}.`,
        "Return JSON only in the shape:",
        '{"summary":"string","attorneyQuestions":["string"],"clientQuestions":["string"],"sponsorQuestions":["string"],"requestedDocuments":["string"]}',
        "Your goal is to identify what is still needed to safely complete the filing.",
        "Be conservative. Do not ask for unnecessary items.",
        "Questions for the attorney should focus on legal strategy, category, conflicts, inadmissibility, and sensitive yes/no questions.",
        "Questions for the client should focus on personal history, addresses, family, travel, immigration history, and factual clarifications.",
        "Questions for the sponsor should focus on employment-based sponsorship facts, job offer, work history support, and petitioner confirmation.",
        "requestedDocuments should list concrete records that could resolve the missing or conflicting fields.",
      ].join("\n"),
      userPrompt: JSON.stringify({
        unresolvedFields: input.unresolvedFields,
        validationWarnings: input.validationWarnings,
        canonicalDossier:
          input.canonicalDossier ?
            {
              overview: input.canonicalDossier.overview,
              unresolvedTopics: input.canonicalDossier.unresolvedTopics,
              conflictTopics: input.canonicalDossier.conflictTopics,
              sections: input.canonicalDossier.sections.map((section) => ({
                title: section.title,
                missingInformation: section.missingInformation,
                conflicts: section.conflicts,
              })),
            }
          : null,
        evidenceUsed: input.fieldAuditEntries.slice(0, 120).map((entry) => ({
          label: entry.label,
          sourceTitles: entry.sourceTitles,
        })),
      }),
      maxCompletionTokens: 1400,
    });

    return {
      summary: normalizeConsolidatedKnowledgeText(completion.json.summary) || fallback.summary,
      attorneyQuestions: uniqueLimitedStrings(
        Array.isArray(completion.json.attorneyQuestions) ? completion.json.attorneyQuestions.map(String) : fallback.attorneyQuestions,
        16,
      ),
      clientQuestions: uniqueLimitedStrings(
        Array.isArray(completion.json.clientQuestions) ? completion.json.clientQuestions.map(String) : fallback.clientQuestions,
        24,
      ),
      sponsorQuestions: uniqueLimitedStrings(
        Array.isArray(completion.json.sponsorQuestions) ? completion.json.sponsorQuestions.map(String) : fallback.sponsorQuestions,
        16,
      ),
      requestedDocuments: uniqueLimitedStrings(
        Array.isArray(completion.json.requestedDocuments) ? completion.json.requestedDocuments.map(String) : fallback.requestedDocuments,
        20,
      ),
    } satisfies FilledFormCompletionSuggestions;
  } catch {
    return fallback;
  }
}

function extendConsolidatedKnowledgeWithI485Dossier(input: {
  consolidatedKnowledge?: {
    summaryText: string;
    profileFacts: ConsolidatedKnowledgeProfileFact[];
    knowledgeFacts: ConsolidatedKnowledgeFact[];
  } | null;
  dossier: I485CanonicalDossier | null;
}) {
  if (!input.dossier) {
    return input.consolidatedKnowledge ?? null;
  }

  const baseKnowledgeFacts = input.consolidatedKnowledge?.knowledgeFacts ?? [];
  const dossierFacts = input.dossier.sections.flatMap((section) =>
    section.facts.map(
      (fact) =>
        ({
          factKey: `${section.sectionCode}_${fact.factKey}`,
          label: `${section.title} • ${fact.label}`,
          value: fact.value,
          confidence: fact.confidence,
          evidence: fact.evidence,
          sourceTitles: fact.sourceTitles,
        }) satisfies ConsolidatedKnowledgeFact,
    ),
  );

  return {
    summaryText:
      input.dossier.overview ||
      input.consolidatedKnowledge?.summaryText ||
      "Canonical I-485 dossier",
    profileFacts: input.consolidatedKnowledge?.profileFacts ?? [],
    knowledgeFacts: [...baseKnowledgeFacts, ...dossierFacts].slice(0, 220),
  };
}

function applyI485DeterministicCleanup(input: {
  fields: Array<{
    pdfFieldName: string;
    label: string;
    dataType: string;
    value: string | null;
    sectionName?: string | null;
    pageNumber?: number | null;
  }>;
  values: Record<string, string>;
}) {
  for (const field of input.fields) {
    if (isI485AdministrativeField(field)) {
      delete input.values[field.pdfFieldName];
    }
  }
}

async function buildI485CanonicalDossier(input: {
  lawFirmId: string;
  clientId: string;
  actorUserId: string;
  formName: string;
  basePrompt: string;
  fields: Array<{
    pdfFieldName: string;
    label: string;
    dataType: string;
    value: string | null;
    instructions?: string | null;
    sectionName?: string | null;
    pageNumber?: number | null;
  }>;
  evidence: ClientFormAiEvidenceBundle;
  consolidatedKnowledge?: {
    summaryText: string;
    profileFacts: ConsolidatedKnowledgeProfileFact[];
    knowledgeFacts: ConsolidatedKnowledgeFact[];
  } | null;
  onProgress?: ((update: {
    stageCode: string;
    progressPercent: number;
    detailText?: string | null;
  }) => Promise<void> | void) | null;
}) {
  const activeSections = I485_SECTION_DEFINITIONS.filter((section) =>
    input.fields.some((field) => section.matchesField(field) && !isI485AdministrativeField(field)),
  );

  const sections: I485SectionDossier[] = [];
  let totalInputTokens = 0;
  let totalOutputTokens = 0;

  for (const [sectionIndex, definition] of activeSections.entries()) {
    const sectionFields = input.fields.filter((field) => definition.matchesField(field));
    const searchTerms = buildI485SectionSearchTerms(definition, sectionFields);
    const selectedAuthoritativeSourceDocuments = selectRelevantAuthoritativeI485SourceDocuments(
      input.evidence.documents,
      input.evidence.clientProfile,
      definition,
      sectionFields,
      8,
    );
    const selectedDocuments = mergeUniqueI485EvidenceDocuments(
      selectedAuthoritativeSourceDocuments,
      selectRelevantKnowledgeDocuments(
        input.evidence.documents.filter(
          (document) =>
            !isPrimaryI485FormDocument(document) &&
            !isSecondaryAttorneyPreparedImmigrationFormDocument(document) &&
            (definition.code === "children" ||
              definition.code === "marital_history" ||
              isLikelyPrincipalApplicantDocument(document, input.evidence.clientProfile)),
        ),
        searchTerms,
        (document) =>
          `${document.title} ${document.document_type_code} ${document.extracted_text ?? ""} ${document.scope_code}`,
        18,
      ),
      22,
    );
    const selectedNotes = selectRelevantKnowledgeDocuments(
      input.evidence.repositoryNotes,
      searchTerms,
      (note) => `${note.item_type_code} ${note.subject ?? ""} ${note.body_text ?? ""}`,
      10,
    );
    const selectedStructuredFacts = selectRelevantClientFacts(input.evidence.facts, searchTerms, 18);
    const selectedProfileFacts = selectRelevantConsolidatedProfileFacts(
      input.consolidatedKnowledge?.profileFacts ?? [],
      searchTerms,
      18,
    );
    const selectedKnowledgeFacts = selectRelevantConsolidatedKnowledgeFacts(
      input.consolidatedKnowledge?.knowledgeFacts ?? [],
      searchTerms,
      28,
    );

    if (input.onProgress) {
      const progressPercent =
        44 + Math.round(((sectionIndex + 1) / Math.max(activeSections.length, 1)) * 18);
      await input.onProgress({
        stageCode: "gathering_evidence",
        progressPercent,
        detailText: `Building canonical I-485 section ${sectionIndex + 1} of ${activeSections.length}: ${definition.title}.`,
      });
    }

    try {
      const completion = await runJsonChatCompletion({
        lawFirmId: input.lawFirmId,
        systemPrompt: buildI485SectionDossierSystemPrompt({
          basePrompt: input.basePrompt,
          sectionTitle: definition.title,
          sectionDescription: definition.description,
        }),
        userPrompt: JSON.stringify({
          formName: input.formName,
          sectionCode: definition.code,
          sectionTitle: definition.title,
          sectionDescription: definition.description,
          keywords: definition.keywords,
          clientProfile:
            input.evidence.clientProfile ?
              {
                firstName: input.evidence.clientProfile.first_name,
                middleName: input.evidence.clientProfile.middle_name,
                lastName: input.evidence.clientProfile.last_name,
                preferredName: input.evidence.clientProfile.preferred_name,
                dateOfBirth: input.evidence.clientProfile.date_of_birth,
                email: input.evidence.clientProfile.email,
                phone: input.evidence.clientProfile.phone,
                preferredLanguage: input.evidence.clientProfile.preferred_language,
                countryOfCitizenship: input.evidence.clientProfile.country_of_citizenship,
                immigrationStatus: input.evidence.clientProfile.immigration_status,
              }
            : null,
          canonicalKnowledge:
            input.consolidatedKnowledge ?
              {
                summaryText: limitText(input.consolidatedKnowledge.summaryText, 2600),
                profileFacts: selectedProfileFacts.map((fact) => ({
                  fieldKey: fact.fieldKey,
                  label: fact.label,
                  value: fact.value,
                  confidence: fact.confidence,
                  evidence: limitText(fact.evidence, 240),
                })),
                knowledgeFacts: selectedKnowledgeFacts.map((fact) => ({
                  factKey: fact.factKey,
                  label: fact.label,
                  value: fact.value,
                  confidence: fact.confidence,
                  evidence: limitText(fact.evidence, 240),
                  sourceTitles: fact.sourceTitles,
                })),
              }
            : null,
          structuredFacts: selectedStructuredFacts.map((fact) => ({
            fieldKey: fact.fieldKey,
            label: fact.label,
            rawValue: fact.rawValue,
            confidence: fact.confidence,
            statusCode: fact.statusCode,
          })),
          fieldsInSection: sectionFields.map((field) => ({
            pdfFieldName: field.pdfFieldName,
            label: field.label,
            dataType: field.dataType,
            customInstructions: field.instructions ?? null,
          })),
          documents: selectedDocuments.map((document) => ({
            title: document.title,
            documentTypeCode: document.document_type_code,
            scopeCode: document.scope_code,
            extractedText: buildI485SectionDocumentExcerpt({
              document,
              section: definition,
              fieldLabels: sectionFields.map((field) => field.label),
              maxChars: 4200,
            }),
          })),
          repositoryNotes: selectedNotes.map((note) => ({
            itemTypeCode: note.item_type_code,
            subject: note.subject,
            bodyText: limitText(note.body_text, 900),
          })),
        }),
        maxCompletionTokens: 2200,
      });

      totalInputTokens += completion.usage.inputTokens;
      totalOutputTokens += completion.usage.outputTokens;

      sections.push({
        sectionCode: definition.code,
        title: definition.title,
        summary: normalizeConsolidatedKnowledgeText(completion.json.summary),
        facts: normalizeI485SectionFacts(completion.json.facts),
        missingInformation: normalizeI485StringList(completion.json.missingInformation),
        conflicts: normalizeI485StringList(completion.json.conflicts),
      });
    } catch (error) {
      sections.push({
        sectionCode: definition.code,
        title: definition.title,
        summary: "",
        facts: [],
        missingInformation: [],
        conflicts: [
          `Section dossier synthesis failed: ${error instanceof Error ? error.message : "Unexpected error"}`,
        ],
      });
    }
  }

  let overview = buildI485CanonicalDossierFallbackOverview(sections);
  let unresolvedTopics = Array.from(
    new Set(sections.flatMap((section) => section.missingInformation)),
  ).slice(0, 24);
  let conflictTopics = Array.from(new Set(sections.flatMap((section) => section.conflicts))).slice(
    0,
    24,
  );

  if (sections.length > 1) {
    try {
      const completion = await runJsonChatCompletion({
        lawFirmId: input.lawFirmId,
        systemPrompt: [
          "You are finalizing a canonical USCIS I-485 dossier for the principal applicant.",
          'Return JSON only in the shape {"overview":"string","unresolvedTopics":["string"],"conflictTopics":["string"]}',
          "Summarize the strongest canonical facts and highlight unresolved or conflicting areas that matter for the I-485.",
        ].join("\n"),
        userPrompt: JSON.stringify({
          formName: input.formName,
          sections: sections.map((section) => ({
            sectionCode: section.sectionCode,
            title: section.title,
            summary: section.summary,
            facts: section.facts.slice(0, 10),
            missingInformation: section.missingInformation,
            conflicts: section.conflicts,
          })),
        }),
        maxCompletionTokens: 1600,
      });

      totalInputTokens += completion.usage.inputTokens;
      totalOutputTokens += completion.usage.outputTokens;

      overview = normalizeConsolidatedKnowledgeText(completion.json.overview) || overview;
      unresolvedTopics =
        normalizeI485StringList(completion.json.unresolvedTopics, 24) || unresolvedTopics;
      conflictTopics = normalizeI485StringList(completion.json.conflictTopics, 24) || conflictTopics;
    } catch {
      // Keep fallback overview and lists.
    }
  }

  const dossier: I485CanonicalDossier = {
    formCode: "uscis_i_485",
    generatedAt: new Date().toISOString(),
    overview,
    unresolvedTopics,
    conflictTopics,
    sections,
    sourceDocumentCount: input.evidence.documents.length,
    sourceNoteCount: input.evidence.repositoryNotes.length,
    repositoryItemId: null,
  };

  const repositoryItemId = await upsertSpecializedClientKnowledgeRepositoryItem({
    lawFirmId: input.lawFirmId,
    clientId: input.clientId,
    actorUserId: input.actorUserId,
    formName: input.formName,
    agentCode: "uscis_i_485",
    bodyText: JSON.stringify({
      kind: "client_knowledge_synthesis_specialized",
      ...dossier,
    }),
    summaryText: dossier.overview,
    metadataJson: {
      kind: "client_knowledge_synthesis_specialized",
      formCode: "uscis_i_485",
      generatedAt: dossier.generatedAt,
      sectionCount: dossier.sections.length,
      sourceDocumentCount: dossier.sourceDocumentCount,
      sourceNoteCount: dossier.sourceNoteCount,
      unresolvedTopicCount: dossier.unresolvedTopics.length,
      conflictTopicCount: dossier.conflictTopics.length,
    },
  });

  return {
    dossier: {
      ...dossier,
      repositoryItemId,
    } satisfies I485CanonicalDossier,
    inputTokens: totalInputTokens,
    outputTokens: totalOutputTokens,
  };
}

async function validateI485FilledFieldValues(input: {
  lawFirmId: string;
  formName: string;
  basePrompt: string;
  fields: Array<{
    pdfFieldName: string;
    label: string;
    dataType: string;
    value: string | null;
    sectionName?: string | null;
    pageNumber?: number | null;
  }>;
  values: Record<string, string>;
  dossier: I485CanonicalDossier | null;
}) {
  const answeredFields = input.fields
    .map((field) => ({
      ...field,
      value: input.values[field.pdfFieldName] ?? field.value ?? null,
    }))
    .filter((field) => field.value?.trim() && !isI485AdministrativeField(field));

  if (!answeredFields.length || !input.dossier) {
    return {
      corrections: [] as Array<{ pdfFieldName: string; value: string }>,
      blankOut: [] as string[],
      warnings: [] as string[],
      inputTokens: 0,
      outputTokens: 0,
    };
  }

  const completion = await runJsonChatCompletion({
    lawFirmId: input.lawFirmId,
    systemPrompt: buildI485FinalValidationSystemPrompt(input.basePrompt),
    userPrompt: JSON.stringify({
      formName: input.formName,
      canonicalDossier: {
        overview: input.dossier.overview,
        unresolvedTopics: input.dossier.unresolvedTopics,
        conflictTopics: input.dossier.conflictTopics,
        sections: input.dossier.sections.map((section) => ({
          sectionCode: section.sectionCode,
          title: section.title,
          summary: section.summary,
          facts: section.facts.slice(0, 10),
        })),
      },
      answeredFields: answeredFields.map((field) => ({
        pdfFieldName: field.pdfFieldName,
        label: field.label,
        dataType: field.dataType,
        value: field.value,
      })),
    }),
    maxCompletionTokens: 1800,
  });

  const rawCorrections = Array.isArray(completion.json.corrections)
    ? completion.json.corrections
    : [];
  const corrections = rawCorrections
    .map((entry) => {
      if (!entry || typeof entry !== "object") {
        return null;
      }

      const pdfFieldName = String(
        (entry as { pdfFieldName?: unknown }).pdfFieldName ?? "",
      ).trim();
      const value = normalizeAiAnswerValue((entry as { value?: unknown }).value);
      if (!pdfFieldName || !value) {
        return null;
      }

      return { pdfFieldName, value };
    })
    .filter((entry): entry is { pdfFieldName: string; value: string } => Boolean(entry));

  return {
    corrections,
    blankOut: normalizeI485StringList(completion.json.blankOut, 64),
    warnings: normalizeI485StringList(completion.json.warnings, 24),
    inputTokens: completion.usage.inputTokens,
    outputTokens: completion.usage.outputTokens,
  };
}

async function inferI485FormFieldValuesWithAi(input: {
  lawFirmId: string;
  clientId: string;
  actorUserId: string;
  formName: string;
  fields: Array<{
    pdfFieldName: string;
    label: string;
    dataType: string;
    value: string | null;
    instructions?: string | null;
    sectionName?: string | null;
    pageNumber?: number | null;
  }>;
  basePrompt: string;
  consolidatedKnowledge?: {
    summaryText: string;
    profileFacts: ConsolidatedKnowledgeProfileFact[];
    knowledgeFacts: ConsolidatedKnowledgeFact[];
  } | null;
  onProgress?: ((update: {
    stageCode: string;
    progressPercent: number;
    detailText?: string | null;
  }) => Promise<void> | void) | null;
}): Promise<SpecializedFormInferenceResult> {
  const evidence = await loadClientFormAiEvidenceBundle({
    lawFirmId: input.lawFirmId,
    clientId: input.clientId,
    caseId: null,
  });
  const legacyArtifacts = extractLegacyEmploymentAdjustmentArtifactsFromDocuments(evidence.documents);
  const legacyKnowledge = buildLegacyEmploymentAdjustmentKnowledge(legacyArtifacts);
  const enrichedConsolidatedKnowledge =
    extendConsolidatedKnowledgeWithLegacyEmploymentAdjustment({
      consolidatedKnowledge: input.consolidatedKnowledge ?? null,
      legacyKnowledge,
    });

  const dossierResult = await buildI485CanonicalDossier({
    lawFirmId: input.lawFirmId,
    clientId: input.clientId,
    actorUserId: input.actorUserId,
    formName: input.formName,
    basePrompt: input.basePrompt,
    fields: input.fields,
    evidence,
    consolidatedKnowledge: enrichedConsolidatedKnowledge,
    onProgress: input.onProgress,
  });
  const canonicalDossier = mergeI485LegacyKnowledgeIntoDossier({
    dossier: dossierResult.dossier,
    legacyKnowledge,
  });

  const values: Record<string, string> = {};
  const fieldAuditEntries = new Map<string, FilledFormAuditEntry>();
  const validationWarnings = [
    ...canonicalDossier.unresolvedTopics.map((item) => `Missing evidence: ${item}`),
    ...canonicalDossier.conflictTopics.map((item) => `Conflict detected: ${item}`),
  ];
  let totalInputTokens = dossierResult.inputTokens;
  let totalOutputTokens = dossierResult.outputTokens;
  const deterministicSourceProfile = buildI485DeterministicSourceProfile({
    evidence,
    clientId: input.clientId,
    legacyArtifacts,
  });
  seedI485FieldValuesFromDeterministicSources({
    fields: input.fields,
    values,
    fieldAuditEntries,
    sourceProfile: deterministicSourceProfile,
    evidence,
  });

  const activeSections = I485_SECTION_DEFINITIONS.map((definition) => ({
    definition,
    fields: input.fields.filter(
      (field) =>
        definition.matchesField(field) &&
        !isI485AdministrativeField(field) &&
        !(field.value?.trim() || values[field.pdfFieldName]),
    ),
  })).filter((section) => section.fields.length > 0);

  for (const [sectionIndex, section] of activeSections.entries()) {
    if (input.onProgress) {
      const progressPercent =
        63 + Math.round(((sectionIndex + 1) / Math.max(activeSections.length, 1)) * 18);
      await input.onProgress({
        stageCode: "running_ai",
        progressPercent,
        detailText: `Filling I-485 section ${sectionIndex + 1} of ${activeSections.length}: ${section.definition.title}.`,
      });
    }

    const searchTerms = buildI485SectionSearchTerms(section.definition, section.fields);
    const selectedAuthoritativeSourceDocuments = selectRelevantAuthoritativeI485SourceDocuments(
      evidence.documents,
      evidence.clientProfile,
      section.definition,
      section.fields,
      8,
    );
    const selectedDocuments = mergeUniqueI485EvidenceDocuments(
      selectedAuthoritativeSourceDocuments,
      selectRelevantKnowledgeDocuments(
        evidence.documents.filter(
          (document) =>
            !isPrimaryI485FormDocument(document) &&
            !isSecondaryAttorneyPreparedImmigrationFormDocument(document) &&
            (section.definition.code === "children" ||
              section.definition.code === "marital_history" ||
              isLikelyPrincipalApplicantDocument(document, evidence.clientProfile)),
        ),
        searchTerms,
        (document) =>
          `${document.title} ${document.document_type_code} ${document.extracted_text ?? ""} ${document.scope_code}`,
        20,
      ),
      24,
    );
    const selectedRepositoryNotes = selectRelevantKnowledgeDocuments(
      evidence.repositoryNotes,
      searchTerms,
      (note) => `${note.item_type_code} ${note.subject ?? ""} ${note.body_text ?? ""}`,
      12,
    );
    const selectedFacts = selectRelevantClientFacts(evidence.facts, searchTerms, 20);
    const dossierSections = canonicalDossier.sections.filter(
      (entry) =>
        entry.sectionCode === section.definition.code ||
        entry.sectionCode === "identity_biographic" ||
        entry.sectionCode === "immigration_history",
    );
    const sectionKnownFieldValues = input.fields
      .map((field) => ({
        pdfFieldName: field.pdfFieldName,
        label: field.label,
        value: values[field.pdfFieldName] ?? field.value,
      }))
      .filter((field) => field.value?.trim());

    if (selectedAuthoritativeSourceDocuments.length) {
      try {
        const seededFromSourceDocuments = await seedI485FieldValuesFromAuthoritativeSources({
          lawFirmId: input.lawFirmId,
          formName: input.formName,
          basePrompt: input.basePrompt,
          section: section.definition,
          unresolvedFields: section.fields.filter(
            (field) => !(field.value?.trim() || values[field.pdfFieldName]),
          ),
          knownFieldValues: sectionKnownFieldValues,
          sourceDocuments: selectedAuthoritativeSourceDocuments,
          dossier: canonicalDossier,
        });

        totalInputTokens += seededFromSourceDocuments.inputTokens;
        totalOutputTokens += seededFromSourceDocuments.outputTokens;
        validationWarnings.push(
          ...seededFromSourceDocuments.warnings.map(
            (warning) => `${section.definition.title}: ${warning}`,
          ),
        );

        for (const [pdfFieldName, value] of Object.entries(seededFromSourceDocuments.values)) {
          if (!value || values[pdfFieldName]) {
            continue;
          }
          values[pdfFieldName] = value;
        }

        for (const auditEntry of seededFromSourceDocuments.fieldAuditEntries) {
          if (!fieldAuditEntries.has(auditEntry.pdfFieldName)) {
            fieldAuditEntries.set(auditEntry.pdfFieldName, auditEntry);
          }
        }
      } catch (error) {
        validationWarnings.push(
          `${section.definition.title}: authoritative source seeding failed (${error instanceof Error ? error.message : "Unexpected error"}).`,
        );
      }
    }

    const unresolvedSectionFields = section.fields.filter(
      (field) => !(field.value?.trim() || values[field.pdfFieldName]),
    );

    if (!unresolvedSectionFields.length) {
      continue;
    }

    try {
      const completion = await runJsonChatCompletion({
        lawFirmId: input.lawFirmId,
        systemPrompt: buildI485SectionFillSystemPrompt({
          basePrompt: input.basePrompt,
          sectionTitle: section.definition.title,
          sectionDescription: section.definition.description,
        }),
        userPrompt: JSON.stringify({
          formName: input.formName,
          sectionCode: section.definition.code,
          sectionTitle: section.definition.title,
          unresolvedFields: unresolvedSectionFields.map((field) => ({
            pdfFieldName: field.pdfFieldName,
            label: field.label,
            dataType: field.dataType,
            customInstructions: field.instructions ?? null,
            pageNumber: field.pageNumber ?? null,
          })),
          knownFieldValues: input.fields
            .map((field) => ({
              pdfFieldName: field.pdfFieldName,
              label: field.label,
              value: values[field.pdfFieldName] ?? field.value,
            }))
            .filter((field) => field.value?.trim())
            .slice(0, 220),
          clientProfile:
            evidence.clientProfile ?
              {
                firstName: evidence.clientProfile.first_name,
                middleName: evidence.clientProfile.middle_name,
                lastName: evidence.clientProfile.last_name,
                preferredName: evidence.clientProfile.preferred_name,
                dateOfBirth: evidence.clientProfile.date_of_birth,
                email: evidence.clientProfile.email,
                phone: evidence.clientProfile.phone,
                preferredLanguage: evidence.clientProfile.preferred_language,
                countryOfCitizenship: evidence.clientProfile.country_of_citizenship,
                immigrationStatus: evidence.clientProfile.immigration_status,
              }
            : null,
          canonicalDossier: {
            overview: canonicalDossier.overview,
            sections: dossierSections.map((entry) => ({
              sectionCode: entry.sectionCode,
              title: entry.title,
              summary: entry.summary,
                facts: entry.facts,
                missingInformation: entry.missingInformation,
                conflicts: entry.conflicts,
              })),
          },
          canonicalKnowledge:
            enrichedConsolidatedKnowledge ?
              {
                summaryText: limitText(enrichedConsolidatedKnowledge.summaryText, 3000),
                profileFacts: selectRelevantConsolidatedProfileFacts(
                  enrichedConsolidatedKnowledge.profileFacts,
                  searchTerms,
                  20,
                ).map((fact) => ({
                  fieldKey: fact.fieldKey,
                  label: fact.label,
                  value: fact.value,
                  confidence: fact.confidence,
                  evidence: limitText(fact.evidence, 240),
                })),
                knowledgeFacts: selectRelevantConsolidatedKnowledgeFacts(
                  enrichedConsolidatedKnowledge.knowledgeFacts,
                  searchTerms,
                  32,
                ).map((fact) => ({
                  factKey: fact.factKey,
                  label: fact.label,
                  value: fact.value,
                  confidence: fact.confidence,
                  evidence: limitText(fact.evidence, 240),
                  sourceTitles: fact.sourceTitles,
                })),
              }
            : null,
          facts: selectedFacts.map((fact) => ({
            fieldKey: fact.fieldKey,
            label: fact.label,
            rawValue: fact.rawValue,
            confidence: fact.confidence,
            statusCode: fact.statusCode,
          })),
          documents: selectedDocuments.map((document) => ({
            title: document.title,
            documentTypeCode: document.document_type_code,
            scopeCode: document.scope_code,
            extractedText: buildI485SectionDocumentExcerpt({
              document,
              section: section.definition,
              fieldLabels: unresolvedSectionFields.map((field) => field.label),
              maxChars: 4200,
            }),
          })),
          repositoryNotes: selectedRepositoryNotes.map((note) => ({
            itemTypeCode: note.item_type_code,
            subject: note.subject,
            bodyText: limitText(note.body_text, 900),
          })),
        }),
        maxCompletionTokens: 2600,
      });

      totalInputTokens += completion.usage.inputTokens;
      totalOutputTokens += completion.usage.outputTokens;

      const rawAnswers = Array.isArray(completion.json.answers) ? completion.json.answers : [];
      const rawWarnings = normalizeI485StringList(completion.json.warnings, 16);
      validationWarnings.push(...rawWarnings.map((warning) => `${section.definition.title}: ${warning}`));

      for (const answer of rawAnswers) {
        if (!answer || typeof answer !== "object") {
          continue;
        }

        const pdfFieldName = String(
          (answer as { pdfFieldName?: unknown }).pdfFieldName ?? "",
        ).trim();
        if (!pdfFieldName) {
          continue;
        }

        const normalizedValue = normalizeAiAnswerValue((answer as { value?: unknown }).value);
        if (!normalizedValue) {
          continue;
        }

        values[pdfFieldName] = normalizedValue;
        const matchingField = unresolvedSectionFields.find(
          (field) => field.pdfFieldName === pdfFieldName,
        );
        fieldAuditEntries.set(pdfFieldName, {
          sectionTitle: section.definition.title,
          label: matchingField?.label ?? pdfFieldName,
          pdfFieldName,
          value: normalizedValue,
          rationale: normalizeFieldAuditRationale(
            (answer as { rationale?: unknown }).rationale,
            `Filled from ${section.definition.title} evidence.`,
          ),
          sourceTitles: normalizeConsolidatedKnowledgeSourceTitles(
            (answer as { sourceTitles?: unknown }).sourceTitles,
          ),
          evidenceExcerpt:
            limitText(
              normalizeConsolidatedKnowledgeText((answer as { evidence?: unknown }).evidence),
              400,
            ) || null,
          confidence: normalizeFieldAuditConfidence(
            (answer as { confidence?: unknown }).confidence,
            0.84,
          ),
        });
      }
    } catch (error) {
      validationWarnings.push(
        `${section.definition.title}: section fill failed (${error instanceof Error ? error.message : "Unexpected error"}).`,
      );
    }
  }

  const fallbackFieldStates = input.fields.map((field) => ({
    ...field,
    value: values[field.pdfFieldName] ?? field.value,
  }));

  const remainingUnresolvedFields = fallbackFieldStates.filter(
    (field) => !field.value?.trim() && !isI485AdministrativeField(field),
  );

  if (remainingUnresolvedFields.length > 0) {
    if (input.onProgress) {
      await input.onProgress({
        stageCode: "running_ai",
        progressPercent: 84,
        detailText: `Running a fallback pass for ${remainingUnresolvedFields.length} unresolved I-485 field(s).`,
      });
    }

    try {
      const fallbackResult = await inferFormFieldValuesWithAiCore({
        lawFirmId: input.lawFirmId,
        caseId: null,
        clientId: input.clientId,
        formName: `${input.formName} (I-485 fallback pass)`,
        fields: fallbackFieldStates,
        systemPrompt: buildFormAiSystemPrompt(input.basePrompt),
        consolidatedKnowledge: extendConsolidatedKnowledgeWithI485Dossier({
          consolidatedKnowledge: enrichedConsolidatedKnowledge ?? null,
          dossier: canonicalDossier,
        }),
      });

      totalInputTokens += fallbackResult.inputTokens;
      totalOutputTokens += fallbackResult.outputTokens;

      for (const [pdfFieldName, value] of Object.entries(fallbackResult.values)) {
        if (!values[pdfFieldName] && value) {
          values[pdfFieldName] = value;
          const matchingAudit = fallbackResult.fieldAuditEntries?.find(
            (entry) => entry.pdfFieldName === pdfFieldName,
          );
          if (matchingAudit) {
            fieldAuditEntries.set(pdfFieldName, matchingAudit);
          }
        }
      }
    } catch (error) {
      validationWarnings.push(
        `Fallback fill pass failed: ${error instanceof Error ? error.message : "Unexpected error"}.`,
      );
    }
  }

  applyI485DeterministicCleanup({
    fields: input.fields,
    values,
  });

  if (input.onProgress) {
    await input.onProgress({
      stageCode: "running_ai",
      progressPercent: 91,
      detailText: "Running a final I-485 validation pass.",
    });
  }

  try {
    const validationResult = await validateI485FilledFieldValues({
      lawFirmId: input.lawFirmId,
      formName: input.formName,
      basePrompt: input.basePrompt,
      fields: input.fields,
      values,
      dossier: canonicalDossier,
    });

    totalInputTokens += validationResult.inputTokens;
    totalOutputTokens += validationResult.outputTokens;
    validationWarnings.push(...validationResult.warnings);

    for (const correction of validationResult.corrections) {
      if (correction.pdfFieldName) {
        const existingAudit = fieldAuditEntries.get(correction.pdfFieldName);
        if (isLockedI485DeterministicAuditEntry(existingAudit)) {
          continue;
        }
        values[correction.pdfFieldName] = correction.value;
        fieldAuditEntries.set(correction.pdfFieldName, {
          sectionTitle: existingAudit?.sectionTitle || "Final validation",
          label:
            existingAudit?.label ||
            input.fields.find((field) => field.pdfFieldName === correction.pdfFieldName)?.label ||
            correction.pdfFieldName,
          pdfFieldName: correction.pdfFieldName,
          value: correction.value,
          rationale: existingAudit?.rationale
            ? `${existingAudit.rationale} Corrected during final I-485 validation.`
            : "Corrected during final I-485 validation against the canonical dossier.",
          sourceTitles: existingAudit?.sourceTitles ?? ["Canonical I-485 dossier"],
          evidenceExcerpt: existingAudit?.evidenceExcerpt ?? null,
          confidence: existingAudit?.confidence ?? 0.8,
        });
      }
    }

    for (const pdfFieldName of validationResult.blankOut) {
      const existingAudit = fieldAuditEntries.get(pdfFieldName);
      if (isLockedI485DeterministicAuditEntry(existingAudit)) {
        continue;
      }
      delete values[pdfFieldName];
      fieldAuditEntries.delete(pdfFieldName);
    }
  } catch (error) {
    validationWarnings.push(
      `Final I-485 validation failed: ${error instanceof Error ? error.message : "Unexpected error"}.`,
    );
  }

  applyI485DeterministicCleanup({
    fields: input.fields,
    values,
  });

  return {
    values,
    inputTokens: totalInputTokens,
    outputTokens: totalOutputTokens,
    canonicalDossier,
    validationWarnings: Array.from(new Set(validationWarnings)).slice(0, 48),
    fieldAuditEntries: Array.from(fieldAuditEntries.values()),
  };
}

async function listBestClientFactsForAi(input: {
  lawFirmId: string;
  clientId: string;
  caseId?: string | null;
}) {
  const rows = await prisma.$queryRaw<
    Array<{
      field_key: string;
      label: string;
      raw_value: string | null;
      confidence_score: number;
      status_code: string;
      case_id: string | null;
      created_at: Date;
    }>
  >`
    SELECT
      df.field_key,
      df.label,
      cf.raw_value,
      cf.confidence_score,
      cf.status_code,
      cf.case_id,
      cf.created_at
    FROM case_facts cf
    JOIN data_fields df ON df.id = cf.data_field_id
    WHERE cf.law_firm_id = ${input.lawFirmId}
      AND cf.client_id = ${input.clientId}
      AND (${input.caseId ?? null} IS NULL OR cf.case_id = ${input.caseId ?? null} OR cf.case_id IS NULL)
      AND cf.deleted_at IS NULL
      AND cf.status_code <> 'rejected'
    ORDER BY
      df.field_key ASC,
      CASE WHEN cf.status_code = 'confirmed' THEN 0 ELSE 1 END ASC,
      cf.confidence_score DESC,
      cf.created_at DESC
  `;

  const bestRows = new Map<string, (typeof rows)[number]>();

  for (const row of rows) {
    if (!bestRows.has(row.field_key)) {
      bestRows.set(row.field_key, row);
    }
  }

  return Array.from(bestRows.values()).map((row) => ({
    fieldKey: row.field_key,
    label: row.label,
    rawValue: row.raw_value,
    confidence: Number(row.confidence_score),
    statusCode: row.status_code,
    caseId: row.case_id,
  }));
}

function buildFormAiSystemPrompt(basePrompt: string) {
  return [
    basePrompt.trim(),
    "Return JSON only in the shape {\"answers\":[{\"pdfFieldName\":\"string\",\"value\":\"string\",\"rationale\":\"string\",\"evidence\":\"string\",\"sourceTitles\":[\"string\"],\"confidence\":0.0}]}",
    "When a consolidated client knowledge base is provided, use it as the primary canonical summary, but still leave unsupported fields unanswered.",
    "Only answer fields when the value is grounded in the provided evidence.",
    "Leave unsupported or ambiguous fields out of the response.",
    "Do not invent facts, names, IDs, dates, numbers, addresses, or checkboxes.",
    "When a field includes customInstructions, follow them as field-specific guidance about how to fill it and where to look for the evidence.",
    "For checkbox fields, use true or false.",
    "For date fields, use MM/DD/YYYY when the evidence supports a date.",
    "For dropdown fields, use the exact option value when it is clearly supported.",
    "For each answered field, include a short rationale, evidence excerpt, and the source document titles used.",
  ].join("\n");
}

async function inferFormFieldValuesWithAiCore(input: {
  lawFirmId: string;
  caseId?: string | null;
  clientId: string;
  formName: string;
  fields: Array<{
    pdfFieldName: string;
    label: string;
    dataType: string;
    value: string | null;
    instructions?: string | null;
    sectionName?: string | null;
    pageNumber?: number | null;
  }>;
  systemPrompt: string;
  consolidatedKnowledge?: {
    summaryText: string;
    profileFacts: ConsolidatedKnowledgeProfileFact[];
    knowledgeFacts: ConsolidatedKnowledgeFact[];
  } | null;
}) {
  const unresolvedFields = input.fields.filter((field) => !field.value?.trim());

  if (!unresolvedFields.length) {
    return {
      values: {} as Record<string, string>,
      inputTokens: 0,
      outputTokens: 0,
    };
  }

  const [clientProfile] = await prisma.$queryRaw<
    Array<{
      first_name: string;
      middle_name: string | null;
      last_name: string;
      preferred_name: string | null;
      date_of_birth: Date | null;
      email: string | null;
      phone: string | null;
      preferred_language: string | null;
      country_of_citizenship: string | null;
      immigration_status: string | null;
    }>
  >`
    SELECT
      first_name,
      middle_name,
      last_name,
      preferred_name,
      date_of_birth,
      email,
      phone,
      preferred_language,
      country_of_citizenship,
      immigration_status
    FROM clients
    WHERE id = ${input.clientId}
      AND deleted_at IS NULL
    LIMIT 1
  `;

  const facts =
    input.caseId ?
      await listCaseFacts({
        lawFirmId: input.lawFirmId,
        caseId: input.caseId,
      }) :
      await listBestClientFactsForAi({
        lawFirmId: input.lawFirmId,
        clientId: input.clientId,
      });

  const documents = await prisma.$queryRaw<
    Array<{
      title: string;
      document_type_code: string;
      extracted_text: string | null;
      scope_code: string;
      created_at: Date;
    }>
  >`
    SELECT
      title,
      document_type_code,
      extracted_text,
      CASE
        WHEN case_id IS NULL THEN 'client'
        WHEN ${input.caseId ?? null} IS NOT NULL AND case_id = ${input.caseId ?? null} THEN 'case'
        ELSE 'client_case'
      END AS scope_code,
      created_at
    FROM document_records
    WHERE law_firm_id = ${input.lawFirmId}
      AND client_id = ${input.clientId}
    ORDER BY created_at DESC
    LIMIT 60
  `;

  const repositoryNotes = await prisma.$queryRaw<
    Array<{
      item_type_code: string;
      subject: string | null;
      body_text: string | null;
      created_at: Date;
    }>
  >`
    SELECT item_type_code, subject, body_text, created_at
    FROM repository_items
    WHERE law_firm_id = ${input.lawFirmId}
      AND client_id = ${input.clientId}
      AND body_text IS NOT NULL
      AND body_text <> ''
      AND (
        source_entity_type IS NULL OR source_entity_type NOT IN (
          'client_knowledge_synthesis',
          'client_knowledge_synthesis_specialized'
        )
      )
    ORDER BY occurred_at DESC, created_at DESC
    LIMIT 80
  `;

  const chunkSize = 40;
  const values: Record<string, string> = {};
  const fieldAuditEntries = new Map<string, FilledFormAuditEntry>();
  let totalInputTokens = 0;
  let totalOutputTokens = 0;

  for (let index = 0; index < unresolvedFields.length; index += chunkSize) {
    const unresolvedChunk = unresolvedFields.slice(index, index + chunkSize);
    const searchTerms = extractKnowledgeSearchTerms(
      `${input.formName} ${unresolvedChunk
        .map((field) => `${field.label} ${field.instructions ?? ""}`)
        .join(" ")}`,
    );

    const selectedDocuments = selectRelevantKnowledgeDocuments(
      documents,
      searchTerms,
      (document) =>
        `${document.title} ${document.document_type_code} ${document.extracted_text ?? ""} ${document.scope_code}`,
      14,
    );
    const selectedRepositoryNotes = selectRelevantKnowledgeDocuments(
      repositoryNotes,
      searchTerms,
      (item) => `${item.item_type_code} ${item.subject ?? ""} ${item.body_text ?? ""}`,
      12,
    );

    const promptPayload = {
      formName: input.formName,
      unresolvedFields: unresolvedChunk.map((field) => ({
        pdfFieldName: field.pdfFieldName,
        label: field.label,
        dataType: field.dataType,
        customInstructions: field.instructions ?? null,
        sectionName: field.sectionName ?? null,
        pageNumber: field.pageNumber ?? null,
      })),
      knownFieldValues: input.fields
        .filter((field) => field.value?.trim() || values[field.pdfFieldName])
        .map((field) => ({
          pdfFieldName: field.pdfFieldName,
          label: field.label,
          value: values[field.pdfFieldName] ?? field.value,
        })),
      clientProfile: clientProfile
        ? {
            firstName: clientProfile.first_name,
            middleName: clientProfile.middle_name,
            lastName: clientProfile.last_name,
            preferredName: clientProfile.preferred_name,
            dateOfBirth: clientProfile.date_of_birth,
            email: clientProfile.email,
            phone: clientProfile.phone,
            preferredLanguage: clientProfile.preferred_language,
            countryOfCitizenship: clientProfile.country_of_citizenship,
            immigrationStatus: clientProfile.immigration_status,
          }
        : null,
      consolidatedKnowledge: input.consolidatedKnowledge
        ? {
            summaryText: limitText(input.consolidatedKnowledge.summaryText, 5000),
            profileFacts: input.consolidatedKnowledge.profileFacts.slice(0, 40).map((fact) => ({
              fieldKey: fact.fieldKey,
              label: fact.label,
              value: fact.value,
              confidence: fact.confidence,
              evidence: limitText(fact.evidence, 300),
            })),
            knowledgeFacts: input.consolidatedKnowledge.knowledgeFacts.slice(0, 120).map((fact) => ({
              factKey: fact.factKey,
              label: fact.label,
              value: fact.value,
              confidence: fact.confidence,
              evidence: limitText(fact.evidence, 300),
              sourceTitles: fact.sourceTitles,
            })),
          }
        : null,
      facts: facts.map((fact) => ({
        fieldKey: fact.fieldKey,
        label: fact.label,
        rawValue: fact.rawValue,
        confidence: fact.confidence,
        statusCode: "statusCode" in fact ? fact.statusCode : null,
        caseId: "caseId" in fact ? fact.caseId : null,
      })),
      documents: selectedDocuments.map((document) => ({
        title: document.title,
        documentTypeCode: document.document_type_code,
        scope: document.scope_code,
        extractedText: limitText(document.extracted_text, 1800),
      })),
      repositoryNotes: selectedRepositoryNotes.map((item) => ({
        itemTypeCode: item.item_type_code,
        subject: item.subject,
        bodyText: limitText(item.body_text, 900),
      })),
    };

    const completion = await runJsonChatCompletion({
      lawFirmId: input.lawFirmId,
      systemPrompt: input.systemPrompt,
      userPrompt: JSON.stringify(promptPayload),
      maxCompletionTokens: 2200,
    });

    totalInputTokens += completion.usage.inputTokens;
    totalOutputTokens += completion.usage.outputTokens;

    const rawAnswers = Array.isArray(completion.json.answers) ? completion.json.answers : [];
    const unresolvedByPdfName = new Map(unresolvedChunk.map((field) => [field.pdfFieldName, field]));

    for (const item of rawAnswers) {
      if (!item || typeof item !== "object") {
        continue;
      }

      const pdfFieldName = String(
        (item as { pdfFieldName?: unknown }).pdfFieldName ?? "",
      ).trim();

      if (!pdfFieldName || !unresolvedByPdfName.has(pdfFieldName)) {
        continue;
      }

      const normalizedValue = normalizeAiAnswerValue((item as { value?: unknown }).value ?? "");
      if (!normalizedValue) {
        continue;
      }

      values[pdfFieldName] = normalizedValue;
      const matchingField = unresolvedByPdfName.get(pdfFieldName);
      fieldAuditEntries.set(pdfFieldName, {
        sectionTitle: matchingField?.sectionName || input.formName,
        label: matchingField?.label ?? pdfFieldName,
        pdfFieldName,
        value: normalizedValue,
        rationale: normalizeFieldAuditRationale(
          (item as { rationale?: unknown }).rationale,
          "Filled from client evidence available in the repository and structured records.",
        ),
        sourceTitles: normalizeConsolidatedKnowledgeSourceTitles(
          (item as { sourceTitles?: unknown }).sourceTitles,
        ),
        evidenceExcerpt:
          limitText(
            normalizeConsolidatedKnowledgeText((item as { evidence?: unknown }).evidence),
            400,
          ) || null,
        confidence: normalizeFieldAuditConfidence(
          (item as { confidence?: unknown }).confidence,
          0.8,
        ),
      });
    }
  }

  return {
    values,
    inputTokens: totalInputTokens,
    outputTokens: totalOutputTokens,
    fieldAuditEntries: Array.from(fieldAuditEntries.values()),
  };
}

async function inferFormFieldValuesWithAi(input: {
  lawFirmId: string;
  caseId: string;
  clientId: string;
  formName: string;
  fields: Array<{
    pdfFieldName: string;
    label: string;
    dataType: string;
    value: string | null;
    instructions?: string | null;
  }>;
  consolidatedKnowledge?: {
    summaryText: string;
    profileFacts: ConsolidatedKnowledgeProfileFact[];
    knowledgeFacts: ConsolidatedKnowledgeFact[];
  } | null;
}) {
  return inferFormFieldValuesWithAiCore({
    lawFirmId: input.lawFirmId,
    caseId: input.caseId,
    clientId: input.clientId,
    formName: input.formName,
    fields: input.fields,
    systemPrompt: buildFormAiSystemPrompt(
      "You fill legal form fields using the provided client profile, structured facts, repository notes, and document text.",
    ),
    consolidatedKnowledge: input.consolidatedKnowledge ?? null,
  });
}

async function ensureWorkflowInstanceForCase(input: {
  lawFirmId: string;
  caseId: string;
  clientId: string;
  workflowTemplateId: string;
  actorUserId: string;
}) {
  const [workflowTemplate] = await prisma.$queryRaw<Array<{ id: string }>>`
    SELECT id
    FROM workflow_templates
    WHERE id = ${input.workflowTemplateId}
    LIMIT 1
  `;

  if (!workflowTemplate) {
    throw new Error("Workflow template not found");
  }

  const [latestInstance] = await prisma.$queryRaw<
    Array<{
      id: string;
      workflow_template_id: string;
    }>
  >`
    SELECT id, workflow_template_id
    FROM workflow_instances
    WHERE case_id = ${input.caseId}
      AND law_firm_id = ${input.lawFirmId}
    ORDER BY created_at DESC
    LIMIT 1
  `;

  if (latestInstance?.workflow_template_id === input.workflowTemplateId) {
    return {
      workflowInstanceId: latestInstance.id,
      activated: false,
      createdSteps: 0,
      createdRequiredDocuments: 0,
      createdCaseForms: 0,
    };
  }

  const caseFactMap = await getWorkflowConditionFactMap({
    clientId: input.clientId,
    caseId: input.caseId,
  });
  const dependentCount = resolveWorkflowDependentCount(caseFactMap);
  const steps = await prisma.$queryRaw<
    Array<{
      id: string;
      step_order: number;
      condition_expression_json: unknown;
    }>
  >`
    SELECT id, step_order, condition_expression_json
    FROM workflow_steps
    WHERE workflow_template_id = ${input.workflowTemplateId}
      AND retired_at IS NULL
    ORDER BY step_order ASC
  `;

  const materializedSteps = steps.flatMap((step) => {
    const appliesWhen = parseWorkflowConditionConfig(step.condition_expression_json).appliesWhen;

    if (!matchesWorkflowAppliesWhen(appliesWhen, caseFactMap)) {
      return [];
    }

    return [
      {
        ...step,
        appliesWhen,
      },
    ];
  });

  const workflowInstanceId = createId();
  await prisma.$executeRaw`
    INSERT INTO workflow_instances (
      id, law_firm_id, case_id, workflow_template_id, status_code, started_at,
      completed_at, current_step_order, template_snapshot_json, created_by_user_id,
      created_at, updated_at
    ) VALUES (
      ${workflowInstanceId},
      ${input.lawFirmId},
      ${input.caseId},
      ${input.workflowTemplateId},
      'active',
      NOW(),
      NULL,
      ${materializedSteps[0]?.step_order ?? 1},
      NULL,
      ${input.actorUserId},
      CURRENT_TIMESTAMP,
      CURRENT_TIMESTAMP
    )
  `;

  for (const [stepIndex, step] of materializedSteps.entries()) {
    const decisionJson =
      step.appliesWhen.includes("has_dependents") && dependentCount > 0
        ? JSON.stringify({ dependentCount })
        : null;

    await prisma.$executeRaw`
      INSERT INTO workflow_step_instances (
        id, workflow_instance_id, workflow_step_id, assignee_user_id, status_code,
        due_at, started_at, completed_at, decision_json, created_at, updated_at
      ) VALUES (
        ${createId()},
        ${workflowInstanceId},
        ${step.id},
        NULL,
        ${stepIndex === 0 ? "active" : "pending"},
        NULL,
        ${stepIndex === 0 ? new Date() : null},
        NULL,
        ${decisionJson},
        CURRENT_TIMESTAMP,
        CURRENT_TIMESTAMP
      )
    `;
  }

  const requiredDocuments = await prisma.$queryRaw<
    Array<{
      id: string;
      document_type_code: string;
      display_name: string;
      condition_expression_json: unknown;
      is_required: number;
    }>
  >`
    SELECT id, document_type_code, display_name, condition_expression_json, is_required
    FROM workflow_required_documents
    WHERE workflow_template_id = ${input.workflowTemplateId}
      AND retired_at IS NULL
  `;

  let createdRequiredDocuments = 0;
  for (const item of requiredDocuments) {
    const appliesWhen = parseWorkflowConditionConfig(item.condition_expression_json).appliesWhen;

    if (!matchesWorkflowAppliesWhen(appliesWhen, caseFactMap)) {
      continue;
    }

    const repeatCount = appliesWhen.includes("has_dependents")
      ? Math.max(1, dependentCount)
      : 1;

    for (let index = 0; index < repeatCount; index += 1) {
      const requirementName =
        repeatCount > 1
          ? `${item.display_name} • Dependente ${index + 1}`
          : item.display_name;

      await prisma.$executeRaw`
        INSERT INTO case_required_documents (
          id, law_firm_id, case_id, workflow_instance_id, workflow_required_document_id,
          document_type_code, requirement_name, is_required, status_code, due_at,
          satisfied_by_document_record_id, notes, created_at, updated_at
        ) VALUES (
          ${createId()},
          ${input.lawFirmId},
          ${input.caseId},
          ${workflowInstanceId},
          ${item.id},
          ${item.document_type_code},
          ${requirementName},
          ${item.is_required},
          'pending',
          NULL,
          NULL,
          NULL,
          CURRENT_TIMESTAMP,
          CURRENT_TIMESTAMP
        )
      `;
      createdRequiredDocuments += 1;
    }
  }

  const requiredForms = await prisma.$queryRaw<
    Array<{
      id: string;
      form_template_id: string;
      form_name: string;
      condition_expression_json: unknown;
      is_required: number;
    }>
  >`
    SELECT wrf.id, wrf.form_template_id, ft.name AS form_name, wrf.condition_expression_json, wrf.is_required
    FROM workflow_required_forms wrf
    JOIN form_templates ft ON ft.id = wrf.form_template_id
    WHERE wrf.workflow_template_id = ${input.workflowTemplateId}
      AND wrf.retired_at IS NULL
  `;

  let createdCaseForms = 0;
  for (const item of requiredForms) {
    const appliesWhen = parseWorkflowConditionConfig(item.condition_expression_json).appliesWhen;

    if (!matchesWorkflowAppliesWhen(appliesWhen, caseFactMap)) {
      continue;
    }

    const repeatCount = appliesWhen.includes("has_dependents")
      ? Math.max(1, dependentCount)
      : 1;

    for (let index = 0; index < repeatCount; index += 1) {
      const requirementName =
        repeatCount > 1
          ? `${item.form_name} • Dependente ${index + 1}`
          : item.form_name;

      await prisma.$executeRaw`
        INSERT INTO case_forms (
          id, law_firm_id, case_id, workflow_instance_id, workflow_required_form_id,
          form_template_id, requirement_name, is_required, status_code, due_at,
          last_reviewed_at, created_at, updated_at
        ) VALUES (
          ${createId()},
          ${input.lawFirmId},
          ${input.caseId},
          ${workflowInstanceId},
          ${item.id},
          ${item.form_template_id},
          ${requirementName},
          ${item.is_required},
          'pending',
          NULL,
          NULL,
          CURRENT_TIMESTAMP,
          CURRENT_TIMESTAMP
        )
      `;
      createdCaseForms += 1;
    }
  }

  return {
    workflowInstanceId,
    activated: true,
    createdSteps: materializedSteps.length,
    createdRequiredDocuments,
    createdCaseForms,
  };
}

export async function fillCaseForms(input: {
  lawFirmId: string;
  caseId: string;
  actorUserId: string;
  workflowInstanceId?: string | null;
}) {
  const [caseRow] = await prisma.$queryRaw<
    Array<{ id: string; client_id: string; title: string | null }>
  >`
    SELECT id, client_id, title
    FROM cases
    WHERE id = ${input.caseId}
      AND law_firm_id = ${input.lawFirmId}
      AND deleted_at IS NULL
    LIMIT 1
  `;

  if (!caseRow) throw new Error("Case not found");

  let consolidatedKnowledge: ConsolidatedClientKnowledge | null = null;
  try {
    consolidatedKnowledge = await synthesizeClientKnowledgeBase({
      lawFirmId: input.lawFirmId,
      clientId: caseRow.client_id,
      caseId: input.caseId,
      actorUserId: input.actorUserId,
      formName: "workflow form generation",
    });
  } catch {
    consolidatedKnowledge = null;
  }

  const aiRun = await createAiRun({
    lawFirmId: input.lawFirmId,
    caseId: input.caseId,
    clientId: caseRow.client_id,
    runType: "form_fill",
  });

  try {
    const caseForms = await prisma.$queryRaw<
      Array<{
        id: string;
        form_template_id: string;
        form_name: string;
        form_code: string | null;
        requirement_name: string;
      }>
    >`
      SELECT
        cf.id,
        cf.form_template_id,
        ft.name AS form_name,
        ft.code AS form_code,
        cf.requirement_name
      FROM case_forms cf
      JOIN form_templates ft ON ft.id = cf.form_template_id
      WHERE cf.case_id = ${input.caseId}
        AND cf.law_firm_id = ${input.lawFirmId}
        AND (${input.workflowInstanceId ?? null} IS NULL OR cf.workflow_instance_id = ${input.workflowInstanceId ?? null})
      ORDER BY cf.created_at ASC
    `;
    const effectiveCaseForms = await resolveEffectiveGapAnalysisCaseForms({
      lawFirmId: input.lawFirmId,
      caseTitle: caseRow.title,
      caseForms,
    });
    const effectiveTemplateIds = Array.from(
      new Set(effectiveCaseForms.map((item) => item.effective_form_template_id)),
    );
    const effectiveTemplates =
      effectiveTemplateIds.length > 0
        ? await prisma.$queryRaw<
            Array<{
              id: string;
              name: string;
              code: string | null;
              base_pdf_file_id: string | null;
              base_pdf_storage_provider: string | null;
              base_pdf_object_key: string | null;
              base_pdf_original_file_name: string | null;
            }>
          >`
            SELECT
              ft.id,
              ft.name,
              ft.code,
              ft.base_pdf_file_id,
              base_pdf.storage_provider AS base_pdf_storage_provider,
              base_pdf.object_key AS base_pdf_object_key,
              base_pdf.original_file_name AS base_pdf_original_file_name
            FROM form_templates ft
            LEFT JOIN files base_pdf ON base_pdf.id = ft.base_pdf_file_id
            WHERE ft.id IN (${Prisma.join(effectiveTemplateIds)})
          `
        : [];
    const effectiveTemplateById = new Map(
      effectiveTemplates.map((item) => [item.id, item]),
    );
    const dataFields = await getDataFields();
    const dataFieldById = new Map(dataFields.map((item) => [item.id, item]));
    const dataFieldByKey = new Map(dataFields.map((item) => [item.field_key, item]));

    const generatedDocs: Array<{
      generatedDocumentId: string;
      repositoryItemId: string;
      fileId: string;
      fileName: string;
      formName: string;
      unresolved: string[];
    }> = [];
    let totalInputTokens = 0;
    let totalOutputTokens = 0;

    for (const caseForm of effectiveCaseForms) {
      const effectiveTemplate = effectiveTemplateById.get(caseForm.effective_form_template_id);
      const fields = await prisma.$queryRaw<
        Array<{
          id: string;
          field_key: string;
          label: string;
          pdf_field_name: string;
          data_type: string;
        }>
      >`
        SELECT id, field_key, label, pdf_field_name, data_type
        FROM form_fields
        WHERE form_template_id = ${caseForm.effective_form_template_id}
        ORDER BY created_at ASC
      `;

      const mappings = await prisma.$queryRaw<
        Array<{ form_field_id: string; data_field_id: string }>
      >`
        SELECT form_field_id, data_field_id
        FROM form_mappings
        WHERE form_template_id = ${caseForm.effective_form_template_id}
          AND is_active = 1
      `;

      const fieldStates: Array<{
        pdfFieldName: string;
        label: string;
        dataType: string;
        value: string | null;
      }> = [];

      for (const field of fields) {
        const mapping = mappings.find((item) => item.form_field_id === field.id);
        const dataField = resolveGapAnalysisDataField({
          formCode: caseForm.effective_form_code,
          field: {
            field_key: field.field_key,
            label: field.label,
            pdf_field_name: field.pdf_field_name,
            section_name: null,
          },
          mapping,
          dataFieldById,
          dataFieldByKey,
        });

        let value: string | null = null;
        if (dataField?.id) {
          const coverage = await getBestFactCoverage({
            clientId: caseRow.client_id,
            caseId: input.caseId,
            dataFieldId: dataField.id,
            threshold: 0,
          });

          if (coverage.chosenFactId) {
            const [fact] = await prisma.$queryRaw<Array<{ raw_value: string | null }>>`
              SELECT raw_value
              FROM case_facts
              WHERE id = ${coverage.chosenFactId}
              LIMIT 1
            `;
            value = fact?.raw_value ?? null;
          }
        }

        fieldStates.push({
          pdfFieldName: field.pdf_field_name,
          label: field.label,
          dataType: field.data_type,
          value,
        });
      }

      try {
        const aiResult = await inferFormFieldValuesWithAi({
          lawFirmId: input.lawFirmId,
          caseId: input.caseId,
          clientId: caseRow.client_id,
          formName: caseForm.effective_form_name,
          fields: fieldStates,
          consolidatedKnowledge,
        });

        totalInputTokens += aiResult.inputTokens;
        totalOutputTokens += aiResult.outputTokens;

        for (const field of fieldStates) {
          if (!field.value && aiResult.values[field.pdfFieldName]) {
            field.value = aiResult.values[field.pdfFieldName];
          }
        }
      } catch {
        // Keep deterministic mapped values when AI enrichment is unavailable.
      }

      const filledFields = Object.fromEntries(
        fieldStates.map((field) => [field.pdfFieldName, field.value]),
      );
      const unresolved = fieldStates
        .filter((field) => !field.value?.trim())
        .map((field) => field.label);

      let basePdfBytes: Buffer | null = null;
      if (effectiveTemplate?.base_pdf_storage_provider && effectiveTemplate.base_pdf_object_key) {
        try {
          basePdfBytes = await readBinaryFile({
            storageProvider: effectiveTemplate.base_pdf_storage_provider,
            objectKey: effectiveTemplate.base_pdf_object_key,
          });
        } catch {
          basePdfBytes = null;
        }
      }

      const payload = {
        caseFormId: caseForm.id,
        formName: caseForm.effective_form_name,
        generatedAt: new Date().toISOString(),
        fields: filledFields,
        unresolved,
      };

      const renderedPdf = await buildFilledFormPdf({
        formName: caseForm.requirement_name || caseForm.effective_form_name,
        fields: fieldStates.map((field) => ({
          pdfFieldName: field.pdfFieldName,
          label: field.label,
          value: field.value,
        })),
        unresolved,
        basePdfBytes,
      });
      const fileName = `${caseForm.effective_form_name.replace(/\s+/g, "_")}.pdf`;
      const fileBuffer = renderedPdf.bytes;
      const stored = await saveBinaryFile({
        lawFirmId: input.lawFirmId,
        caseId: input.caseId,
        fileName,
        bytes: fileBuffer,
        kind: "generated",
      });

      const repositoryItemId = await createRepositoryItem({
        lawFirmId: input.lawFirmId,
        clientId: caseRow.client_id,
        caseId: input.caseId,
        itemTypeCode: "generated_form",
        channelCode: "system",
        sourceEntityType: "case_form",
        sourceEntityId: caseForm.id,
        subject: caseForm.effective_form_name,
        bodyText: JSON.stringify(payload),
        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},
          ${caseRow.client_id},
          ${input.caseId},
          ${repositoryItemId},
          'local_dev',
          'workspace',
          ${stored.relativeObjectKey},
          'local',
          ${fileName},
          ${stored.storedFileName},
          'application/pdf',
          ${fileBuffer.length},
          ${stored.checksumSha256},
          0,
          ${input.actorUserId},
          NOW(),
          CURRENT_TIMESTAMP
        )
      `;

      const generatedDocumentId = createId();
      await prisma.$executeRaw`
        INSERT INTO generated_documents (
          id, law_firm_id, client_id, case_id, repository_item_id, file_id,
          document_kind, source_entity_type, source_entity_id, ai_run_id, version_number, created_at
        ) VALUES (
          ${generatedDocumentId},
          ${input.lawFirmId},
          ${caseRow.client_id},
          ${input.caseId},
          ${repositoryItemId},
          ${fileId},
          'form',
          'case_form',
          ${caseForm.id},
          ${aiRun.id},
          1,
          CURRENT_TIMESTAMP
        )
      `;

      await prisma.$executeRaw`
        INSERT INTO form_fill_runs (
          id, law_firm_id, case_form_id, generated_document_id, ai_run_id, status_code,
          mapping_snapshot_json, fill_summary_json, started_at, completed_at, error_message, created_at
        ) VALUES (
          ${createId()},
          ${input.lawFirmId},
          ${caseForm.id},
          ${generatedDocumentId},
          ${aiRun.id},
          'completed',
          NULL,
          ${JSON.stringify({ unresolved })},
          NOW(),
          NOW(),
          NULL,
          CURRENT_TIMESTAMP
        )
      `;

      await prisma.$executeRaw`
        UPDATE case_forms
        SET status_code = 'generated', last_reviewed_at = NOW(), updated_at = CURRENT_TIMESTAMP
        WHERE id = ${caseForm.id}
      `;

      generatedDocs.push({
        generatedDocumentId,
        repositoryItemId,
        fileId,
        fileName,
        formName: caseForm.effective_form_name,
        unresolved,
      });
    }

    await finishAiRun({
      aiRunId: aiRun.id,
      status: "completed",
      inputTokens: totalInputTokens,
      outputTokens: totalOutputTokens,
      estimatedCost: 0,
    });

    return generatedDocs;
  } catch (error) {
    await finishAiRun({
      aiRunId: aiRun.id,
      status: "failed",
      errorMessage: error instanceof Error ? error.message : "Unexpected error",
    });
    throw error;
  }
}

export async function generateClientFormFromTemplate(input: {
  lawFirmId: string;
  clientId: string;
  actorUserId: string;
  formTemplateId: string;
  formName: string;
  agentCode?: string | null;
  agentPrompt?: string | null;
  onProgress?: ((update: {
    stageCode: string;
    progressPercent: number;
    detailText?: string | null;
  }) => Promise<void> | void) | null;
}) {
  if (input.onProgress) {
    await input.onProgress({
      stageCode: "loading_form",
      progressPercent: 5,
      detailText: "Loading form template and client context.",
    });
  }

  const [clientRow] = await prisma.$queryRaw<
    Array<{
      id: string;
      first_name: string;
      last_name: string;
    }>
  >`
    SELECT id, first_name, last_name
    FROM clients
    WHERE id = ${input.clientId}
      AND law_firm_id = ${input.lawFirmId}
      AND deleted_at IS NULL
    LIMIT 1
  `;

  if (!clientRow) {
    throw new Error("Client not found");
  }

  const [templateRow] = await prisma.$queryRaw<
    Array<{
      id: string;
      name: string;
      base_pdf_file_id: string | null;
      base_pdf_storage_provider: string | null;
      base_pdf_object_key: string | null;
      base_pdf_original_file_name: string | null;
    }>
  >`
    SELECT
      ft.id,
      ft.name,
      ft.base_pdf_file_id,
      base_pdf.storage_provider AS base_pdf_storage_provider,
      base_pdf.object_key AS base_pdf_object_key,
      base_pdf.original_file_name AS base_pdf_original_file_name
    FROM form_templates ft
    LEFT JOIN files base_pdf ON base_pdf.id = ft.base_pdf_file_id
    WHERE ft.id = ${input.formTemplateId}
      AND ft.law_firm_id = ${input.lawFirmId}
    LIMIT 1
  `;

  if (!templateRow) {
    throw new Error("Form template not found");
  }

  const aiRun = await createAiRun({
    lawFirmId: input.lawFirmId,
    clientId: input.clientId,
    runType: "form_fill",
  });
  const normalizedAgentCode = String(input.agentCode ?? "").trim();

  try {
    let consolidatedKnowledge: ConsolidatedClientKnowledge | null = null;
    if (normalizedAgentCode !== "uscis_i_485") {
      if (input.onProgress) {
        await input.onProgress({
          stageCode: "gathering_evidence",
          progressPercent: 20,
          detailText: "Building a canonical client knowledge base from the repository.",
        });
      }

      try {
        consolidatedKnowledge = await synthesizeClientKnowledgeBase({
          lawFirmId: input.lawFirmId,
          clientId: input.clientId,
          actorUserId: input.actorUserId,
          formName: input.formName || templateRow.name,
          onProgress: input.onProgress,
        });
      } catch {
        consolidatedKnowledge = null;
      }
    }

    if (input.onProgress) {
      await input.onProgress({
        stageCode: "gathering_evidence",
        progressPercent: 42,
        detailText: "Gathering mapped fields and preparing the consolidated knowledge base for the specialist agent.",
      });
    }

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

    if (fields.length === 0) {
      throw new Error("This form template has no fields configured");
    }

    const mappings = await prisma.$queryRaw<
      Array<{ form_field_id: string; data_field_id: string }>
    >`
      SELECT form_field_id, data_field_id
      FROM form_mappings
      WHERE form_template_id = ${input.formTemplateId}
        AND is_active = 1
    `;

    const fieldStates: Array<{
      pdfFieldName: string;
      label: string;
      dataType: string;
      value: string | null;
      instructions: string | null;
      sectionName: string | null;
      pageNumber: number | null;
    }> = [];
    const fieldAuditByPdfFieldName = new Map<string, FilledFormAuditEntry>();
    const factAuditReferenceCache = new Map<
      string,
      Promise<{ sourceTitles: string[]; evidenceExcerpt: string | null }>
    >();

    for (const field of fields) {
      const mapping = mappings.find((item) => item.form_field_id === field.id);
      let value: string | null = null;

      if (mapping?.data_field_id) {
        const coverage = await getBestFactCoverage({
          clientId: input.clientId,
          caseId: null,
          dataFieldId: mapping.data_field_id,
          threshold: 0,
        });

        if (coverage.chosenFactId) {
          const [fact] = await prisma.$queryRaw<Array<{ raw_value: string | null }>>`
            SELECT raw_value
            FROM case_facts
            WHERE id = ${coverage.chosenFactId}
            LIMIT 1
          `;
          value = fact?.raw_value ?? null;

          if (value?.trim()) {
            if (!factAuditReferenceCache.has(coverage.chosenFactId)) {
              factAuditReferenceCache.set(
                coverage.chosenFactId,
                getFactAuditReference(coverage.chosenFactId),
              );
            }
            const factAuditReference = await factAuditReferenceCache.get(coverage.chosenFactId)!;
            fieldAuditByPdfFieldName.set(field.pdf_field_name, {
              sectionTitle: field.section_name || templateRow.name,
              label: field.label,
              pdfFieldName: field.pdf_field_name,
              value,
              rationale: `Filled from an existing structured fact mapped to this form field. ${coverage.reasoning}`,
              sourceTitles: factAuditReference.sourceTitles,
              evidenceExcerpt: factAuditReference.evidenceExcerpt,
              confidence: coverage.currentConfidence,
            });
          }
        }
      }

      fieldStates.push({
        pdfFieldName: field.pdf_field_name,
        label: field.label,
        dataType: field.data_type,
        value,
        instructions: field.instructions,
        sectionName: field.section_name,
        pageNumber: field.page_number,
      });
    }

    if (input.onProgress) {
      await input.onProgress({
        stageCode: "running_ai",
        progressPercent: 55,
        detailText: "The specialist agent is analyzing the client repository and evidence.",
      });
    }

    const baseAgentPrompt =
      String(input.agentPrompt ?? "").trim() ||
      "You are a specialist legal form-filling agent for this client form.";
    let aiResult: SpecializedFormInferenceResult;

    if (normalizedAgentCode === "uscis_i_485") {
      aiResult = await inferI485FormFieldValuesWithAi({
        lawFirmId: input.lawFirmId,
        clientId: input.clientId,
        actorUserId: input.actorUserId,
        formName: input.formName || templateRow.name,
        fields: fieldStates,
        basePrompt: baseAgentPrompt,
        consolidatedKnowledge,
        onProgress: input.onProgress,
      });
    } else {
      const genericAiResult = await inferFormFieldValuesWithAiCore({
        lawFirmId: input.lawFirmId,
        caseId: null,
        clientId: input.clientId,
        formName: input.formName || templateRow.name,
        fields: fieldStates,
        systemPrompt: buildFormAiSystemPrompt(baseAgentPrompt),
        consolidatedKnowledge,
      });

      aiResult = {
        ...genericAiResult,
        canonicalDossier: null,
        validationWarnings: [],
      };
    }

    for (const field of fieldStates) {
      if (!field.value && aiResult.values[field.pdfFieldName]) {
        field.value = aiResult.values[field.pdfFieldName];
      }
    }

    for (const entry of aiResult.fieldAuditEntries ?? []) {
      fieldAuditByPdfFieldName.set(entry.pdfFieldName, entry);
    }

    let basePdfBytes: Buffer | null = null;
    if (templateRow.base_pdf_storage_provider && templateRow.base_pdf_object_key) {
      try {
        basePdfBytes = await readBinaryFile({
          storageProvider: templateRow.base_pdf_storage_provider,
          objectKey: templateRow.base_pdf_object_key,
        });
      } catch {
        basePdfBytes = null;
      }
    }

    const unresolved = fieldStates
      .filter((field) => !field.value?.trim())
      .map((field) => field.label);
    const filledCount = fieldStates.length - unresolved.length;
    const clientFullName = `${clientRow.first_name} ${clientRow.last_name}`.trim();
    const filledFieldAuditEntries = fieldStates
      .filter((field) => field.value?.trim())
      .map((field) => {
        const existingEntry = fieldAuditByPdfFieldName.get(field.pdfFieldName);
        return {
          sectionTitle: existingEntry?.sectionTitle || field.sectionName || templateRow.name,
          label: field.label,
          pdfFieldName: field.pdfFieldName,
          value: field.value?.trim() || "",
          rationale:
            existingEntry?.rationale ||
            "Filled from existing structured client data available before the specialist inference run.",
          sourceTitles: existingEntry?.sourceTitles ?? [],
          evidenceExcerpt: existingEntry?.evidenceExcerpt ?? null,
          confidence: existingEntry?.confidence ?? null,
        } satisfies FilledFormAuditEntry;
      });
    const completionSuggestions =
      normalizedAgentCode === "uscis_i_485"
        ? await buildI485CompletionSuggestions({
            lawFirmId: input.lawFirmId,
            formName: input.formName || templateRow.name,
            basePrompt: baseAgentPrompt,
            unresolvedFields: unresolved,
            validationWarnings: aiResult.validationWarnings ?? [],
            canonicalDossier: aiResult.canonicalDossier ?? null,
            fieldAuditEntries: filledFieldAuditEntries,
          })
        : null;
    const payload: {
      templateId: string;
      agentCode: string | null;
      formName: string;
      clientId: string;
      generatedAt: string;
      filledCount: number;
      unresolvedCount: number;
      unresolved: string[];
      fields: Record<string, string | null>;
      fieldAuditEntries: FilledFormAuditEntry[];
      completionSuggestions?: FilledFormCompletionSuggestions | null;
      canonicalDossier: {
        overview: string;
        unresolvedTopics: string[];
        conflictTopics: string[];
        repositoryItemId: string | null;
        sections: Array<{
          sectionCode: string;
          title: string;
          summary: string;
          factCount: number;
          missingInformation: string[];
          conflicts: string[];
        }>;
      } | null;
      validationWarnings: string[];
      explanationReport?: {
        generatedDocumentId: string;
        repositoryItemId: string;
        fileId: string;
        fileName: string;
        entryCount: number;
      };
    } = {
      templateId: input.formTemplateId,
      agentCode: input.agentCode ?? null,
      formName: templateRow.name,
      clientId: input.clientId,
      generatedAt: new Date().toISOString(),
      filledCount,
      unresolvedCount: unresolved.length,
      unresolved,
      fields: Object.fromEntries(fieldStates.map((field) => [field.pdfFieldName, field.value])),
      fieldAuditEntries: filledFieldAuditEntries,
      completionSuggestions,
      canonicalDossier:
        aiResult.canonicalDossier ?
          {
            overview: aiResult.canonicalDossier.overview,
            unresolvedTopics: aiResult.canonicalDossier.unresolvedTopics,
            conflictTopics: aiResult.canonicalDossier.conflictTopics,
            repositoryItemId: aiResult.canonicalDossier.repositoryItemId,
            sections: aiResult.canonicalDossier.sections.map((section) => ({
              sectionCode: section.sectionCode,
              title: section.title,
              summary: section.summary,
              factCount: section.facts.length,
              missingInformation: section.missingInformation,
              conflicts: section.conflicts,
            })),
          }
        : null,
      validationWarnings: aiResult.validationWarnings ?? [],
    };

    if (input.onProgress) {
      await input.onProgress({
        stageCode: "rendering_pdf",
        progressPercent: 78,
        detailText: "Writing the filled PDF with the confirmed field values.",
      });
    }

    const renderedPdf = await buildFilledFormPdf({
      formName: templateRow.name,
      fields: fieldStates.map((field) => ({
        pdfFieldName: field.pdfFieldName,
        label: field.label,
        value: field.value,
      })),
      unresolved,
      basePdfBytes,
    });
    const fileName = `${templateRow.name.replace(/\s+/g, "_")}_${clientFullName.replace(/\s+/g, "_") || input.clientId}.pdf`;
    const fileBuffer = renderedPdf.bytes;

    if (input.onProgress) {
      await input.onProgress({
        stageCode: "rendering_pdf",
        progressPercent: 84,
        detailText: "Preparing the field-by-field explanation report.",
      });
    }

    const explanationPdf = await buildFilledFormAuditPdf({
      formName: templateRow.name,
      clientName: clientFullName || input.clientId,
      filledCount,
      unresolvedCount: unresolved.length,
      entries: filledFieldAuditEntries,
      completionSuggestions,
    });
    const explanationFileName = `${templateRow.name.replace(/\s+/g, "_")}_${clientFullName.replace(/\s+/g, "_") || input.clientId}_field_explanation.pdf`;
    const explanationStored = await saveBinaryFile({
      lawFirmId: input.lawFirmId,
      caseId: null,
      fileName: explanationFileName,
      bytes: explanationPdf.bytes,
      kind: "generated",
    });

    if (input.onProgress) {
      await input.onProgress({
        stageCode: "saving_output",
        progressPercent: 90,
        detailText: "Saving the generated form in the client repository.",
      });
    }

    const stored = await saveBinaryFile({
      lawFirmId: input.lawFirmId,
      caseId: null,
      fileName,
      bytes: fileBuffer,
      kind: "generated",
    });

    const explanationRepositoryItemId = await createRepositoryItem({
      lawFirmId: input.lawFirmId,
      clientId: input.clientId,
      itemTypeCode: "generated_form",
      channelCode: "system",
      sourceEntityType: "form_template_audit",
      sourceEntityId: input.formTemplateId,
      subject: `${templateRow.name} field explanation • ${clientFullName || input.clientId}`,
      bodyText: JSON.stringify({
        templateId: input.formTemplateId,
        agentCode: input.agentCode ?? null,
        formName: templateRow.name,
        clientId: input.clientId,
        generatedAt: new Date().toISOString(),
        filledCount,
        unresolvedCount: unresolved.length,
        fieldAuditEntries: filledFieldAuditEntries,
        completionSuggestions,
      }),
      createdByUserId: input.actorUserId,
    });

    const explanationFileId = 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 (
        ${explanationFileId},
        ${input.lawFirmId},
        ${input.clientId},
        NULL,
        ${explanationRepositoryItemId},
        'local_dev',
        'workspace',
        ${explanationStored.relativeObjectKey},
        'local',
        ${explanationFileName},
        ${explanationStored.storedFileName},
        'application/pdf',
        ${explanationPdf.bytes.length},
        ${explanationStored.checksumSha256},
        0,
        ${input.actorUserId},
        NOW(),
        CURRENT_TIMESTAMP
      )
    `;

    const explanationGeneratedDocumentId = createId();
    await prisma.$executeRaw`
      INSERT INTO generated_documents (
        id, law_firm_id, client_id, case_id, repository_item_id, file_id,
        document_kind, source_entity_type, source_entity_id, ai_run_id, version_number, created_at
      ) VALUES (
        ${explanationGeneratedDocumentId},
        ${input.lawFirmId},
        ${input.clientId},
        NULL,
        ${explanationRepositoryItemId},
        ${explanationFileId},
        'form_audit',
        'form_template_audit',
        ${input.formTemplateId},
        ${aiRun.id},
        1,
        CURRENT_TIMESTAMP
      )
    `;

    payload.explanationReport = {
      generatedDocumentId: explanationGeneratedDocumentId,
      repositoryItemId: explanationRepositoryItemId,
      fileId: explanationFileId,
      fileName: explanationFileName,
      entryCount: filledFieldAuditEntries.length,
    };

    const repositoryItemId = await createRepositoryItem({
      lawFirmId: input.lawFirmId,
      clientId: input.clientId,
      itemTypeCode: "generated_form",
      channelCode: "system",
      sourceEntityType: "form_template",
      sourceEntityId: input.formTemplateId,
      subject: `${templateRow.name} • ${clientFullName || input.clientId}`,
      bodyText: JSON.stringify(payload),
      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},
        ${input.clientId},
        NULL,
        ${repositoryItemId},
        'local_dev',
        'workspace',
        ${stored.relativeObjectKey},
        'local',
        ${fileName},
        ${stored.storedFileName},
        'application/pdf',
        ${fileBuffer.length},
        ${stored.checksumSha256},
        0,
        ${input.actorUserId},
        NOW(),
        CURRENT_TIMESTAMP
      )
    `;

    const generatedDocumentId = createId();
    await prisma.$executeRaw`
      INSERT INTO generated_documents (
        id, law_firm_id, client_id, case_id, repository_item_id, file_id,
        document_kind, source_entity_type, source_entity_id, ai_run_id, version_number, created_at
      ) VALUES (
        ${generatedDocumentId},
        ${input.lawFirmId},
        ${input.clientId},
        NULL,
        ${repositoryItemId},
        ${fileId},
        'form',
        'form_template',
        ${input.formTemplateId},
        ${aiRun.id},
        1,
        CURRENT_TIMESTAMP
      )
    `;

    await finishAiRun({
      aiRunId: aiRun.id,
      status: "completed",
      inputTokens: aiResult.inputTokens,
      outputTokens: aiResult.outputTokens,
      estimatedCost: 0,
    });

    return {
      agentCode: String(input.agentCode ?? "").trim() || "specialized_form",
      templateId: input.formTemplateId,
      formName: templateRow.name,
      clientId: input.clientId,
      generatedDocumentId,
      repositoryItemId,
      fileId,
      fileName,
      explanationGeneratedDocumentId,
      explanationRepositoryItemId,
      explanationFileId,
      explanationFileName,
      filledCount,
      unresolvedCount: unresolved.length,
      unresolvedFields: unresolved,
      totalFieldCount: fieldStates.length,
      fieldValues: Object.fromEntries(fieldStates.map((field) => [field.pdfFieldName, field.value])),
      canonicalDossier: aiResult.canonicalDossier ?? null,
      validationWarnings: aiResult.validationWarnings ?? [],
      fieldAuditEntries: filledFieldAuditEntries,
      completionSuggestions,
    };
  } catch (error) {
    await finishAiRun({
      aiRunId: aiRun.id,
      status: "failed",
      errorMessage: error instanceof Error ? error.message : "Unexpected error",
    });
    throw error;
  }
}

function parsePacketTemplateItemConfig(value: unknown) {
  if (!value) {
    return null;
  }

  let parsedValue = value;

  if (typeof parsedValue === "string") {
    try {
      parsedValue = JSON.parse(parsedValue);
    } catch {
      return null;
    }
  }

  if (!parsedValue || typeof parsedValue !== "object") {
    return null;
  }

  return {
    sourceType: String((parsedValue as { sourceType?: string }).sourceType ?? "").trim() || null,
    workflowRequiredDocumentId:
      String(
        (parsedValue as { workflowRequiredDocumentId?: string }).workflowRequiredDocumentId ?? "",
      ).trim() || null,
    workflowRequiredFormId:
      String((parsedValue as { workflowRequiredFormId?: string }).workflowRequiredFormId ?? "").trim() ||
      null,
    requirementCode:
      String((parsedValue as { requirementCode?: string }).requirementCode ?? "").trim() || null,
    conditionKey:
      String((parsedValue as { conditionKey?: string }).conditionKey ?? "").trim() || null,
    partyType:
      String((parsedValue as { partyType?: string }).partyType ?? "").trim() || null,
  };
}

const workflowFinalPacketPartyTypes = [
  "client",
  "sponsor",
  "internal",
  "dependents",
] as const;

function normalizeWorkflowPacketConditionKeys(value: unknown) {
  if (!Array.isArray(value)) {
    return [];
  }

  return Array.from(
    new Set(
      value
        .map((item) =>
          String(item ?? "")
            .normalize("NFD")
            .replace(/[\u0300-\u036f]/g, "")
            .trim()
            .toLowerCase()
            .replace(/[^a-z0-9]+/g, "_")
            .replace(/^_+|_+$/g, ""),
        )
        .filter((item): item is string => Boolean(item)),
    ),
  );
}

function parseWorkflowPacketAppliesWhen(value: unknown) {
  if (!value) {
    return [] as string[];
  }

  let parsedValue = value;

  if (typeof parsedValue === "string") {
    try {
      parsedValue = JSON.parse(parsedValue);
    } catch {
      return [];
    }
  }

  if (!parsedValue || typeof parsedValue !== "object") {
    return [];
  }

  return normalizeWorkflowPacketConditionKeys(
    (parsedValue as { appliesWhen?: unknown[] }).appliesWhen ?? [],
  );
}

function normalizeWorkflowPacketPartyType(
  value: unknown,
  fallback: (typeof workflowFinalPacketPartyTypes)[number] = "client",
) {
  const normalizedValue = String(value ?? "").trim();
  return workflowFinalPacketPartyTypes.includes(
    normalizedValue as (typeof workflowFinalPacketPartyTypes)[number],
  )
    ? (normalizedValue as (typeof workflowFinalPacketPartyTypes)[number])
    : fallback;
}

function resolveDefaultWorkflowPacketPartyType(input: {
  conditionKey?: string | null;
  appliesWhen?: string[];
}) {
  if (
    input.conditionKey === "has_dependents" ||
    (input.appliesWhen ?? []).includes("has_dependents")
  ) {
    return "dependents" as const;
  }

  return "client" as const;
}

export async function assembleCasePacket(input: {
  lawFirmId: string;
  caseId: string;
  actorUserId: string;
  packetTemplateId?: string | null;
  workflowInstanceId?: string | null;
  workflowTemplateId?: string | null;
}) {
  const [caseRow] = await prisma.$queryRaw<
    Array<{
      id: string;
      client_id: string;
      title: string;
      client_name: string;
    }>
  >`
    SELECT
      c.id,
      c.client_id,
      c.title,
      CONCAT_WS(' ', cl.first_name, cl.last_name) AS client_name
    FROM cases c
    JOIN clients cl ON cl.id = c.client_id
    WHERE c.id = ${input.caseId}
      AND c.law_firm_id = ${input.lawFirmId}
      AND c.deleted_at IS NULL
    LIMIT 1
  `;

  if (!caseRow) throw new Error("Case not found");

  const aiRun = await createAiRun({
    lawFirmId: input.lawFirmId,
    caseId: input.caseId,
    clientId: caseRow.client_id,
    runType: "packet_assembly",
  });

  try {
    let workflowInstanceId = input.workflowInstanceId ?? null;
    let workflowTemplateId = input.workflowTemplateId ?? null;

    if (workflowInstanceId) {
      const [workflowInstance] = await prisma.$queryRaw<
        Array<{
          id: string;
          workflow_template_id: string;
        }>
      >`
        SELECT id, workflow_template_id
        FROM workflow_instances
        WHERE id = ${workflowInstanceId}
          AND law_firm_id = ${input.lawFirmId}
          AND case_id = ${input.caseId}
        LIMIT 1
      `;

      if (!workflowInstance) {
        throw new Error("Workflow instance not found for this case");
      }

      workflowTemplateId = workflowInstance.workflow_template_id;
    }

    if (!workflowInstanceId) {
      const workflowInstanceFilter = workflowTemplateId
        ? prisma.$queryRaw<
            Array<{
              id: string;
              workflow_template_id: string;
            }>
          >`
            SELECT id, workflow_template_id
            FROM workflow_instances
            WHERE law_firm_id = ${input.lawFirmId}
              AND case_id = ${input.caseId}
              AND workflow_template_id = ${workflowTemplateId}
            ORDER BY started_at DESC, created_at DESC
            LIMIT 1
          `
        : prisma.$queryRaw<
            Array<{
              id: string;
              workflow_template_id: string;
            }>
          >`
            SELECT id, workflow_template_id
            FROM workflow_instances
            WHERE law_firm_id = ${input.lawFirmId}
              AND case_id = ${input.caseId}
            ORDER BY started_at DESC, created_at DESC
            LIMIT 1
          `;

      const [workflowInstance] = await workflowInstanceFilter;
      workflowInstanceId = workflowInstance?.id ?? null;
      workflowTemplateId = workflowInstance?.workflow_template_id ?? workflowTemplateId;
    }

    const [workflowTemplate] = workflowTemplateId
      ? await prisma.$queryRaw<Array<{ id: string; name: string }>>`
          SELECT id, name
          FROM workflow_templates
          WHERE id = ${workflowTemplateId}
          LIMIT 1
        `
      : [];
    const workflowName = workflowTemplate?.name ?? "Workflow padrao";

    let packetTemplateId = input.packetTemplateId ?? null;
    if (!packetTemplateId && workflowTemplateId) {
      const [workflowPacketTemplate] = await prisma.$queryRaw<Array<{ id: string }>>`
          SELECT id
          FROM packet_templates
          WHERE law_firm_id = ${input.lawFirmId}
            AND code = ${`workflow_${workflowTemplateId}`}
          ORDER BY updated_at DESC
          LIMIT 1
        `;

      packetTemplateId = workflowPacketTemplate?.id ?? null;
    }

    if (!packetTemplateId) {
      const [template] = await prisma.$queryRaw<Array<{ id: string }>>`
        SELECT id
        FROM packet_templates
        WHERE law_firm_id IS NULL
          AND code = 'DEFAULT_CASE_PACKET'
        ORDER BY created_at DESC
        LIMIT 1
      `;
      packetTemplateId = template?.id ?? null;
    }

    const autoLinkedDocuments = await autoLinkCaseRequiredDocumentsFromClientRepository({
      lawFirmId: input.lawFirmId,
      caseId: input.caseId,
      clientId: caseRow.client_id,
      workflowInstanceId,
    });

    const packetTemplateItems = packetTemplateId
      ? await prisma.$queryRaw<
          Array<{
            id: string;
            sort_order: number;
            display_name: string;
            form_template_id: string | null;
            document_type_code: string | null;
            condition_expression_json: unknown;
          }>
        >`
          SELECT
            id,
            sort_order,
            display_name,
            form_template_id,
            document_type_code,
            condition_expression_json
          FROM packet_template_items
          WHERE packet_template_id = ${packetTemplateId}
            AND retired_at IS NULL
          ORDER BY sort_order ASC
        `
      : [];

    const generatedForms = await prisma.$queryRaw<
      Array<{
        id: string;
        document_kind: string;
        source_entity_id: string | null;
        file_id: string;
      }>
    >`
      SELECT gd.id, gd.document_kind, gd.source_entity_id, gd.file_id
      FROM generated_documents gd
      JOIN case_forms cf ON cf.id = gd.source_entity_id
      WHERE gd.law_firm_id = ${input.lawFirmId}
        AND gd.case_id = ${input.caseId}
        AND gd.document_kind = 'form'
        AND gd.source_entity_type = 'case_form'
        AND (${workflowInstanceId ?? null} IS NULL OR cf.workflow_instance_id = ${workflowInstanceId ?? null})
      ORDER BY gd.created_at ASC
    `;

    const generatedCaseForms = await prisma.$queryRaw<
      Array<{
        generated_document_id: string;
        case_form_id: string;
        workflow_required_form_id: string | null;
        form_template_id: string;
        requirement_name: string;
        condition_expression_json: unknown;
      }>
    >`
      SELECT
        gd.id AS generated_document_id,
        cf.id AS case_form_id,
        cf.workflow_required_form_id,
        cf.form_template_id,
        cf.requirement_name,
        wrf.condition_expression_json
      FROM generated_documents gd
      JOIN case_forms cf ON cf.id = gd.source_entity_id
      LEFT JOIN workflow_required_forms wrf ON wrf.id = cf.workflow_required_form_id
      WHERE gd.law_firm_id = ${input.lawFirmId}
        AND gd.case_id = ${input.caseId}
        AND gd.source_entity_type = 'case_form'
        AND gd.document_kind = 'form'
        AND (${workflowInstanceId ?? null} IS NULL OR cf.workflow_instance_id = ${workflowInstanceId ?? null})
      ORDER BY gd.created_at ASC
    `;

    const supportingDocuments = await prisma.$queryRaw<
      Array<{
        id: string;
        title: string;
        file_id: string;
      }>
    >`
      SELECT id, title, file_id
      FROM document_records
      WHERE law_firm_id = ${input.lawFirmId}
        AND case_id = ${input.caseId}
      ORDER BY created_at ASC
    `;

    const caseRequiredDocuments = await prisma.$queryRaw<
      Array<{
        workflow_required_document_id: string | null;
        document_type_code: string;
        requirement_name: string;
        document_record_id: string | null;
        document_title: string | null;
        condition_expression_json: unknown;
      }>
    >`
      SELECT
        crd.workflow_required_document_id,
        crd.document_type_code,
        crd.requirement_name,
        crd.satisfied_by_document_record_id AS document_record_id,
        dr.title AS document_title,
        wrd.condition_expression_json
      FROM case_required_documents crd
      LEFT JOIN document_records dr ON dr.id = crd.satisfied_by_document_record_id
      LEFT JOIN workflow_required_documents wrd ON wrd.id = crd.workflow_required_document_id
      WHERE crd.law_firm_id = ${input.lawFirmId}
        AND crd.case_id = ${input.caseId}
        AND (${workflowInstanceId ?? null} IS NULL OR crd.workflow_instance_id = ${workflowInstanceId ?? null})
      ORDER BY crd.created_at ASC
    `;

    const orderedPacketItems: Array<{
      packetTemplateItemId: string | null;
      sortOrder: number;
      sourceEntityType: "generated_document" | "document_record";
      sourceEntityId: string;
      generatedDocumentId: string | null;
      notes: string | null;
      kind: "form" | "document";
      manifestValue: Record<string, unknown>;
    }> = [];

    for (const templateItem of packetTemplateItems) {
      const config = parsePacketTemplateItemConfig(templateItem.condition_expression_json);
      const configPartyType = normalizeWorkflowPacketPartyType(
        config?.partyType,
        resolveDefaultWorkflowPacketPartyType({
          conditionKey: config?.conditionKey ?? null,
          appliesWhen: parseWorkflowPacketAppliesWhen(templateItem.condition_expression_json),
        }),
      );

      if (config?.sourceType === "condition" && config.conditionKey) {
        const conditionKey = config.conditionKey;
        const matchedConditionDocuments = caseRequiredDocuments.filter(
          (item) =>
            Boolean(item.document_record_id) &&
            parseWorkflowPacketAppliesWhen(item.condition_expression_json).includes(
              conditionKey,
            ),
        );
        const matchedConditionForms = generatedCaseForms.filter((item) =>
          parseWorkflowPacketAppliesWhen(item.condition_expression_json).includes(
            conditionKey,
          ),
        );

        let conditionGroupOffset = 0;

        for (const matchedDocument of matchedConditionDocuments) {
          conditionGroupOffset += 1;
          orderedPacketItems.push({
            packetTemplateItemId: templateItem.id,
            sortOrder: templateItem.sort_order + conditionGroupOffset / 100,
            sourceEntityType: "document_record",
            sourceEntityId: matchedDocument.document_record_id as string,
            generatedDocumentId: null,
            notes: matchedDocument.document_title || matchedDocument.requirement_name,
            kind: "document",
            manifestValue: {
              id: matchedDocument.document_record_id,
              displayName: matchedDocument.document_title || matchedDocument.requirement_name,
              requirementName: matchedDocument.requirement_name,
              title: matchedDocument.document_title,
              partyType: configPartyType,
              packetTemplateItemId: templateItem.id,
              sortOrder: templateItem.sort_order + conditionGroupOffset / 100,
            },
          });
        }

        for (const matchedForm of matchedConditionForms) {
          conditionGroupOffset += 1;
          orderedPacketItems.push({
            packetTemplateItemId: templateItem.id,
            sortOrder: templateItem.sort_order + conditionGroupOffset / 100,
            sourceEntityType: "generated_document",
            sourceEntityId: matchedForm.generated_document_id,
            generatedDocumentId: matchedForm.generated_document_id,
            notes: matchedForm.requirement_name,
            kind: "form",
            manifestValue: {
              id: matchedForm.generated_document_id,
              displayName: matchedForm.requirement_name,
              requirementName: matchedForm.requirement_name,
              caseFormId: matchedForm.case_form_id,
              partyType: configPartyType,
              packetTemplateItemId: templateItem.id,
              sortOrder: templateItem.sort_order + conditionGroupOffset / 100,
            },
          });
        }

        continue;
      }

      if (templateItem.form_template_id) {
        const matchedForm = generatedCaseForms.find(
          (item) =>
            ((config?.workflowRequiredFormId &&
              item.workflow_required_form_id === config.workflowRequiredFormId) ||
              item.form_template_id === templateItem.form_template_id),
        );

        if (!matchedForm) {
          continue;
        }

        orderedPacketItems.push({
          packetTemplateItemId: templateItem.id,
          sortOrder: templateItem.sort_order,
          sourceEntityType: "generated_document",
          sourceEntityId: matchedForm.generated_document_id,
          generatedDocumentId: matchedForm.generated_document_id,
          notes: matchedForm.requirement_name,
          kind: "form",
          manifestValue: {
            id: matchedForm.generated_document_id,
            displayName: templateItem.display_name,
            requirementName: matchedForm.requirement_name,
            caseFormId: matchedForm.case_form_id,
            partyType: configPartyType,
            packetTemplateItemId: templateItem.id,
            sortOrder: templateItem.sort_order,
          },
        });
        continue;
      }

      const matchedDocument = caseRequiredDocuments.find(
        (item) =>
          Boolean(item.document_record_id) &&
          ((config?.workflowRequiredDocumentId &&
            item.workflow_required_document_id === config.workflowRequiredDocumentId) ||
            item.document_type_code === templateItem.document_type_code),
      );

      if (!matchedDocument?.document_record_id) {
        continue;
      }

      orderedPacketItems.push({
        packetTemplateItemId: templateItem.id,
        sortOrder: templateItem.sort_order,
        sourceEntityType: "document_record",
        sourceEntityId: matchedDocument.document_record_id,
        generatedDocumentId: null,
        notes: matchedDocument.document_title || matchedDocument.requirement_name,
        kind: "document",
        manifestValue: {
          id: matchedDocument.document_record_id,
          displayName: templateItem.display_name,
          requirementName: matchedDocument.requirement_name,
          title: matchedDocument.document_title,
          partyType: configPartyType,
          packetTemplateItemId: templateItem.id,
          sortOrder: templateItem.sort_order,
        },
      });
    }

    const packetAssemblyItems =
      orderedPacketItems.length > 0
        ? orderedPacketItems
        : [
            ...generatedForms.map((formDoc, index) => ({
              packetTemplateItemId: null,
              sortOrder: index + 1,
              sourceEntityType: "generated_document" as const,
              sourceEntityId: formDoc.id,
              generatedDocumentId: formDoc.id,
              notes: null,
              kind: "form" as const,
              manifestValue: {
                id: formDoc.id,
                documentKind: formDoc.document_kind,
                sourceEntityId: formDoc.source_entity_id,
                fileId: formDoc.file_id,
                sortOrder: index + 1,
              },
            })),
            ...supportingDocuments.map((documentRecord, index) => ({
              packetTemplateItemId: null,
              sortOrder: generatedForms.length + index + 1,
              sourceEntityType: "document_record" as const,
              sourceEntityId: documentRecord.id,
              generatedDocumentId: null,
              notes: documentRecord.title,
              kind: "document" as const,
              manifestValue: {
                id: documentRecord.id,
                title: documentRecord.title,
                fileId: documentRecord.file_id,
                sortOrder: generatedForms.length + index + 1,
              },
            })),
          ];

    const manifest = {
      caseId: input.caseId,
      generatedAt: new Date().toISOString(),
      packetTemplateId,
      workflowInstanceId,
      workflowTemplateId,
      workflowName,
      orderedItems: packetAssemblyItems.map((item) => ({
        ...item.manifestValue,
        itemType: item.kind,
      })),
      forms: packetAssemblyItems
        .filter((item) => item.kind === "form")
        .map((item) => item.manifestValue),
      supportingDocuments: packetAssemblyItems
        .filter((item) => item.kind === "document")
        .map((item) => item.manifestValue),
    };

    const generatedDocumentIds = Array.from(
      new Set(
        packetAssemblyItems
          .filter((item) => item.sourceEntityType === "generated_document")
          .map((item) => item.sourceEntityId),
      ),
    );
    const generatedDocumentAssets = generatedDocumentIds.length
      ? await prisma.$queryRaw<
          Array<{
            generated_document_id: string;
            file_id: string;
            storage_provider: string;
            object_key: string;
            mime_type: string;
            original_file_name: string;
          }>
        >`
          SELECT
            gd.id AS generated_document_id,
            f.id AS file_id,
            f.storage_provider,
            f.object_key,
            f.mime_type,
            f.original_file_name
          FROM generated_documents gd
          JOIN files f ON f.id = gd.file_id
          WHERE gd.id IN (${Prisma.join(generatedDocumentIds)})
        `
      : [];
    const generatedDocumentAssetMap = new Map(
      generatedDocumentAssets.map((item) => [item.generated_document_id, item]),
    );

    const documentRecordIds = Array.from(
      new Set(
        packetAssemblyItems
          .filter((item) => item.sourceEntityType === "document_record")
          .map((item) => item.sourceEntityId),
      ),
    );
    const documentRecordAssets = documentRecordIds.length
      ? await prisma.$queryRaw<
          Array<{
            document_record_id: string;
            title: string;
            extracted_text: string | null;
            file_id: string;
            storage_provider: string;
            object_key: string;
            mime_type: string;
            original_file_name: string;
          }>
        >`
          SELECT
            dr.id AS document_record_id,
            dr.title,
            dr.extracted_text,
            f.id AS file_id,
            f.storage_provider,
            f.object_key,
            f.mime_type,
            f.original_file_name
          FROM document_records dr
          JOIN files f ON f.id = dr.file_id
          WHERE dr.id IN (${Prisma.join(documentRecordIds)})
        `
      : [];
    const documentRecordAssetMap = new Map(
      documentRecordAssets.map((item) => [item.document_record_id, item]),
    );

    const packetPdfItems: Array<{
      kind: "form" | "document";
      title: string;
      mimeType: string;
      originalFileName: string;
      bytes: Buffer;
      fallbackText?: string | null;
    }> = [];

    for (const packetItem of packetAssemblyItems) {
      if (packetItem.sourceEntityType === "generated_document") {
        const asset = generatedDocumentAssetMap.get(packetItem.sourceEntityId);
        if (!asset) {
          continue;
        }

        let bytes: Buffer;
        let mimeType = asset.mime_type;
        let originalFileName = asset.original_file_name;

        try {
          bytes = await readBinaryFile({
            storageProvider: asset.storage_provider,
            objectKey: asset.object_key,
          });
        } catch {
          bytes = Buffer.from(packetItem.notes ?? packetItem.sourceEntityId, "utf8");
          mimeType = "text/plain";
          originalFileName = `${packetItem.sourceEntityId}.txt`;
        }

        packetPdfItems.push({
          kind: packetItem.kind,
          title: packetItem.notes ?? "Formulario",
          mimeType,
          originalFileName,
          bytes,
          fallbackText: packetItem.notes,
        });
        continue;
      }

      const asset = documentRecordAssetMap.get(packetItem.sourceEntityId);
      if (!asset) {
        continue;
      }

      let bytes: Buffer;
      let mimeType = asset.mime_type;
      let originalFileName = asset.original_file_name;

      try {
        bytes = await readBinaryFile({
          storageProvider: asset.storage_provider,
          objectKey: asset.object_key,
        });
      } catch {
        bytes = Buffer.from(asset.extracted_text ?? asset.title, "utf8");
        mimeType = "text/plain";
        originalFileName = `${asset.title.replace(/\s+/g, "_")}.txt`;
      }

      packetPdfItems.push({
        kind: packetItem.kind,
        title: asset.title,
        mimeType,
        originalFileName,
        bytes,
        fallbackText: asset.extracted_text,
      });
    }

    const renderedPacket = await buildPacketPdf({
      caseTitle: caseRow.title,
      clientName: caseRow.client_name,
      workflowName,
      items: packetPdfItems,
    });
    const fileName = `processo_${input.caseId}.pdf`;
    const fileBuffer = renderedPacket.bytes;
    const stored = await saveBinaryFile({
      lawFirmId: input.lawFirmId,
      caseId: input.caseId,
      fileName,
      bytes: fileBuffer,
      kind: "generated",
    });

    const repositoryItemId = await createRepositoryItem({
      lawFirmId: input.lawFirmId,
      clientId: caseRow.client_id,
      caseId: input.caseId,
      itemTypeCode: "final_packet",
      channelCode: "system",
      sourceEntityType: "case_packet",
      sourceEntityId: null,
      subject: `Final packet for case ${input.caseId}`,
      bodyText: JSON.stringify(manifest),
      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},
        ${caseRow.client_id},
        ${input.caseId},
        ${repositoryItemId},
        'local_dev',
        'workspace',
        ${stored.relativeObjectKey},
        'local',
        ${fileName},
        ${stored.storedFileName},
        'application/pdf',
        ${fileBuffer.length},
        ${stored.checksumSha256},
        0,
        ${input.actorUserId},
        NOW(),
        CURRENT_TIMESTAMP
      )
    `;

    const generatedDocumentId = createId();
    await prisma.$executeRaw`
      INSERT INTO generated_documents (
        id, law_firm_id, client_id, case_id, repository_item_id, file_id, document_kind,
        source_entity_type, source_entity_id, ai_run_id, version_number, created_at
      ) VALUES (
        ${generatedDocumentId},
        ${input.lawFirmId},
        ${caseRow.client_id},
        ${input.caseId},
        ${repositoryItemId},
        ${fileId},
        'packet',
        'case_packet',
        NULL,
        ${aiRun.id},
        1,
        CURRENT_TIMESTAMP
      )
    `;

    const casePacketId = createId();
    await prisma.$executeRaw`
      INSERT INTO case_packets (
        id, law_firm_id, case_id, packet_template_id, generated_document_id, status_code,
        assembled_by_user_id, ai_run_id, started_at, completed_at, created_at, updated_at
      ) VALUES (
        ${casePacketId},
        ${input.lawFirmId},
        ${input.caseId},
        ${packetTemplateId},
        ${generatedDocumentId},
        'completed',
        ${input.actorUserId},
        ${aiRun.id},
        NOW(),
        NOW(),
        CURRENT_TIMESTAMP,
        CURRENT_TIMESTAMP
      )
    `;

    for (const packetItem of packetAssemblyItems) {
      await prisma.$executeRaw`
        INSERT INTO case_packet_items (
          id, case_packet_id, packet_template_item_id, sort_order, source_entity_type, source_entity_id,
          generated_document_id, status_code, notes, created_at, updated_at
        ) VALUES (
          ${createId()},
          ${casePacketId},
          ${packetItem.packetTemplateItemId},
          ${packetItem.sortOrder},
          ${packetItem.sourceEntityType},
          ${packetItem.sourceEntityId},
          ${packetItem.generatedDocumentId},
          'included',
          ${packetItem.notes},
          CURRENT_TIMESTAMP,
          CURRENT_TIMESTAMP
        )
      `;
    }

    await finishAiRun({
      aiRunId: aiRun.id,
      status: "completed",
      inputTokens: 0,
      outputTokens: packetAssemblyItems.length * 10,
      estimatedCost: 0,
    });

    return {
      casePacketId,
      generatedDocumentId,
      repositoryItemId,
      fileId,
      fileName,
      itemsCount: packetAssemblyItems.length,
      pageCount: renderedPacket.pageCount,
      autoLinkedDocumentsCount: autoLinkedDocuments.linkedCount,
    };
  } catch (error) {
    await finishAiRun({
      aiRunId: aiRun.id,
      status: "failed",
      errorMessage: error instanceof Error ? error.message : "Unexpected error",
    });
    throw error;
  }
}

export async function generateCaseProcess(input: {
  lawFirmId: string;
  clientId: string;
  caseId: string;
  workflowTemplateId: string;
  actorUserId: string;
}) {
  const [caseRow] = await prisma.$queryRaw<
    Array<{
      id: string;
      client_id: string;
    }>
  >`
    SELECT id, client_id
    FROM cases
    WHERE id = ${input.caseId}
      AND law_firm_id = ${input.lawFirmId}
      AND deleted_at IS NULL
    LIMIT 1
  `;

  if (!caseRow) {
    throw new Error("Case not found");
  }

  if (caseRow.client_id !== input.clientId) {
    throw new Error("The selected case does not belong to this client");
  }

  const consolidation = await consolidateCaseFacts({
    lawFirmId: input.lawFirmId,
    caseId: input.caseId,
    actorUserId: input.actorUserId,
  });
  const activation = await ensureWorkflowInstanceForCase({
    lawFirmId: input.lawFirmId,
    caseId: input.caseId,
    clientId: input.clientId,
    workflowTemplateId: input.workflowTemplateId,
    actorUserId: input.actorUserId,
  });
  const forms = await fillCaseForms({
    lawFirmId: input.lawFirmId,
    caseId: input.caseId,
    actorUserId: input.actorUserId,
    workflowInstanceId: activation.workflowInstanceId,
  });
  const packet = await assembleCasePacket({
    lawFirmId: input.lawFirmId,
    caseId: input.caseId,
    actorUserId: input.actorUserId,
    workflowInstanceId: activation.workflowInstanceId,
    workflowTemplateId: input.workflowTemplateId,
  });

  return {
    clientId: caseRow.client_id,
    caseId: input.caseId,
    workflowTemplateId: input.workflowTemplateId,
    workflowInstanceId: activation.workflowInstanceId,
    activatedWorkflow: activation.activated,
    createdSteps: activation.createdSteps,
    createdRequiredDocuments: activation.createdRequiredDocuments,
    createdCaseForms: activation.createdCaseForms,
    factsCount: consolidation.facts.length,
    generatedFormsCount: forms.length,
    packet,
  };
}

export async function createReview(input: {
  lawFirmId: string;
  caseId?: string | null;
  entityType: string;
  entityId?: string | null;
  reviewType: string;
  reviewerUserId: string;
  notes?: string | null;
  aiRunId?: string | null;
}) {
  const id = createId();
  await prisma.$executeRaw`
    INSERT INTO reviews (
      id, law_firm_id, case_id, entity_type, entity_id, review_type, review_status,
      reviewer_user_id, ai_run_id, notes, created_at, completed_at
    ) VALUES (
      ${id},
      ${input.lawFirmId},
      ${input.caseId ?? null},
      ${input.entityType},
      ${input.entityId ?? null},
      ${input.reviewType},
      'completed',
      ${input.reviewerUserId},
      ${input.aiRunId ?? null},
      ${input.notes ?? null},
      CURRENT_TIMESTAMP,
      NOW()
    )
  `;

  return id;
}

export async function createDocumentAndExtraction(input: {
  lawFirmId: string;
  clientId: string;
  caseId?: string | null;
  actorUserId: string | null;
  title: string;
  documentTypeCode: string;
  documentTypeMode?: "force" | "auto";
  originalFileName: string;
  mimeType: string;
  fileBuffer: Buffer;
  textContent?: string | null;
}) {
  const documentTypeResolution =
    input.documentTypeMode === "auto" ?
      await classifyDocumentType({
        lawFirmId: input.lawFirmId,
        clientId: input.clientId,
        caseId: input.caseId ?? null,
        title: input.title,
        originalFileName: input.originalFileName,
        mimeType: input.mimeType,
        textContent: input.textContent ?? null,
        fallbackDocumentTypeCode: input.documentTypeCode,
      }) :
      {
        documentTypeCode: String(input.documentTypeCode ?? "").trim() || "other_supporting",
        classificationSource: "forced" as const,
        classificationConfidence: null,
        classificationReason: "Used the explicitly selected document type",
      };

  const resolvedDocumentTypeCode = documentTypeResolution.documentTypeCode;

  const stored = await saveBinaryFile({
    lawFirmId: input.lawFirmId,
    caseId: input.caseId ?? null,
    fileName: input.originalFileName,
    bytes: input.fileBuffer,
    kind: "uploads",
  });

  const repositoryItemId = await createRepositoryItem({
    lawFirmId: input.lawFirmId,
    clientId: input.clientId,
    caseId: input.caseId ?? null,
    itemTypeCode: "document",
    channelCode: "portal",
    subject: input.title,
    bodyText: input.textContent ?? null,
    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},
      ${input.clientId},
      ${input.caseId ?? null},
      ${repositoryItemId},
      'local_dev',
      'workspace',
      ${stored.relativeObjectKey},
      'local',
      ${input.originalFileName},
      ${stored.storedFileName},
      ${input.mimeType},
      ${input.fileBuffer.length},
      ${stored.checksumSha256},
      0,
      ${input.actorUserId},
      NOW(),
      CURRENT_TIMESTAMP
    )
  `;

  const documentRecordId = createId();
  await prisma.$executeRaw`
    INSERT INTO document_records (
      id, law_firm_id, client_id, case_id, file_id, document_type_code, title,
      document_status, issuing_country_code, issue_date, expiration_date, document_language,
      page_count, extracted_text, created_by_user_id, created_at, updated_at
    ) VALUES (
      ${documentRecordId},
      ${input.lawFirmId},
      ${input.clientId},
      ${input.caseId ?? null},
      ${fileId},
      ${resolvedDocumentTypeCode},
      ${input.title},
      'received',
      NULL,
      NULL,
      NULL,
      'en',
      1,
      ${input.textContent ?? null},
      ${input.actorUserId},
      CURRENT_TIMESTAMP,
      CURRENT_TIMESTAMP
    )
  `;

  if (input.caseId) {
    const [requiredDocument] = await prisma.$queryRaw<Array<{ id: string }>>`
      SELECT id
      FROM case_required_documents
      WHERE case_id = ${input.caseId}
        AND law_firm_id = ${input.lawFirmId}
        AND document_type_code = ${resolvedDocumentTypeCode}
        AND status_code = 'pending'
      ORDER BY created_at ASC
      LIMIT 1
    `;

    if (requiredDocument) {
      await prisma.$executeRaw`
        UPDATE case_required_documents
        SET status_code = 'received',
            satisfied_by_document_record_id = ${documentRecordId},
            updated_at = CURRENT_TIMESTAMP
        WHERE id = ${requiredDocument.id}
      `;
    }
  }

  let extraction: { aiRunId: string; extractionId: string; extractedData: Record<string, string> } | null =
    null;
  let syncedClientFieldKeys: string[] = [];

  try {
    if (input.textContent) {
      extraction = await generateDocumentExtraction({
        lawFirmId: input.lawFirmId,
        clientId: input.clientId,
        caseId: input.caseId ?? null,
        documentRecordId,
        repositoryItemId,
        title: input.title,
        documentTypeCode: resolvedDocumentTypeCode,
        originalFileName: input.originalFileName,
        mimeType: input.mimeType,
        textContent: input.textContent,
      });

      syncedClientFieldKeys = await syncClientStructuredDataFromExtraction({
        clientId: input.clientId,
        extractedData: extraction.extractedData,
      });
    }
  } catch {
    extraction = null;
    syncedClientFieldKeys = [];
  }

  return {
    repositoryItemId,
    fileId,
    documentRecordId,
    documentTypeCode: resolvedDocumentTypeCode,
    classificationSource: documentTypeResolution.classificationSource,
    classificationConfidence: documentTypeResolution.classificationConfidence,
    classificationReason: documentTypeResolution.classificationReason,
    extraction,
    syncedClientFieldKeys,
  };
}

export async function reprocessExistingDocumentWithAi(input: {
  lawFirmId: string;
  clientId: string;
  caseId?: string | null;
  actorUserId: string | null;
  documentRecordId: string;
}) {
  const [documentRow] = await prisma.$queryRaw<
    Array<{
      id: string;
      client_id: string;
      case_id: string | null;
      file_id: string;
      repository_item_id: string | null;
      title: string;
      document_type_code: string;
      extracted_text: string | null;
      storage_provider: string;
      object_key: string;
      original_file_name: string;
      mime_type: string;
    }>
  >`
    SELECT
      dr.id,
      dr.client_id,
      dr.case_id,
      dr.file_id,
      f.repository_item_id,
      dr.title,
      dr.document_type_code,
      dr.extracted_text,
      f.storage_provider,
      f.object_key,
      f.original_file_name,
      f.mime_type
    FROM document_records dr
    JOIN files f ON f.id = dr.file_id
    WHERE dr.id = ${input.documentRecordId}
      AND dr.law_firm_id = ${input.lawFirmId}
      AND dr.client_id = ${input.clientId}
    LIMIT 1
  `;

  if (!documentRow) {
    throw new Error("Document not found in this client repository");
  }

  const existingText = String(documentRow.extracted_text ?? "").trim();
  let textContent = existingText;

  if (!textContent) {
    const bytes = await readBinaryFile({
      storageProvider: documentRow.storage_provider,
      objectKey: documentRow.object_key,
    });

    textContent = await extractDocumentText({
      fileName: documentRow.original_file_name,
      mimeType: documentRow.mime_type,
      bytes,
    });
  }

  await prisma.$executeRaw`
    UPDATE document_records
    SET extracted_text = ${textContent || null},
        updated_at = CURRENT_TIMESTAMP
    WHERE id = ${documentRow.id}
  `;

  const extraction = textContent
    ? await generateDetailedDocumentExtractionWithAi({
        lawFirmId: input.lawFirmId,
        clientId: input.clientId,
        caseId: input.caseId ?? documentRow.case_id ?? null,
        documentRecordId: documentRow.id,
        repositoryItemId: documentRow.repository_item_id,
        title: documentRow.title,
        documentTypeCode: documentRow.document_type_code,
        originalFileName: documentRow.original_file_name,
        mimeType: documentRow.mime_type,
        textContent,
      })
    : {
        aiRunId: null,
        extractionId: null,
        extractedData: {} as Record<string, string>,
        summaryText: "No readable text was extracted from this document during reprocessing.",
        importantFacts: [] as DetailedDocumentImportantFact[],
      };

  const syncedClientFieldKeys = await syncClientStructuredDataFromExtraction({
    clientId: input.clientId,
    extractedData: extraction.extractedData,
  });

  const dataFields = await getDataFields();
  const fieldLabelByKey = new Map(dataFields.map((field) => [field.field_key, field.label]));
  const extractedDataEntries = Object.entries(extraction.extractedData).map(([fieldKey, value]) => ({
    label: fieldLabelByKey.get(fieldKey) ?? fieldKey,
    value,
  }));

  if (documentRow.repository_item_id) {
    await prisma.$executeRaw`
      UPDATE repository_items
      SET
        body_text = ${buildRepositoryDocumentKnowledgeBody({
          summaryText: extraction.summaryText,
          extractedData: extractedDataEntries,
          importantFacts: extraction.importantFacts,
          rawText: textContent,
        })},
        summary_text = ${extraction.summaryText || null},
        updated_at = CURRENT_TIMESTAMP
      WHERE id = ${documentRow.repository_item_id}
    `;
  }

  return {
    documentRecordId: documentRow.id,
    repositoryItemId: documentRow.repository_item_id,
    fileId: documentRow.file_id,
    title: documentRow.title,
    documentTypeCode: documentRow.document_type_code,
    textLength: textContent.length,
    aiRunId: extraction.aiRunId,
    extractionId: extraction.extractionId,
    extractedData: extraction.extractedData,
    extractedPreview: extractedDataEntries,
    syncedClientFieldKeys,
    summaryText: extraction.summaryText,
    importantFacts: extraction.importantFacts,
  } satisfies RepositoryDocumentAiReprocessResult;
}
