import { suite } from '../lib/context.js'

/**
 * Helper: make a raw WebDAV request with Basic Auth.
 * The API client doesn't support custom HTTP methods, so we use fetch directly.
 */
async function dav(
  baseUrl: string,
  method: string,
  path: string,
  opts: { auth: string; headers?: Record<string, string>; body?: string | Buffer } = { auth: '' },
) {
  const headers: Record<string, string> = {
    Authorization: opts.auth,
    ...(opts.headers ?? {}),
  }

  const res = await fetch(`${baseUrl}${path}`, {
    method,
    headers,
    body: opts.body,
  })

  const text = await res.text()
  return { status: res.status, body: text, headers: Object.fromEntries(res.headers.entries()) }
}

suite('WebDAV', { tags: ['webdav', 'storage'] }, (t) => {
  // Use the same test user created by the test runner
  const email = 'test@lunarfront.dev'
  const password = 'testpassword1234'
  const basicAuth = 'Basic ' + Buffer.from(`${email}:${password}`).toString('base64')
  const badAuth = 'Basic ' + Buffer.from(`${email}:wrongpassword`).toString('base64')

  // --- OPTIONS ---

  t.test('OPTIONS returns DAV headers on root', { tags: ['options'] }, async () => {
    const res = await dav(t.baseUrl, 'OPTIONS', '/webdav/')
    // CORS plugin may return 204 for preflight, our handler returns 200 — both are valid
    t.assert.ok(res.status === 200 || res.status === 204, `Expected 200 or 204, got ${res.status}`)
  })

  t.test('OPTIONS returns DAV headers on subpath', { tags: ['options'] }, async () => {
    const res = await dav(t.baseUrl, 'OPTIONS', '/webdav/any/path')
    t.assert.ok(res.status === 200 || res.status === 204, `Expected 200 or 204, got ${res.status}`)
  })

  // --- Authentication ---

  t.test('returns 401 without credentials', { tags: ['auth'] }, async () => {
    const res = await fetch(`${t.baseUrl}/webdav/`, { method: 'PROPFIND' })
    t.assert.equal(res.status, 401)
    t.assert.contains(res.headers.get('www-authenticate') ?? '', 'Basic')
    await res.text()
  })

  t.test('returns 401 with wrong password', { tags: ['auth'] }, async () => {
    const res = await dav(t.baseUrl, 'PROPFIND', '/webdav/', { auth: badAuth, headers: { Depth: '0' } })
    t.assert.equal(res.status, 401)
  })

  t.test('authenticates with correct credentials', { tags: ['auth'] }, async () => {
    const res = await dav(t.baseUrl, 'PROPFIND', '/webdav/', { auth: basicAuth, headers: { Depth: '0' } })
    t.assert.equal(res.status, 207)
  })

  // --- PROPFIND ---

  t.test('PROPFIND root with depth 0 returns collection', { tags: ['propfind'] }, async () => {
    const res = await dav(t.baseUrl, 'PROPFIND', '/webdav/', { auth: basicAuth, headers: { Depth: '0' } })
    t.assert.equal(res.status, 207)
    t.assert.contains(res.headers['content-type'] ?? '', 'application/xml')
    t.assert.contains(res.body, '<D:multistatus')
    t.assert.contains(res.body, '<D:collection/')
  })

  t.test('PROPFIND root with depth 1 lists folders', { tags: ['propfind'] }, async () => {
    // Create a folder first
    await dav(t.baseUrl, 'MKCOL', '/webdav/PropfindTest', { auth: basicAuth })

    const res = await dav(t.baseUrl, 'PROPFIND', '/webdav/', { auth: basicAuth, headers: { Depth: '1' } })
    t.assert.equal(res.status, 207)
    t.assert.contains(res.body, 'PropfindTest')
  })

  t.test('PROPFIND on folder lists files', { tags: ['propfind'] }, async () => {
    // Create folder and upload a file
    await dav(t.baseUrl, 'MKCOL', '/webdav/PropfindFiles', { auth: basicAuth })
    await dav(t.baseUrl, 'PUT', '/webdav/PropfindFiles/readme.txt', {
      auth: basicAuth,
      headers: { 'Content-Type': 'text/plain' },
      body: 'hello',
    })

    const res = await dav(t.baseUrl, 'PROPFIND', '/webdav/PropfindFiles', {
      auth: basicAuth,
      headers: { Depth: '1' },
    })
    t.assert.equal(res.status, 207)
    t.assert.contains(res.body, 'readme.txt')
    t.assert.contains(res.body, 'text/plain')
  })

  // --- MKCOL ---

  t.test('MKCOL creates a folder', { tags: ['mkcol'] }, async () => {
    const res = await dav(t.baseUrl, 'MKCOL', '/webdav/NewFolder', { auth: basicAuth })
    t.assert.equal(res.status, 201)

    // Verify it shows in listing
    const listing = await dav(t.baseUrl, 'PROPFIND', '/webdav/', { auth: basicAuth, headers: { Depth: '1' } })
    t.assert.contains(listing.body, 'NewFolder')
  })

  t.test('MKCOL creates nested folder', { tags: ['mkcol'] }, async () => {
    await dav(t.baseUrl, 'MKCOL', '/webdav/ParentDir', { auth: basicAuth })
    const res = await dav(t.baseUrl, 'MKCOL', '/webdav/ParentDir/ChildDir', { auth: basicAuth })
    t.assert.equal(res.status, 201)

    const listing = await dav(t.baseUrl, 'PROPFIND', '/webdav/ParentDir', {
      auth: basicAuth,
      headers: { Depth: '1' },
    })
    t.assert.contains(listing.body, 'ChildDir')
  })

  t.test('MKCOL returns 405 if folder already exists', { tags: ['mkcol'] }, async () => {
    await dav(t.baseUrl, 'MKCOL', '/webdav/DuplicateDir', { auth: basicAuth })
    const res = await dav(t.baseUrl, 'MKCOL', '/webdav/DuplicateDir', { auth: basicAuth })
    t.assert.equal(res.status, 405)
  })

  // --- PUT / GET / DELETE ---

  t.test('PUT uploads a file, GET retrieves it', { tags: ['put', 'get'] }, async () => {
    await dav(t.baseUrl, 'MKCOL', '/webdav/Uploads', { auth: basicAuth })

    const content = 'Hello, WebDAV!'
    const putRes = await dav(t.baseUrl, 'PUT', '/webdav/Uploads/test.txt', {
      auth: basicAuth,
      headers: { 'Content-Type': 'text/plain' },
      body: content,
    })
    t.assert.equal(putRes.status, 201)
    t.assert.ok(putRes.headers['etag'])

    const getRes = await dav(t.baseUrl, 'GET', '/webdav/Uploads/test.txt', { auth: basicAuth })
    t.assert.equal(getRes.status, 200)
    t.assert.equal(getRes.body, content)
    t.assert.contains(getRes.headers['content-type'] ?? '', 'text/plain')
  })

  t.test('PUT overwrites an existing file', { tags: ['put'] }, async () => {
    await dav(t.baseUrl, 'MKCOL', '/webdav/Overwrite', { auth: basicAuth })

    await dav(t.baseUrl, 'PUT', '/webdav/Overwrite/doc.txt', {
      auth: basicAuth,
      headers: { 'Content-Type': 'text/plain' },
      body: 'version 1',
    })

    const putRes = await dav(t.baseUrl, 'PUT', '/webdav/Overwrite/doc.txt', {
      auth: basicAuth,
      headers: { 'Content-Type': 'text/plain' },
      body: 'version 2',
    })
    t.assert.equal(putRes.status, 204)

    const getRes = await dav(t.baseUrl, 'GET', '/webdav/Overwrite/doc.txt', { auth: basicAuth })
    t.assert.equal(getRes.body, 'version 2')
  })

  t.test('DELETE removes a file', { tags: ['delete'] }, async () => {
    await dav(t.baseUrl, 'MKCOL', '/webdav/DeleteFile', { auth: basicAuth })
    await dav(t.baseUrl, 'PUT', '/webdav/DeleteFile/gone.txt', {
      auth: basicAuth,
      headers: { 'Content-Type': 'text/plain' },
      body: 'delete me',
    })

    const delRes = await dav(t.baseUrl, 'DELETE', '/webdav/DeleteFile/gone.txt', { auth: basicAuth })
    t.assert.equal(delRes.status, 204)

    const getRes = await dav(t.baseUrl, 'GET', '/webdav/DeleteFile/gone.txt', { auth: basicAuth })
    t.assert.equal(getRes.status, 404)
  })

  t.test('DELETE removes a folder', { tags: ['delete'] }, async () => {
    await dav(t.baseUrl, 'MKCOL', '/webdav/DeleteFolder', { auth: basicAuth })
    const res = await dav(t.baseUrl, 'DELETE', '/webdav/DeleteFolder', { auth: basicAuth })
    t.assert.equal(res.status, 204)

    const listing = await dav(t.baseUrl, 'PROPFIND', '/webdav/', { auth: basicAuth, headers: { Depth: '1' } })
    // Should not contain the deleted folder
    // Note: other test folders may exist, just check this one is gone
    // We can't easily assert "not contains" without adding it to assert, so verify with GET
    const getRes = await dav(t.baseUrl, 'PROPFIND', '/webdav/DeleteFolder', { auth: basicAuth, headers: { Depth: '0' } })
    t.assert.equal(getRes.status, 404)
  })

  // --- LOCK / UNLOCK ---

  t.test('LOCK returns a lock token', { tags: ['lock'] }, async () => {
    const res = await dav(t.baseUrl, 'LOCK', '/webdav/locktest', { auth: basicAuth })
    t.assert.equal(res.status, 200)
    t.assert.contains(res.headers['lock-token'] ?? '', 'opaquelocktoken:')
    t.assert.contains(res.body, '<D:lockdiscovery')
  })

  t.test('UNLOCK returns 204', { tags: ['lock'] }, async () => {
    const lockRes = await dav(t.baseUrl, 'LOCK', '/webdav/unlocktest', { auth: basicAuth })
    const lockToken = lockRes.headers['lock-token'] ?? ''

    const res = await dav(t.baseUrl, 'UNLOCK', '/webdav/unlocktest', {
      auth: basicAuth,
      headers: { 'Lock-Token': lockToken },
    })
    t.assert.equal(res.status, 204)
  })

  // --- COPY ---

  t.test('COPY duplicates a file to another folder', { tags: ['copy'] }, async () => {
    await dav(t.baseUrl, 'MKCOL', '/webdav/CopySrc', { auth: basicAuth })
    await dav(t.baseUrl, 'MKCOL', '/webdav/CopyDst', { auth: basicAuth })
    await dav(t.baseUrl, 'PUT', '/webdav/CopySrc/original.txt', {
      auth: basicAuth,
      headers: { 'Content-Type': 'text/plain' },
      body: 'copy me',
    })

    const res = await dav(t.baseUrl, 'COPY', '/webdav/CopySrc/original.txt', {
      auth: basicAuth,
      headers: { Destination: `${t.baseUrl}/webdav/CopyDst/copied.txt` },
    })
    t.assert.equal(res.status, 201)

    // Verify copy exists
    const getRes = await dav(t.baseUrl, 'GET', '/webdav/CopyDst/copied.txt', { auth: basicAuth })
    t.assert.equal(getRes.status, 200)
    t.assert.equal(getRes.body, 'copy me')

    // Verify original still exists
    const origRes = await dav(t.baseUrl, 'GET', '/webdav/CopySrc/original.txt', { auth: basicAuth })
    t.assert.equal(origRes.status, 200)
  })

  // --- MOVE ---

  t.test('MOVE moves a file to another folder', { tags: ['move'] }, async () => {
    await dav(t.baseUrl, 'MKCOL', '/webdav/MoveSrc', { auth: basicAuth })
    await dav(t.baseUrl, 'MKCOL', '/webdav/MoveDst', { auth: basicAuth })
    await dav(t.baseUrl, 'PUT', '/webdav/MoveSrc/moveme.txt', {
      auth: basicAuth,
      headers: { 'Content-Type': 'text/plain' },
      body: 'move me',
    })

    const res = await dav(t.baseUrl, 'MOVE', '/webdav/MoveSrc/moveme.txt', {
      auth: basicAuth,
      headers: { Destination: `${t.baseUrl}/webdav/MoveDst/moved.txt` },
    })
    t.assert.equal(res.status, 201)

    // Verify moved file exists at destination
    const getRes = await dav(t.baseUrl, 'GET', '/webdav/MoveDst/moved.txt', { auth: basicAuth })
    t.assert.equal(getRes.status, 200)
    t.assert.equal(getRes.body, 'move me')

    // Verify original is gone
    const origRes = await dav(t.baseUrl, 'GET', '/webdav/MoveSrc/moveme.txt', { auth: basicAuth })
    t.assert.equal(origRes.status, 404)
  })

  // --- HEAD ---

  t.test('HEAD returns correct headers for a file', { tags: ['head'] }, async () => {
    await dav(t.baseUrl, 'MKCOL', '/webdav/HeadTest', { auth: basicAuth })
    await dav(t.baseUrl, 'PUT', '/webdav/HeadTest/info.txt', {
      auth: basicAuth,
      headers: { 'Content-Type': 'text/plain' },
      body: 'head check',
    })

    const res = await dav(t.baseUrl, 'HEAD', '/webdav/HeadTest/info.txt', { auth: basicAuth })
    t.assert.equal(res.status, 200)
    t.assert.contains(res.headers['content-type'] ?? '', 'text/plain')
    t.assert.ok(res.headers['etag'])
  })

  // --- 404 cases ---

  t.test('GET returns 404 for nonexistent file', { tags: ['get'] }, async () => {
    const res = await dav(t.baseUrl, 'GET', '/webdav/NoSuchFolder/nofile.txt', { auth: basicAuth })
    t.assert.equal(res.status, 404)
  })

  t.test('PUT returns 409 when parent folder missing', { tags: ['put'] }, async () => {
    const res = await dav(t.baseUrl, 'PUT', '/webdav/NonExistent/file.txt', {
      auth: basicAuth,
      headers: { 'Content-Type': 'text/plain' },
      body: 'orphan',
    })
    t.assert.equal(res.status, 409)
  })
})
