import { and, desc, eq, type SQL } from 'drizzle-orm';
import { headers } from 'next/headers';
import { trAuditLogs } from '@/db/schema';
import { db } from '@/lib/db';
import { getCurrentUserContext } from '@/features/foundation/auth-context/service';
import type { AuditLogInput, AuditLogListFilters, AuditLogRecord } from './types';

async function resolveAuditContext(input: AuditLogInput) {
  const context = await getCurrentUserContext();
  const organizationId = input.organizationId ?? context?.activeOrganization?.id ?? null;
  const userId = input.userId ?? context?.user.id ?? null;

  if (!organizationId) {
    throw new Error('organizationId is required for audit logging');
  }

  if (!userId) {
    throw new Error('userId is required for audit logging');
  }

  return {
    organizationId,
    userId
  };
}

async function resolveRequestId(inputRequestId?: string | null) {
  if (inputRequestId) {
    return inputRequestId;
  }

  try {
    const requestHeaders = await headers();

    return requestHeaders.get('x-request-id');
  } catch {
    return null;
  }
}

function mapAuditRecord(row: typeof trAuditLogs.$inferSelect): AuditLogRecord {
  return {
    id: row.id,
    organizationId: row.organizationId,
    branchId: row.branchId,
    userId: row.userId,
    entityType: row.entityType,
    entityId: row.entityId,
    action: row.action,
    beforeData: row.beforeData,
    afterData: row.afterData,
    requestId: row.requestId,
    createdAt: row.createdAt.toISOString()
  };
}

export async function auditAction(input: AuditLogInput) {
  const { organizationId, userId } = await resolveAuditContext(input);
  const requestId = await resolveRequestId(input.requestId);
  const [created] = await db
    .insert(trAuditLogs)
    .values({
      id: crypto.randomUUID(),
      organizationId,
      branchId: input.branchId ?? null,
      userId,
      entityType: input.entityType,
      entityId: input.entityId,
      action: input.action,
      beforeData:
        input.beforeData === undefined ? null : JSON.parse(JSON.stringify(input.beforeData)),
      afterData: input.afterData === undefined ? null : JSON.parse(JSON.stringify(input.afterData)),
      requestId
    })
    .returning();

  return mapAuditRecord(created);
}

export async function auditCreate(input: Omit<AuditLogInput, 'action'>) {
  return auditAction({
    ...input,
    action: 'create'
  });
}

export async function auditUpdate(input: Omit<AuditLogInput, 'action'>) {
  return auditAction({
    ...input,
    action: 'update'
  });
}

export async function auditDelete(input: Omit<AuditLogInput, 'action'>) {
  return auditAction({
    ...input,
    action: 'delete'
  });
}

export async function listAuditLogs(filters: AuditLogListFilters): Promise<AuditLogRecord[]> {
  const whereFilters: SQL[] = [
    ...(filters.organizationId ? [eq(trAuditLogs.organizationId, filters.organizationId)] : []),
    ...(filters.entityType ? [eq(trAuditLogs.entityType, filters.entityType)] : []),
    ...(filters.entityId ? [eq(trAuditLogs.entityId, filters.entityId)] : [])
  ];
  const where = whereFilters.length > 1 ? and(...whereFilters) : whereFilters[0];
  const limit = filters.limit ?? 20;

  const rows = await db.query.trAuditLogs.findMany({
    where,
    orderBy: [desc(trAuditLogs.createdAt)],
    limit
  });

  return rows.map(mapAuditRecord);
}
