// Tapestry history walk-back tests. Verifies the `useInfiniteQuery` config
// shape so a future query-key rename or page-cap regression fails CI before
// it ships.

const captured: { options: any } = { options: null }

jest.mock('@tanstack/react-query', () => ({
  useInfiniteQuery: (options: unknown) => {
    captured.options = options
    return {} as unknown
  },
}))

jest.mock('src/lib/api-context', () => ({
  ContextForActiveAccount: () => ({ request: jest.fn() }),
}))

// `range.ts` -> `queries/keys.ts` -> `accountStore.ts` -> `storage.ts` ->
// `react-native-mmkv`. The native-module load fails outside a built app, so
// short-circuit at the lowest stable layer with no-op MMKV/SecureStore
// shims. The test never reads or writes through them.
jest.mock('react-native-mmkv', () => ({
  createMMKV: () => ({
    getString: () => undefined,
    getNumber: () => undefined,
    getBoolean: () => undefined,
    set: () => {},
    remove: () => {},
    delete: () => {},
    clearAll: () => {},
    getAllKeys: () => [],
    addOnValueChangedListener: () => ({ remove: () => {} }),
  }),
}))

jest.mock('expo-secure-store', () => ({
  getItem: () => null,
  setItem: () => {},
  deleteItemAsync: async () => {},
}))

const { useTapestryPages, MAX_BACKFILL_PAGES, MIN_ROWS_FOR_VIEWPORT } =
  require('src/lib/tapestry/range') as typeof import('src/lib/tapestry/range')

describe('useTapestryPages', () => {
  beforeEach(() => {
    captured.options = null
  })

  it('uses the global per-instance pages query key', () => {
    useTapestryPages('outlande.rs')
    expect(captured.options).not.toBeNull()
    expect(captured.options.queryKey).toEqual([
      'global',
      'tapestry',
      'outlande.rs',
      'pages',
    ])
  })

  it('seeds from the current 30-minute page', () => {
    useTapestryPages('outlande.rs')
    const param: string = captured.options.initialPageParam
    expect(param).toMatch(/^\d{12}$/)
    // Page-aligned: minutes are either 00 or 30.
    expect(['00', '30']).toContain(param.slice(10, 12))
  })

  it('walks pages backwards by 30 minutes', () => {
    useTapestryPages('outlande.rs')
    // Fixed page on a clean boundary so `previousPage` math is unambiguous.
    const next = captured.options.getNextPageParam({
      page: '202604281200',
      rows: [],
    })
    expect(next).toBe('202604281130')
  })

  it('walks across hour and day boundaries', () => {
    useTapestryPages('outlande.rs')
    expect(
      captured.options.getNextPageParam({ page: '202604281000', rows: [] })
    ).toBe('202604280930')
    // Hour boundary: 12:00 → 11:30
    expect(
      captured.options.getNextPageParam({ page: '202604281200', rows: [] })
    ).toBe('202604281130')
    // Day boundary: 00:00 (28 Apr) → 23:30 (27 Apr)
    expect(
      captured.options.getNextPageParam({ page: '202604280000', rows: [] })
    ).toBe('202604272330')
  })

  it('caps in-memory at the full 30-day retention window (1440 pages)', () => {
    useTapestryPages('outlande.rs')
    expect(captured.options.maxPages).toBe(1440)
  })

  it('exports backfill constants matching the in-memory cap', () => {
    // Eager loop walks the full retention window so a sparse instance can
    // reach its oldest populated bucket; viewport-row goal stays at 30.
    expect(MAX_BACKFILL_PAGES).toBe(1440)
    expect(MIN_ROWS_FOR_VIEWPORT).toBe(30)
  })

  it('disables when host is empty', () => {
    useTapestryPages('')
    expect(captured.options.enabled).toBe(false)
  })

  it('rekeys on instance', () => {
    useTapestryPages('outlande.rs')
    const a = captured.options.queryKey
    useTapestryPages('halflight.example')
    const b = captured.options.queryKey
    expect(a).not.toEqual(b)
    expect(b[2]).toBe('halflight.example')
  })
})
