import { and, desc, eq } from 'drizzle-orm';

import { db } from '@/lib/db';
import { formTemplates } from '@/lib/db/schema/documents';
import { createAuditLog } from '@/lib/audit';
import { CodedError, NotFoundError } from '@/lib/errors';
import type {
  CreateFormTemplateInput,
  UpdateFormTemplateInput,
} from '@/lib/validators/form-templates';

interface AuditMeta {
  userId: string;
  portId: string;
  ipAddress?: string;
  userAgent?: string;
}

export async function listFormTemplates(portId: string) {
  return db
    .select()
    .from(formTemplates)
    .where(eq(formTemplates.portId, portId))
    .orderBy(desc(formTemplates.updatedAt));
}

export async function getFormTemplateById(id: string, portId: string) {
  const tpl = await db.query.formTemplates.findFirst({
    where: and(eq(formTemplates.id, id), eq(formTemplates.portId, portId)),
  });
  if (!tpl) throw new NotFoundError('Form template');
  return tpl;
}

export async function createFormTemplate(
  portId: string,
  data: CreateFormTemplateInput,
  meta: AuditMeta,
) {
  const [tpl] = await db
    .insert(formTemplates)
    .values({
      portId,
      name: data.name,
      description: data.description ?? null,
      fields: data.fields,
      branding: data.branding ?? {},
      isActive: data.isActive ?? true,
      createdBy: meta.userId,
    })
    .returning();

  if (!tpl)
    throw new CodedError('INSERT_RETURNING_EMPTY', {
      internalMessage: 'Form template insert returned no row',
    });

  void createAuditLog({
    userId: meta.userId,
    portId,
    action: 'create',
    entityType: 'form_template',
    entityId: tpl.id,
    newValue: { name: data.name },
    ipAddress: meta.ipAddress ?? '',
    userAgent: meta.userAgent ?? '',
  });

  return tpl;
}

export async function updateFormTemplate(
  id: string,
  portId: string,
  data: UpdateFormTemplateInput,
  meta: AuditMeta,
) {
  const existing = await getFormTemplateById(id, portId);

  const [updated] = await db
    .update(formTemplates)
    .set({
      ...(data.name !== undefined && { name: data.name }),
      ...(data.description !== undefined && { description: data.description ?? null }),
      ...(data.fields !== undefined && { fields: data.fields }),
      ...(data.branding !== undefined && { branding: data.branding }),
      ...(data.isActive !== undefined && { isActive: data.isActive }),
      updatedAt: new Date(),
    })
    .where(and(eq(formTemplates.id, id), eq(formTemplates.portId, portId)))
    .returning();

  if (!updated) throw new NotFoundError('Form template');

  void createAuditLog({
    userId: meta.userId,
    portId,
    action: 'update',
    entityType: 'form_template',
    entityId: id,
    oldValue: { name: existing.name },
    newValue: data,
    ipAddress: meta.ipAddress ?? '',
    userAgent: meta.userAgent ?? '',
  });

  return updated;
}

export async function deleteFormTemplate(id: string, portId: string, meta: AuditMeta) {
  await getFormTemplateById(id, portId);

  await db
    .delete(formTemplates)
    .where(and(eq(formTemplates.id, id), eq(formTemplates.portId, portId)));

  void createAuditLog({
    userId: meta.userId,
    portId,
    action: 'delete',
    entityType: 'form_template',
    entityId: id,
    ipAddress: meta.ipAddress ?? '',
    userAgent: meta.userAgent ?? '',
  });
}
