import type { FastifyInstance } from "fastify";
import type { MultipartFile } from "@fastify/multipart";
import { Prisma } from "@prisma/client";
import { z } from "zod";
import { requireSession } from "../../lib/auth.js";
import { writeAuditLog } from "../../lib/audit.js";
import {
  analyzeImportedFormTemplate,
  buildFormFieldInstructions,
  mergeFormFieldQuestionConfig,
  parseFormFieldQuestionConfig,
  suggestFormFieldConfigurationValue,
} from "../../lib/form-template-analysis.js";
import { createId } from "../../lib/id.js";
import { isImageKitConfigured, uploadBinaryFileToImageKit } from "../../lib/imagekit.js";
import { prisma } from "../../lib/prisma.js";
import { createRepositoryItem } from "../../lib/repository.js";
import { getSessionProfile } from "../../lib/session.js";
import { createAiRun, finishAiRun } from "../../lib/tenant-ai.js";
import {
  createSpecializedFormGenerationJob,
  getSpecializedFormGenerationJobDetail,
  pauseSpecializedFormGenerationJob,
  resumePendingSpecializedFormGenerationJobs,
  resumeSpecializedFormGenerationJob,
  startSpecializedFormGenerationJob,
} from "../../lib/specialized-form-generation-jobs.js";
import {
  generateSpecializedFormForClient,
  listSpecializedFormFieldGuidance,
  listSpecializedForms,
  updateSpecializedFormFieldGuidance,
  updateSpecializedFormPrompt,
} from "../../lib/specialized-forms.js";
import { saveBinaryFile } from "../../lib/storage.js";

const formTemplateSchema = z.object({
  code: z.string().min(2).max(50),
  name: z.string().min(2).max(255),
  versionLabel: z.string().min(1).max(50).default("1"),
  description: z.string().max(1000).optional().or(z.literal("")),
  basePdfFileId: z.string().uuid().optional().nullable(),
});

const formFieldSchema = z.object({
  fieldKey: z.string().min(2).max(100),
  label: z.string().min(2).max(255),
  pdfFieldName: z.string().min(2).max(255),
  dataType: z.string().min(2).max(50),
  isRequired: z.boolean().default(true),
  sectionName: z.string().max(100).optional().or(z.literal("")),
});

const formFieldConfigurationSchema = z.object({
  questionText: z.string().min(3).max(500),
  answerMode: z.enum(["default_value", "source_hint", "ask_user", "leave_blank"]),
  defaultValue: z.string().max(1000).optional().or(z.literal("")),
  sourceHint: z.string().max(2000).optional().or(z.literal("")),
  additionalInstructions: z.string().max(4000).optional().or(z.literal("")),
  clientDataStructureNodeId: z.string().max(64).optional().or(z.literal("")),
  clientDataStructureFieldKey: z.string().max(100).optional().or(z.literal("")),
  clientDataStructurePath: z.string().max(1000).optional().or(z.literal("")),
});

const formFieldAiSuggestionSchema = z.object({
  target: z.enum(["source_hint", "additional_instructions"]),
});

const specializedFormPromptSchema = z.object({
  prompt: z.string().max(50000),
});

const specializedFormFieldGuidanceSchema = z.object({
  instructions: z.string().max(4000),
});

const specializedFormGenerationSchema = z.object({
  clientId: z.string().uuid(),
});

function getMultipartFieldValue(
  fields: Record<string, MultipartFile["fields"][string]>,
  key: string,
) {
  const field = fields[key];

  if (!field || Array.isArray(field) || !("value" in field)) {
    return "";
  }

  return String(field.value ?? "");
}

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

function normalizeTemplateCode(value: string) {
  return compactWhitespace(value)
    .replace(/\.[^.]+$/, "")
    .replace(/[^a-z0-9]+/gi, "_")
    .replace(/_+/g, "_")
    .replace(/^_+|_+$/g, "")
    .toUpperCase()
    .slice(0, 50);
}

