// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========

import { afterEach, describe, expect, it, vi } from 'vitest'
import { DropboxTokenManager, dropboxDownload } from './client.ts'
import type { ByteWindow } from '../../utils/ranges.ts'

const BODY = '0123456789'

function tokenManager(): DropboxTokenManager {
  return new DropboxTokenManager({
    clientId: 'c',
    clientSecret: 's',
    refreshToken: 'r',
    refreshFn: () => Promise.resolve({ accessToken: 'tok', expiresIn: 3600 }),
  })
}

function respond(status: number, body: string): typeof globalThis.fetch {
  return vi.fn(() =>
    Promise.resolve({
      ok: status < 400,
      status,
      arrayBuffer: () => Promise.resolve(new TextEncoder().encode(body).buffer),
      text: () => Promise.resolve(''),
    }),
  ) as unknown as typeof globalThis.fetch
}

async function download(
  status: number,
  body: string,
  window?: ByteWindow,
): Promise<{ out: Uint8Array; sent: Record<string, string> }> {
  const fetch = respond(status, body)
  vi.stubGlobal('fetch', fetch)
  const out = await dropboxDownload(tokenManager(), '/a.txt', window)
  const init = vi.mocked(fetch).mock.calls[0]?.[1]
  return { out, sent: (init?.headers ?? {}) as Record<string, string> }
}

afterEach(() => {
  vi.unstubAllGlobals()
})

describe('dropboxDownload', () => {
  it('sends the window as a Range header', async () => {
    const { sent } = await download(206, '234', { offset: 2, size: 3 })
    expect(sent.Range).toBe('bytes=2-4')
  })

  it('trusts a 206 body as already the window', async () => {
    const { out } = await download(206, '234', { offset: 2, size: 3 })
    expect(new TextDecoder().decode(out)).toBe('234')
  })

  // RFC 9110 lets a server answer a Range request with the whole
  // representation. Before this was handled the caller got every byte for
  // what it asked to be a window.
  it('slices locally when the server ignores the range', async () => {
    const { out } = await download(200, BODY, { offset: 2, size: 3 })
    expect(new TextDecoder().decode(out)).toBe('234')
  })

  it('sends no Range and reads whole when no window is asked for', async () => {
    const { out, sent } = await download(200, BODY)
    expect(new TextDecoder().decode(out)).toBe(BODY)
    expect(sent.Range).toBeUndefined()
  })
})
