/**
 * Task audit log query helpers — tasks-detail-communication.
 *
 * All helpers are tenant-scoped. Immutable insert-only log.
 */
import { and, eq, asc } from 'drizzle-orm'
import type { Db, DbTx } from '../client'
import { taskAuditLog } from '../schema/task-comms'
import { users } from '../schema/users'

// ── Types ─────────────────────────────────────────────────────────────────────

export interface TaskAuditRow {
  id: string
  taskId: string
  actorId: string
  actorName: string | null
  action: string
  previousValue: unknown
  newValue: unknown
  createdAt: string
}

// ── Serializer ────────────────────────────────────────────────────────────────

function serializeAuditRow(
  row: typeof taskAuditLog.$inferSelect & { actorName: string | null },
): TaskAuditRow {
  return {
    id: row.id,
    taskId: row.taskId,
    actorId: row.actorId,
    actorName: row.actorName,
    action: row.action,
    previousValue: row.previousValue ?? null,
    newValue: row.newValue ?? null,
    createdAt: row.createdAt.toISOString(),
  }
}

// ── recordTaskAudit ───────────────────────────────────────────────────────────

export async function recordTaskAudit(
  db: Db | DbTx,
  args: {
    tenantId: string
    taskId: string
    actorId: string
    action: string
    previousValue: unknown
    newValue: unknown
  },
): Promise<void> {
  await db.insert(taskAuditLog).values({
    taskId: args.taskId,
    tenantId: args.tenantId,
    actorId: args.actorId,
    action: args.action,
    previousValue: args.previousValue as Record<string, unknown> | null,
    newValue: args.newValue as Record<string, unknown> | null,
  })
}

// ── listTaskAudit ─────────────────────────────────────────────────────────────

export async function listTaskAudit(
  db: Db,
  tenantId: string,
  taskId: string,
  opts: { limit: number; cursor?: string },
): Promise<{ rows: TaskAuditRow[]; nextCursor: string | null }> {
  const limit = Math.min(opts.limit, 100)

  const rows = await db
    .select({
      id: taskAuditLog.id,
      taskId: taskAuditLog.taskId,
      tenantId: taskAuditLog.tenantId,
      actorId: taskAuditLog.actorId,
      action: taskAuditLog.action,
      previousValue: taskAuditLog.previousValue,
      newValue: taskAuditLog.newValue,
      createdAt: taskAuditLog.createdAt,
      actorName: users.name,
    })
    .from(taskAuditLog)
    .leftJoin(users, eq(taskAuditLog.actorId, users.id))
    .where(
      and(
        eq(taskAuditLog.tenantId, tenantId),
        eq(taskAuditLog.taskId, taskId),
      ),
    )
    .orderBy(asc(taskAuditLog.createdAt), asc(taskAuditLog.id))
    .limit(limit + 1)

  // Apply cursor filtering in-memory
  let filteredRows = rows
  if (opts.cursor) {
    try {
      const ts = Buffer.from(opts.cursor, 'base64url').toString('utf8')
      const { createdAt, id } = JSON.parse(ts) as { createdAt: string; id: string }
      const cursorDate = new Date(createdAt)
      filteredRows = rows.filter((r) => {
        const rDate = r.createdAt
        return rDate > cursorDate || (rDate.getTime() === cursorDate.getTime() && r.id > id)
      })
    } catch {
      // ignore invalid cursor
    }
  }

  const hasMore = filteredRows.length > limit
  const pageRows = hasMore ? filteredRows.slice(0, limit) : filteredRows

  let nextCursor: string | null = null
  if (hasMore) {
    const last = pageRows[pageRows.length - 1]
    if (last) {
      nextCursor = Buffer.from(
        JSON.stringify({ createdAt: last.createdAt.toISOString(), id: last.id }),
      ).toString('base64url')
    }
  }

  return {
    rows: pageRows.map((r) => serializeAuditRow({ ...r, actorName: r.actorName ?? null })),
    nextCursor,
  }
}
