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

import { publishRealtimeEvent as hostPublishRealtimeEvent } from '@zync/realtime/server'
import type { RealtimeEvent } from '@zync/realtime'

import {
  dispatchPlatformRealtimeEvent,
  handlePlatformRealtimeBatch,
  publishPlatformRealtimeEvent,
  toPlatformRealtimeEvent,
  verifyPlatformRealtimeInternalSecret,
} from '../realtime'

describe('realtime platform parity', () => {
  beforeEach(() => {
    vi.restoreAllMocks()
    Object.assign(crypto.subtle, {
      timingSafeEqual(left: ArrayBufferView, right: ArrayBufferView) {
        const a = new Uint8Array(left.buffer, left.byteOffset, left.byteLength)
        const b = new Uint8Array(right.buffer, right.byteOffset, right.byteLength)
        if (a.byteLength !== b.byteLength) return false
        let diff = 0
        for (let i = 0; i < a.byteLength; i += 1) diff |= a[i]! ^ b[i]!
        return diff === 0
      },
    })
  })

  it('matches the legacy queue envelope while preserving tenant routing', async () => {
    vi.spyOn(crypto, 'randomUUID').mockReturnValue('11111111-1111-1111-1111-111111111111')
    vi.useFakeTimers()
    vi.setSystemTime(new Date('2026-07-01T12:00:00.000Z'))

    const sendHost = vi.fn(async (_value: unknown) => {})
    const sendPlatform = vi.fn(async (_value: unknown) => {})
    const input = {
      type: 'notification.new' as const,
      tenantId: 'tenant-1',
      targetUserId: 'user-1',
      payload: { notificationId: 'n-1', unreadCount: 3 },
    }

    await hostPublishRealtimeEvent({ send: sendHost } as never, input)
    await publishPlatformRealtimeEvent({ send: sendPlatform } as never, input)

    expect(sendPlatform.mock.calls[0]?.[0]).toEqual(
      toPlatformRealtimeEvent(sendHost.mock.calls[0]?.[0] as RealtimeEvent),
    )
    vi.useRealTimers()
  })

  it('matches the legacy DO broadcast request body and queue ack/retry semantics', async () => {
    const fetchOk = vi.fn(async () => new Response(null, { status: 204 }))
    const fetchFail = vi.fn(async () => {
      throw new Error('unreachable')
    })
    const makeEnv = (fetchImpl: typeof fetchOk) =>
      ({
        DO_REALTIME: {
          idFromName: vi.fn((name: string) => name),
          get: vi.fn(() => ({ fetch: fetchImpl })),
        },
      }) as never

    const event: RealtimeEvent = {
      id: 'event-1',
      type: 'task.assigned',
      tenantId: 'tenant-1',
      targetUserId: 'user-9',
      payload: {
        taskId: 'task-1',
        assignedToUserId: 'user-9',
        assignedByUserId: 'user-2',
      },
      timestamp: '2026-07-01T12:00:00.000Z',
    }

    await dispatchPlatformRealtimeEvent(makeEnv(fetchOk), event)

    expect(fetchOk).toHaveBeenCalledWith('https://do/broadcast', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(event),
    })

    const ack = vi.fn()
    const retry = vi.fn()
    await handlePlatformRealtimeBatch(
      {
        queue: 'zync-realtime',
        messages: [{ body: event, ack, retry }],
        retryAll: vi.fn(),
        ackAll: vi.fn(),
      } as never,
      makeEnv(fetchOk),
    )
    expect(ack).toHaveBeenCalledOnce()
    expect(retry).not.toHaveBeenCalled()

    const ackFail = vi.fn()
    const retryFail = vi.fn()
    await handlePlatformRealtimeBatch(
      {
        queue: 'zync-realtime',
        messages: [{ body: event, ack: ackFail, retry: retryFail }],
        retryAll: vi.fn(),
        ackAll: vi.fn(),
      } as never,
      makeEnv(fetchFail),
    )
    expect(retryFail).toHaveBeenCalledOnce()
    expect(ackFail).not.toHaveBeenCalled()
  })

  it('matches legacy constant-time comparison verdicts', () => {
    const expected = 'super-secret-token-value'
    const equalLengthWrong = 'super-secret-token-wrong'

    expect(verifyPlatformRealtimeInternalSecret(expected, expected)).toBe(true)
    expect(verifyPlatformRealtimeInternalSecret(equalLengthWrong, expected)).toBe(false)
    expect(verifyPlatformRealtimeInternalSecret('short', expected)).toBe(false)
    expect(verifyPlatformRealtimeInternalSecret(null, expected)).toBe(false)
  })
})
