import { describe, expect, it, vi } from 'vitest'

const ids = {
  tenant: '00000000-0000-4000-8000-000000000001',
  user: '00000000-0000-4000-8000-000000000002',
}

function selectDb(row: Record<string, unknown> | null) {
  const limit = vi.fn().mockResolvedValue(row ? [row] : [])
  const where = vi.fn(() => ({ limit }))
  const from = vi.fn(() => ({ where }))
  const select = vi.fn(() => ({ from }))
  return { select, where, limit }
}

function transactionDb(tx: Record<string, unknown>) {
  return { transaction: vi.fn(async (callback: (value: typeof tx) => Promise<unknown>) => callback(tx)) }
}

describe('shell layout queries', () => {
  it('serializes timestamps while reading the tenant/user/device-isolated row', async () => {
    const db = selectDb({ payload: { windows: [] }, version: 7, updatedAt: new Date('2026-07-13T00:00:00.000Z') })
    const { getShellLayout } = await import('../../src/queries/shell-layout')

    await expect(getShellLayout(db as never, ids.user, ids.tenant, 'desktop')).resolves.toEqual({
      payload: { windows: [] },
      version: 7,
      updated_at: '2026-07-13T00:00:00.000Z',
    })
    expect(db.where).toHaveBeenCalledTimes(1)
  })

  it('writes the audit row in the same transaction as a successful update', async () => {
    const auditValues: Record<string, unknown>[] = []
    const tx = {
      update: vi.fn(() => ({
        set: vi.fn(() => ({
          where: vi.fn(() => ({
            returning: vi.fn().mockResolvedValue([{ payload: { windows: [] }, version: 3, updatedAt: new Date('2026-07-13T00:00:00.000Z') }]),
          })),
        })),
      })),
      insert: vi.fn(() => ({ values: vi.fn((values: Record<string, unknown>) => { auditValues.push(values); return Promise.resolve() }) })),
    }
    const db = transactionDb(tx)
    const { setShellLayout } = await import('../../src/queries/shell-layout')

    await expect(setShellLayout(db as never, ids.user, ids.tenant, 'desktop', { windows: [] }, 2)).resolves.toMatchObject({ version: 3 })
    expect(auditValues).toEqual([expect.objectContaining({ tenantId: ids.tenant, actorId: ids.user, action: 'shell_layout.updated' })])
  })

  it('returns a conflict without auditing when the optimistic version is stale', async () => {
    const auditValues: Record<string, unknown>[] = []
    const tx = {
      update: vi.fn(() => ({
        set: vi.fn(() => ({ where: vi.fn(() => ({ returning: vi.fn().mockResolvedValue([]) })) })),
      })),
      insert: vi.fn(() => ({ values: vi.fn((values: Record<string, unknown>) => { auditValues.push(values); return Promise.resolve() }) })),
    }
    const db = transactionDb(tx)
    const { setShellLayout } = await import('../../src/queries/shell-layout')

    await expect(setShellLayout(db as never, ids.user, ids.tenant, 'desktop', { windows: [] }, 4)).resolves.toBeNull()
    expect(auditValues).toHaveLength(0)
  })

  it('audits only a delete that removed an existing layout', async () => {
    const auditValues: Record<string, unknown>[] = []
    const tx = {
      delete: vi.fn(() => ({ where: vi.fn(() => ({ returning: vi.fn().mockResolvedValue([{ userId: ids.user }]) })) })),
      insert: vi.fn(() => ({ values: vi.fn((values: Record<string, unknown>) => { auditValues.push(values); return Promise.resolve() }) })),
    }
    const db = transactionDb(tx)
    const { deleteShellLayout } = await import('../../src/queries/shell-layout')

    await expect(deleteShellLayout(db as never, ids.user, ids.tenant, 'mobile')).resolves.toBe(true)
    expect(auditValues).toEqual([expect.objectContaining({ action: 'shell_layout.deleted' })])
  })
})
