import type {
  CollectionAfterChangeHook,
  CollectionAfterDeleteHook,
  GlobalAfterChangeHook,
} from 'payload'

type AuditAction = 'create' | 'update' | 'delete'

const writeAuditLog = async (args: {
  action: AuditAction
  entityType: 'collection' | 'global'
  entity: string
  docId?: string | number | null
  doc?: unknown
  previousDoc?: unknown
  req: { payload: any; user?: any; ip?: string; context?: Record<string, unknown> }
}) => {
  if (args.req?.context?.skipAuditLog) return
  if (!args.req.user) return

  const user = args.req.user
  const data = {
    action: args.action,
    entityType: args.entityType,
    entity: args.entity,
    docId: args.docId != null ? String(args.docId) : null,
    user: user?.id ?? null,
    userName: user?.name ?? null,
    userEmail: user?.email ?? null,
    ip: args.req.ip ?? null,
    data: {
      before: args.previousDoc ?? null,
      after: args.doc ?? null,
    },
  }

  try {
    await args.req.payload.create({
      collection: 'audit-logs',
      data,
      // req: args.req, // DECOUPLED: Removed to prevent transaction deadlock with GCS plugin
      overrideAccess: true, // Required since we're not passing the req/user context
      context: { skipAuditLog: true },
    })
  } catch (error: any) {
    args.req.payload?.logger?.error?.(`[audit-log] failed to write audit entry: ${error.message}`)
  }
}

const sanitizeMediaDoc = (doc: any) => {
  if (!doc || typeof doc !== 'object') return doc
  return {
    id: doc.id,
    filename: doc.filename,
    mimeType: doc.mimeType,
    filesize: doc.filesize,
    width: doc.width,
    height: doc.height,
  }
}

export const logCollectionChange =
  (collection: string): CollectionAfterChangeHook =>
  async ({ doc, previousDoc, operation, req, context }) => {
    if (context?.skipAuditLog) return doc
    if (operation !== 'create' && operation !== 'update') return doc

    const safeDoc = collection === 'media' ? sanitizeMediaDoc(doc) : doc
    const safePreviousDoc = collection === 'media' ? sanitizeMediaDoc(previousDoc) : previousDoc

    await writeAuditLog({
      action: operation,
      entityType: 'collection',
      entity: collection,
      docId: (doc as { id?: string | number })?.id ?? null,
      doc: safeDoc,
      previousDoc: safePreviousDoc,
      req,
    })

    return doc
  }

export const logCollectionDelete =
  (collection: string): CollectionAfterDeleteHook =>
  async ({ doc, id, req, context }) => {
    if (context?.skipAuditLog) return doc

    const safeDoc = collection === 'media' ? sanitizeMediaDoc(doc) : doc

    await writeAuditLog({
      action: 'delete',
      entityType: 'collection',
      entity: collection,
      docId: id ?? (doc as { id?: string | number })?.id ?? null,
      doc: safeDoc,
      previousDoc: null,
      req,
    })

    return doc
  }

export const logGlobalChange =
  (global: string): GlobalAfterChangeHook =>
  async ({ doc, previousDoc, req, context }) => {
    if (context?.skipAuditLog) return doc

    await writeAuditLog({
      action: 'update',
      entityType: 'global',
      entity: global,
      docId: global,
      doc,
      previousDoc,
      req,
    })

    return doc
  }