function humanizeFileName(value: string) {
  const normalized = compactWhitespace(value)
    .replace(/\.[^.]+$/, "")
    .replace(/[_-]+/g, " ");

  if (!normalized) {
    return "Uploaded form";
  }

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

async function buildUniqueTemplateCode(input: {
  lawFirmId: string;
  requestedCode: string;
  versionLabel: string;
}) {
  const baseCode = normalizeTemplateCode(input.requestedCode) || "FORM";
  let suffix = 1;
  let nextCode = baseCode;

  while (true) {
    const [existing] = await prisma.$queryRaw<Array<{ id: string }>>`
      SELECT id
      FROM form_templates
      WHERE (law_firm_id = ${input.lawFirmId} OR law_firm_id IS NULL)
        AND code = ${nextCode}
        AND version_label = ${input.versionLabel}
      LIMIT 1
    `;

    if (!existing) {
      return nextCode;
    }

    suffix += 1;
    nextCode = `${baseCode.slice(0, Math.max(1, 46 - String(suffix).length))}_${suffix}`;
  }
}

async function storeUploadedFormTemplateAsset(input: {
  lawFirmId: string;
  actorUserId: string;
  file: MultipartFile;
  title: string;
  folder?: string | null;
}) {
  const buffer = await input.file.toBuffer();
  const stored = isImageKitConfigured()
    ? await uploadBinaryFileToImageKit({
        bytes: buffer,
        fileName: input.file.filename,
        mimeType: input.file.mimetype,
        folder: input.folder || undefined,
      })
    : await (async () => {
        const localFile = await saveBinaryFile({
          lawFirmId: input.lawFirmId,
          caseId: null,
          fileName: input.file.filename,
          bytes: buffer,
          kind: "uploads",
        });

        return {
          storageProvider: "local_dev",
          storageBucket: "workspace",
          objectKey: localFile.relativeObjectKey,
          storageRegion: "local",
          storedFileName: localFile.storedFileName,
          checksumSha256: localFile.checksumSha256,
          uploadUrl: null,
        };
      })();

  const repositoryItemId = await createRepositoryItem({
    lawFirmId: input.lawFirmId,
    itemTypeCode: "document",
    subject: input.title,
    createdByUserId: input.actorUserId,
  });

  const fileId = createId();
  await prisma.$executeRaw`
    INSERT INTO files (
      id, law_firm_id, client_id, case_id, repository_item_id, storage_provider, storage_bucket,
      object_key, storage_region, original_file_name, stored_file_name, mime_type, size_bytes,
      checksum_sha256, is_encrypted, uploaded_by_user_id, uploaded_at, created_at
    ) VALUES (
      ${fileId},
      ${input.lawFirmId},
      NULL,
      NULL,
      ${repositoryItemId},
      ${stored.storageProvider},
      ${stored.storageBucket},
      ${stored.objectKey},
      ${stored.storageRegion},
      ${input.file.filename},
      ${stored.storedFileName},
      ${input.file.mimetype},
      ${buffer.length},
      ${stored.checksumSha256},
      0,
      ${input.actorUserId},
      NOW(),
      CURRENT_TIMESTAMP
    )
  `;

  return {
    buffer,
    fileId,
    repositoryItemId,
    originalFileName: input.file.filename,
    storedFileName: stored.storedFileName,
    storageProvider: stored.storageProvider,
    uploadUrl: stored.uploadUrl,
  };
}

export async function registerFormRoutes(app: FastifyInstance) {
  app.get("/specialized", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    return listSpecializedForms({
      lawFirmId: profile.lawFirm.id,
      actorUserId: profile.user.id,
    });
  });

  app.patch("/specialized/:agentCode/prompt", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);
    const payload = specializedFormPromptSchema.parse(request.body);
    const { agentCode } = request.params as { agentCode: string };

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    const summary = await updateSpecializedFormPrompt({
      lawFirmId: profile.lawFirm.id,
      actorUserId: profile.user.id,
      agentCode,
      prompt: payload.prompt,
    });

    return summary;
  });

  app.get("/specialized/:agentCode/fields", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);
    const { agentCode } = request.params as { agentCode: string };

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    return listSpecializedFormFieldGuidance({
      lawFirmId: profile.lawFirm.id,
      actorUserId: profile.user.id,
      agentCode,
    });
  });

  app.patch("/specialized/:agentCode/fields/:fieldId", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);
    const payload = specializedFormFieldGuidanceSchema.parse(request.body);
    const { agentCode, fieldId } = request.params as { agentCode: string; fieldId: string };

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    const field = await updateSpecializedFormFieldGuidance({
      lawFirmId: profile.lawFirm.id,
      actorUserId: profile.user.id,
      agentCode,
      fieldId,
      instructions: payload.instructions,
    });

    if (!field) {
      throw reply.notFound("Specialized form field not found");
    }

    return field;
  });

  app.post("/specialized/:agentCode/generate", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);
    const payload = specializedFormGenerationSchema.parse(request.body);
    const { agentCode } = request.params as { agentCode: string };

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    return generateSpecializedFormForClient({
      lawFirmId: profile.lawFirm.id,
      actorUserId: profile.user.id,
      clientId: payload.clientId,
      agentCode,
    });
  });

  app.post("/specialized/:agentCode/generate-jobs", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);
    const payload = specializedFormGenerationSchema.parse(request.body);
    const { agentCode } = request.params as { agentCode: string };

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    const jobId = await createSpecializedFormGenerationJob({
      lawFirmId: profile.lawFirm.id,
      actorUserId: profile.user.id,
      clientId: payload.clientId,
      agentCode,
    });

    await startSpecializedFormGenerationJob({
      jobId,
      logger: app.log,
    });

    const detail = await getSpecializedFormGenerationJobDetail({
      lawFirmId: profile.lawFirm.id,
      jobId,
      logger: app.log,
    });

    if (!detail || detail.agentCode !== agentCode) {
      throw reply.notFound("Specialized form generation job not found");
    }

    return detail;
  });

  app.get("/specialized/:agentCode/generate-jobs/:jobId", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);
    const { agentCode, jobId } = request.params as { agentCode: string; jobId: string };

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    const detail = await getSpecializedFormGenerationJobDetail({
      lawFirmId: profile.lawFirm.id,
      jobId,
      logger: app.log,
    });

    if (!detail || detail.agentCode !== agentCode) {
      throw reply.notFound("Specialized form generation job not found");
    }

    return detail;
  });

  app.post("/specialized/:agentCode/generate-jobs/:jobId/pause", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);
    const { agentCode, jobId } = request.params as { agentCode: string; jobId: string };

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    const detail = await pauseSpecializedFormGenerationJob({
      lawFirmId: profile.lawFirm.id,
      jobId,
    });

    if (!detail || detail.agentCode !== agentCode) {
      throw reply.notFound("Specialized form generation job not found");
    }

    return detail;
  });

  app.post("/specialized/:agentCode/generate-jobs/:jobId/resume", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);
    const { agentCode, jobId } = request.params as { agentCode: string; jobId: string };

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    const detail = await resumeSpecializedFormGenerationJob({
      lawFirmId: profile.lawFirm.id,
      jobId,
      logger: app.log,
    });

    if (!detail || detail.agentCode !== agentCode) {
      throw reply.notFound("Specialized form generation job not found");
    }

    return detail;
  });

  app.get("/templates", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    const templates = await prisma.$queryRaw<
      Array<{
        id: string;
        law_firm_id: string | null;
        code: string;
        name: string;
        version_label: string;
        base_pdf_file_id: string | null;
        base_pdf_original_file_name: string | null;
        base_pdf_storage_provider: string | null;
        description: string | null;
        is_system_template: number;
      }>
    >`
      SELECT
        ft.id,
        ft.law_firm_id,
        ft.code,
        ft.name,
        ft.version_label,
        ft.base_pdf_file_id,
        f.original_file_name AS base_pdf_original_file_name,
        f.storage_provider AS base_pdf_storage_provider,
        ft.description,
        ft.is_system_template
      FROM form_templates ft
      LEFT JOIN files f ON f.id = ft.base_pdf_file_id
      WHERE ft.is_active = 1
        AND (
          ft.law_firm_id = ${profile.lawFirm.id}
          OR (
            ft.law_firm_id IS NULL
            AND NOT EXISTS (
              SELECT 1
              FROM form_templates workspace_ft
              WHERE workspace_ft.law_firm_id = ${profile.lawFirm.id}
                AND workspace_ft.code = ft.code
                AND workspace_ft.version_label = ft.version_label
            )
          )
        )
      ORDER BY is_system_template DESC, name ASC
    `;

    const fields = templates.length
      ? 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;
            page_number: number | null;
            instructions: string | null;
            condition_expression_json: unknown;
          }>
        >`
          SELECT
            id,
            form_template_id,
            field_key,
            label,
            pdf_field_name,
            data_type,
            is_required,
            section_name,
            page_number,
            instructions,
            condition_expression_json
          FROM form_fields
          WHERE form_template_id IN (${Prisma.join(templates.map((template) => template.id))})
          ORDER BY page_number ASC, created_at ASC
        `
      : [];

    return templates.map((template) => ({
      id: template.id,
      code: template.code,
      name: template.name,
      versionLabel: template.version_label,
      basePdfFileId: template.base_pdf_file_id,
      basePdfOriginalFileName: template.base_pdf_original_file_name,
      basePdfStorageProvider: template.base_pdf_storage_provider,
      description: template.description,
      isSystemTemplate: Boolean(template.is_system_template),
      fields: fields
        .filter((field) => field.form_template_id === template.id)
        .map((field) => {
          const questionConfig = parseFormFieldQuestionConfig({
            rawMetadata: field.condition_expression_json,
            fallbackLabel: field.label,
          });

          return {
            questionConfig,
            id: field.id,
            fieldKey: field.field_key,
            label: field.label,
            pdfFieldName: field.pdf_field_name,
            dataType: field.data_type,
            isRequired: Boolean(field.is_required),
            sectionName: field.section_name,
            pageNumber: field.page_number,
            instructions:
              field.instructions ||
              buildFormFieldInstructions({
                label: field.label,
                dataType: field.data_type,
                sectionName: field.section_name,
                questionConfig,
              }),
          };
        }),
    }));
  });

  app.post("/templates", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);
    const payload = formTemplateSchema.parse(request.body);

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    const id = createId();
    await prisma.$executeRaw`
      INSERT INTO form_templates (
        id, law_firm_id, code, name, version_label, base_pdf_file_id, description, is_active,
        is_system_template, created_at, updated_at
      ) VALUES (
        ${id},
        ${profile.lawFirm.id},
        ${payload.code},
        ${payload.name},
        ${payload.versionLabel},
        ${payload.basePdfFileId ?? null},
        ${payload.description || null},
        1,
        0,
        CURRENT_TIMESTAMP,
        CURRENT_TIMESTAMP
      )
    `;

    await writeAuditLog({
      lawFirmId: profile.lawFirm.id,
      officeId: profile.user.primaryOfficeId ?? null,
      actorUserId: profile.user.id,
      entityType: "form_template",
      entityId: id,
      action: "form_template.create",
      afterJson: payload,
      request,
    });

    return reply.code(201).send({ id });
  });

  app.post("/templates/import", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    const multipartRequest = request as typeof request & {
      file: () => Promise<MultipartFile | undefined>;
    };
    const file = await multipartRequest.file();

    if (!file) {
      throw reply.badRequest("A file upload is required");
    }

    if (file.mimetype !== "application/pdf" && !file.filename.toLowerCase().endsWith(".pdf")) {
      throw reply.badRequest("Only PDF uploads are supported for form templates");
    }

    const rawName = compactWhitespace(getMultipartFieldValue(file.fields, "name"));
    const rawCode = compactWhitespace(getMultipartFieldValue(file.fields, "code"));
    const versionLabel = compactWhitespace(getMultipartFieldValue(file.fields, "versionLabel")) || "1";
    const description = compactWhitespace(getMultipartFieldValue(file.fields, "description"));
    const folder = getMultipartFieldValue(file.fields, "folder");
    const formName = rawName || humanizeFileName(file.filename);
    const templateCode = await buildUniqueTemplateCode({
      lawFirmId: profile.lawFirm.id,
      requestedCode: rawCode || formName || file.filename,
      versionLabel,
    });
    const storedAsset = await storeUploadedFormTemplateAsset({
      lawFirmId: profile.lawFirm.id,
      actorUserId: profile.user.id,
      file,
      title: formName,
      folder,
    });
    const analysis = await analyzeImportedFormTemplate({
      lawFirmId: profile.lawFirm.id,
      fileName: storedAsset.originalFileName,
      formName,
      bytes: storedAsset.buffer,
    });

    const templateId = createId();
    await prisma.$executeRaw`
      INSERT INTO form_templates (
        id, law_firm_id, code, name, version_label, base_pdf_file_id, description, is_active,
        is_system_template, created_at, updated_at
      ) VALUES (
        ${templateId},
        ${profile.lawFirm.id},
        ${templateCode},
        ${formName},
        ${versionLabel},
        ${storedAsset.fileId},
        ${description || null},
        1,
        0,
        CURRENT_TIMESTAMP,
        CURRENT_TIMESTAMP
      )
    `;

    const createdFields: Array<{
      id: string;
      questionConfig: ReturnType<typeof parseFormFieldQuestionConfig>;
      fieldKey: string;
      label: string;
      pdfFieldName: string;
      dataType: string;
      isRequired: boolean;
      sectionName: string | null;
      pageNumber: number | null;
      instructions: string;
    }> = [];

    for (const field of analysis.fields) {
      const fieldId = createId();
      const metadata = mergeFormFieldQuestionConfig({
        rawMetadata: null,
        fallbackLabel: field.label,
        nextConfig: field.questionConfig,
      });

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

      const [dataField] = await prisma.$queryRaw<Array<{ id: string }>>`
        SELECT id FROM data_fields WHERE field_key = ${field.fieldKey} LIMIT 1
      `;

      if (dataField) {
        await prisma.$executeRaw`
          INSERT INTO form_mappings (
            id, form_template_id, form_field_id, data_field_id, mapping_strategy,
            confidence_threshold, is_active, created_at, updated_at
          ) VALUES (
            ${createId()},
            ${templateId},
            ${fieldId},
            ${dataField.id},
            'direct',
            0.8000,
            1,
            CURRENT_TIMESTAMP,
            CURRENT_TIMESTAMP
          )
        `;
      }

      createdFields.push({
        id: fieldId,
        questionConfig: field.questionConfig,
        fieldKey: field.fieldKey,
        label: field.label,
        pdfFieldName: field.pdfFieldName,
        dataType: field.dataType,
        isRequired: field.isRequired,
        sectionName: field.sectionName,
        pageNumber: field.pageNumber,
        instructions: field.instructions,
      });
    }

    await writeAuditLog({
      lawFirmId: profile.lawFirm.id,
      officeId: profile.user.primaryOfficeId ?? null,
      actorUserId: profile.user.id,
      entityType: "form_template",
      entityId: templateId,
      action: "form_template.import",
      afterJson: {
        code: templateCode,
        name: formName,
        versionLabel,
        description: description || null,
        basePdfFileId: storedAsset.fileId,
        importedFieldCount: analysis.fields.length,
      },
      request,
    });

    return reply.code(201).send({
      id: templateId,
      code: templateCode,
      name: formName,
      versionLabel,
      basePdfFileId: storedAsset.fileId,
      basePdfOriginalFileName: storedAsset.originalFileName,
      basePdfStorageProvider: storedAsset.storageProvider,
      description: description || null,
      isSystemTemplate: false,
      fields: createdFields,
    });
  });

  app.post("/template-assets/upload", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    const multipartRequest = request as typeof request & {
      file: () => Promise<MultipartFile | undefined>;
    };
    const file = await multipartRequest.file();

    if (!file) {
      throw reply.badRequest("A file upload is required");
    }

    if (file.mimetype !== "application/pdf" && !file.filename.toLowerCase().endsWith(".pdf")) {
      throw reply.badRequest("Only PDF uploads are supported for form templates");
    }

    const title = getMultipartFieldValue(file.fields, "title") || file.filename;
    const folder = getMultipartFieldValue(file.fields, "folder");
    const buffer = await file.toBuffer();
    const stored = isImageKitConfigured()
      ? await uploadBinaryFileToImageKit({
          bytes: buffer,
          fileName: file.filename,
          mimeType: file.mimetype,
          folder: folder || undefined,
        })
      : await (async () => {
          const localFile = await saveBinaryFile({
            lawFirmId: profile.lawFirm.id,
            caseId: null,
            fileName: file.filename,
            bytes: buffer,
            kind: "uploads",
          });

          return {
            storageProvider: "local_dev",
            storageBucket: "workspace",
            objectKey: localFile.relativeObjectKey,
            storageRegion: "local",
            storedFileName: localFile.storedFileName,
            checksumSha256: localFile.checksumSha256,
            uploadUrl: null,
          };
        })();

    const repositoryItemId = await createRepositoryItem({
      lawFirmId: profile.lawFirm.id,
      itemTypeCode: "document",
      subject: title,
      createdByUserId: profile.user.id,
    });

    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},
        ${profile.lawFirm.id},
        NULL,
        NULL,
        ${repositoryItemId},
        ${stored.storageProvider},
        ${stored.storageBucket},
        ${stored.objectKey},
        ${stored.storageRegion},
        ${file.filename},
        ${stored.storedFileName},
        ${file.mimetype},
        ${buffer.length},
        ${stored.checksumSha256},
        0,
        ${profile.user.id},
        NOW(),
        CURRENT_TIMESTAMP
      )
    `;

    await writeAuditLog({
      lawFirmId: profile.lawFirm.id,
      officeId: profile.user.primaryOfficeId ?? null,
      actorUserId: profile.user.id,
      entityType: "file",
      entityId: fileId,
      action: "form_template_asset.upload",
      afterJson: {
        originalFileName: file.filename,
        storageProvider: stored.storageProvider,
        storageBucket: stored.storageBucket,
      },
      request,
    });

    return reply.code(201).send({
      fileId,
      originalFileName: file.filename,
      storedFileName: stored.storedFileName,
      storageProvider: stored.storageProvider,
      uploadUrl: stored.uploadUrl,
    });
  });

  app.post("/templates/:templateId/fields", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);
    const payload = formFieldSchema.parse(request.body);
    const { templateId } = request.params as { templateId: string };

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    const [template] = await prisma.$queryRaw<Array<{ law_firm_id: string | null }>>`
      SELECT law_firm_id
      FROM form_templates
      WHERE id = ${templateId}
      LIMIT 1
    `;

    if (!template) {
      throw reply.notFound("Form template not found");
    }

    if (template.law_firm_id !== profile.lawFirm.id) {
      throw reply.forbidden("Only workspace-owned form templates can be edited");
    }

    const questionConfig = parseFormFieldQuestionConfig({
      rawMetadata: null,
      fallbackLabel: payload.label,
    });
    const metadata = mergeFormFieldQuestionConfig({
      rawMetadata: null,
      fallbackLabel: payload.label,
      nextConfig: questionConfig,
    });
    const instructions = buildFormFieldInstructions({
      label: payload.label,
      dataType: payload.dataType,
      sectionName: payload.sectionName || null,
      questionConfig,
    });
    const fieldId = createId();
    await prisma.$executeRaw`
      INSERT INTO form_fields (
        id, form_template_id, section_name, page_number, field_key, label, pdf_field_name,
        data_type, is_required, is_repeatable, condition_expression_json, instructions, created_at, updated_at
      ) VALUES (
        ${fieldId},
        ${templateId},
        ${payload.sectionName || null},
        1,
        ${payload.fieldKey},
        ${payload.label},
        ${payload.pdfFieldName},
        ${payload.dataType},
        ${payload.isRequired ? 1 : 0},
        0,
        ${JSON.stringify(metadata)},
        ${instructions},
        CURRENT_TIMESTAMP,
        CURRENT_TIMESTAMP
      )
    `;

    const [dataField] = await prisma.$queryRaw<Array<{ id: string }>>`
      SELECT id FROM data_fields WHERE field_key = ${payload.fieldKey} LIMIT 1
    `;

    if (dataField) {
      await prisma.$executeRaw`
        INSERT INTO form_mappings (
          id, form_template_id, form_field_id, data_field_id, mapping_strategy,
          confidence_threshold, is_active, created_at, updated_at
        ) VALUES (
          ${createId()},
          ${templateId},
          ${fieldId},
          ${dataField.id},
          'direct',
          0.8000,
          1,
          CURRENT_TIMESTAMP,
          CURRENT_TIMESTAMP
        )
      `;
    }

    return reply.code(201).send({ id: fieldId });
  });

  app.patch("/templates/:templateId/fields/:fieldId", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);
    const payload = formFieldConfigurationSchema.parse(request.body);
    const { templateId, fieldId } = request.params as { templateId: string; fieldId: string };

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    const [fieldRow] = await prisma.$queryRaw<
      Array<{
        id: string;
        field_key: string;
        label: string;
        pdf_field_name: string;
        data_type: string;
        is_required: number;
        section_name: string | null;
        page_number: number | null;
        instructions: string | null;
        condition_expression_json: unknown;
        template_law_firm_id: string | null;
      }>
    >`
      SELECT
        ff.id,
        ff.field_key,
        ff.label,
        ff.pdf_field_name,
        ff.data_type,
        ff.is_required,
        ff.section_name,
        ff.page_number,
        ff.instructions,
        ff.condition_expression_json,
        ft.law_firm_id AS template_law_firm_id
      FROM form_fields ff
      JOIN form_templates ft ON ft.id = ff.form_template_id
      WHERE ff.id = ${fieldId}
        AND ff.form_template_id = ${templateId}
      LIMIT 1
    `;

    if (!fieldRow) {
      throw reply.notFound("Form field not found");
    }

    if (fieldRow.template_law_firm_id !== profile.lawFirm.id) {
      throw reply.forbidden("Only workspace-owned form templates can be edited");
    }

    const metadata = mergeFormFieldQuestionConfig({
      rawMetadata: fieldRow.condition_expression_json,
      fallbackLabel: fieldRow.label,
      nextConfig: payload,
    });
    const questionConfig = parseFormFieldQuestionConfig({
      rawMetadata: metadata,
      fallbackLabel: fieldRow.label,
    });
    const instructions = buildFormFieldInstructions({
      label: fieldRow.label,
      dataType: fieldRow.data_type,
      sectionName: fieldRow.section_name,
      questionConfig,
    });

    await prisma.$executeRaw`
      UPDATE form_fields
      SET
        condition_expression_json = ${JSON.stringify(metadata)},
        instructions = ${instructions},
        updated_at = CURRENT_TIMESTAMP
      WHERE id = ${fieldId}
        AND form_template_id = ${templateId}
    `;

    await writeAuditLog({
      lawFirmId: profile.lawFirm.id,
      officeId: profile.user.primaryOfficeId ?? null,
      actorUserId: profile.user.id,
      entityType: "form_field",
      entityId: fieldId,
      action: "form_field.update_configuration",
      afterJson: {
        questionConfig,
        instructions,
      },
      request,
    });

    return {
      id: fieldId,
      templateId,
      fieldKey: fieldRow.field_key,
      label: fieldRow.label,
      pdfFieldName: fieldRow.pdf_field_name,
      dataType: fieldRow.data_type,
      isRequired: Boolean(fieldRow.is_required),
      sectionName: fieldRow.section_name,
      pageNumber: fieldRow.page_number,
      instructions,
      questionConfig,
    };
  });

  app.post("/templates/:templateId/fields/:fieldId/ai-suggestion", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);
    const payload = formFieldAiSuggestionSchema.parse(request.body);
    const { templateId, fieldId } = request.params as { templateId: string; fieldId: string };

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    const [fieldRow] = await prisma.$queryRaw<
      Array<{
        id: string;
        label: string;
        pdf_field_name: string;
        data_type: string;
        section_name: string | null;
        page_number: number | null;
        condition_expression_json: unknown;
        template_law_firm_id: string | null;
        template_name: string;
        template_code: string;
        template_version_label: string;
        template_description: string | null;
      }>
    >`
      SELECT
        ff.id,
        ff.label,
        ff.pdf_field_name,
        ff.data_type,
        ff.section_name,
        ff.page_number,
        ff.condition_expression_json,
        ft.law_firm_id AS template_law_firm_id,
        ft.name AS template_name,
        ft.code AS template_code,
        ft.version_label AS template_version_label,
        ft.description AS template_description
      FROM form_fields ff
      JOIN form_templates ft ON ft.id = ff.form_template_id
      WHERE ff.id = ${fieldId}
        AND ff.form_template_id = ${templateId}
      LIMIT 1
    `;

    if (!fieldRow) {
      throw reply.notFound("Form field not found");
    }

    if (fieldRow.template_law_firm_id !== profile.lawFirm.id) {
      throw reply.forbidden("Only workspace-owned form templates can be edited");
    }

    const nearbyFields = await prisma.$queryRaw<
      Array<{
        label: string;
        data_type: string;
        section_name: string | null;
      }>
    >`
      SELECT label, data_type, section_name
      FROM form_fields
      WHERE form_template_id = ${templateId}
        AND id <> ${fieldId}
      ORDER BY
        CASE WHEN section_name <=> ${fieldRow.section_name} THEN 0 ELSE 1 END,
        page_number ASC,
        created_at ASC
      LIMIT 10
    `;

    const questionConfig = parseFormFieldQuestionConfig({
      rawMetadata: fieldRow.condition_expression_json,
      fallbackLabel: fieldRow.label,
    });

    let aiRunId: string | null = null;

    try {
      const aiRun = await createAiRun({
        lawFirmId: profile.lawFirm.id,
        runType: "form_field_configuration_suggestion",
        status: "running",
      });
      aiRunId = aiRun.id;

      const suggestion = await suggestFormFieldConfigurationValue({
        lawFirmId: profile.lawFirm.id,
        target: payload.target,
        formTemplate: {
          name: fieldRow.template_name,
          code: fieldRow.template_code,
          versionLabel: fieldRow.template_version_label,
          description: fieldRow.template_description,
        },
        field: {
          label: fieldRow.label,
          pdfFieldName: fieldRow.pdf_field_name,
          dataType: fieldRow.data_type,
          sectionName: fieldRow.section_name,
          pageNumber: fieldRow.page_number,
          questionConfig,
        },
        nearbyFields: nearbyFields.map((item) => ({
          label: item.label,
          dataType: item.data_type,
          sectionName: item.section_name,
        })),
      });

      await finishAiRun({
        aiRunId: aiRun.id,
        status: "completed",
        inputTokens: suggestion.usage.inputTokens,
        outputTokens: suggestion.usage.outputTokens,
      });

      await writeAuditLog({
        lawFirmId: profile.lawFirm.id,
        officeId: profile.user.primaryOfficeId ?? null,
        actorUserId: profile.user.id,
        entityType: "form_field",
        entityId: fieldId,
        action: "form_field.ai_suggestion.generate",
        afterJson: {
          target: payload.target,
          aiRunId: aiRun.id,
          model: suggestion.model,
          fieldLabel: fieldRow.label,
          templateId,
          templateCode: fieldRow.template_code,
        },
        request,
      });

      return {
        aiRunId: aiRun.id,
        model: suggestion.model,
        target: suggestion.target,
        value: suggestion.value,
        reason: suggestion.reason,
      };
    } catch (error) {
      if (aiRunId) {
        await finishAiRun({
          aiRunId,
          status: "failed",
          errorMessage: error instanceof Error ? error.message : "Failed to generate AI suggestion.",
        });
      }

      throw reply.badRequest(
        error instanceof Error ? error.message : "Failed to generate AI suggestion.",
      );
    }
  });

  app.delete("/templates/:templateId", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);
    const { templateId } = request.params as { templateId: string };

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    const [template] = await prisma.$queryRaw<
      Array<{
        id: string;
        law_firm_id: string | null;
        code: string;
        name: string;
        version_label: string;
        description: string | null;
        base_pdf_file_id: string | null;
        is_active: number;
        is_system_template: number;
        agent_code: string | null;
        field_count: bigint | number | string;
      }>
    >`
      SELECT
        ft.id,
        ft.law_firm_id,
        ft.code,
        ft.name,
        ft.version_label,
        ft.description,
        ft.base_pdf_file_id,
        ft.is_active,
        ft.is_system_template,
        ft.agent_code,
        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 = ${templateId}
      GROUP BY
        ft.id,
        ft.law_firm_id,
        ft.code,
        ft.name,
        ft.version_label,
        ft.description,
        ft.base_pdf_file_id,
        ft.is_active,
        ft.is_system_template,
        ft.agent_code
      LIMIT 1
    `;

    if (!template) {
      throw reply.notFound("Form template not found");
    }

    if (template.law_firm_id !== profile.lawFirm.id) {
      if (template.law_firm_id !== null || !Boolean(template.is_system_template)) {
        throw reply.forbidden("Only workspace-visible form templates can be deleted");
      }
    }

    if (!Boolean(template.is_active)) {
      return { deleted: false };
    }

    if (template.law_firm_id === null && Boolean(template.is_system_template)) {
      const [existingWorkspaceOverride] = await prisma.$queryRaw<
        Array<{
          id: string;
          is_active: number;
        }>
      >`
        SELECT id, is_active
        FROM form_templates
        WHERE law_firm_id = ${profile.lawFirm.id}
          AND code = ${template.code}
          AND version_label = ${template.version_label}
        LIMIT 1
      `;

      if (existingWorkspaceOverride) {
        await prisma.$executeRaw`
          UPDATE form_templates
          SET
            name = ${template.name},
            description = ${template.description},
            base_pdf_file_id = ${template.base_pdf_file_id},
            is_active = 0,
            is_system_template = 0,
            updated_at = CURRENT_TIMESTAMP
          WHERE id = ${existingWorkspaceOverride.id}
        `;
      } else {
        await prisma.$executeRaw`
          INSERT INTO form_templates (
            id, law_firm_id, code, name, version_label, base_pdf_file_id, description, is_active,
            is_system_template, created_at, updated_at
          ) VALUES (
            ${createId()},
            ${profile.lawFirm.id},
            ${template.code},
            ${template.name},
            ${template.version_label},
            ${template.base_pdf_file_id},
            ${template.description},
            0,
            0,
            CURRENT_TIMESTAMP,
            CURRENT_TIMESTAMP
          )
        `;
      }

      await writeAuditLog({
        lawFirmId: profile.lawFirm.id,
        officeId: profile.user.primaryOfficeId ?? null,
        actorUserId: profile.user.id,
        entityType: "form_template",
        entityId: templateId,
        action: "form_template.hide_system",
        beforeJson: {
          code: template.code,
          name: template.name,
          versionLabel: template.version_label,
          description: template.description,
          basePdfFileId: template.base_pdf_file_id,
          fieldCount: Number(template.field_count ?? 0),
          agentCode: template.agent_code,
        },
        afterJson: {
          deleted: true,
          hiddenFromWorkspace: true,
        },
        request,
      });

      return { deleted: true };
    }

    await prisma.$executeRaw`
      UPDATE form_templates
      SET
        is_active = 0,
        updated_at = CURRENT_TIMESTAMP
      WHERE id = ${templateId}
        AND law_firm_id = ${profile.lawFirm.id}
    `;

    await writeAuditLog({
      lawFirmId: profile.lawFirm.id,
      officeId: profile.user.primaryOfficeId ?? null,
      actorUserId: profile.user.id,
      entityType: "form_template",
      entityId: templateId,
      action: "form_template.delete",
      beforeJson: {
        code: template.code,
        name: template.name,
        versionLabel: template.version_label,
        description: template.description,
        basePdfFileId: template.base_pdf_file_id,
        fieldCount: Number(template.field_count ?? 0),
        agentCode: template.agent_code,
      },
      afterJson: {
        deleted: true,
      },
      request,
    });

    return { deleted: true };
  });

  app.get("/case/:caseId", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);
    const { caseId } = request.params as { caseId: string };

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    const caseForms = await prisma.$queryRaw<
      Array<{
        id: string;
        requirement_name: string;
        status_code: string;
        form_name: string;
      }>
    >`
      SELECT cf.id, cf.requirement_name, cf.status_code, ft.name AS form_name
      FROM case_forms cf
      JOIN form_templates ft ON ft.id = cf.form_template_id
      WHERE cf.case_id = ${caseId}
      ORDER BY cf.created_at ASC
    `;

    return caseForms.map((caseForm) => ({
      id: caseForm.id,
      requirementName: caseForm.requirement_name,
      statusCode: caseForm.status_code,
      formName: caseForm.form_name,
    }));
  });

  void resumePendingSpecializedFormGenerationJobs(app.log).catch((error) => {
    app.log.error({ error }, "Unable to resume pending specialized form generation jobs");
  });
}
