import { mkdtempSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import assert from 'node:assert/strict'
import test from 'node:test'
import { writeBufferFullySync, writeFileAtomic } from '../apps/desktop/src/main/fs-atomic.ts'

function withTempDir(fn: (dir: string) => void | Promise<void>) {
  const dir = mkdtempSync(join(tmpdir(), 'cowork-atomic-'))
  return Promise.resolve(fn(dir)).finally(() => rmSync(dir, { recursive: true, force: true }))
}

test('writeFileAtomic writes and can be read back', () => withTempDir((dir) => {
  const target = join(dir, 'settings.enc')
  writeFileAtomic(target, 'payload', { mode: 0o600 })
  assert.equal(readFileSync(target, 'utf-8'), 'payload')
}))

test('writeFileAtomic sets 0o600 perms by default', () => withTempDir((dir) => {
  const target = join(dir, 'creds.json')
  writeFileAtomic(target, 'secret')
  // Mask against ownership bits so the test is stable across platforms
  // that surface group/other bits differently. 0o777 is plenty to
  // verify the owner-only-readable promise.
  const mode = statSync(target).mode & 0o777
  assert.equal(mode, 0o600)
}))

test('writeFileAtomic replaces an existing file in place', () => withTempDir((dir) => {
  const target = join(dir, 'settings.enc')
  writeFileSync(target, 'old-content', { mode: 0o600 })
  writeFileAtomic(target, 'new-content')
  assert.equal(readFileSync(target, 'utf-8'), 'new-content')
}))

test('writeFileAtomic applies requested perms when replacing an existing file', () => withTempDir((dir) => {
  const target = join(dir, 'settings.enc')
  writeFileSync(target, 'old-content', { mode: 0o644 })
  writeFileAtomic(target, 'new-content', { mode: 0o600 })
  assert.equal(readFileSync(target, 'utf-8'), 'new-content')
  if (process.platform !== 'win32') {
    assert.equal(statSync(target).mode & 0o777, 0o600)
  }
}))

test('writeFileAtomic does not leave temp files behind after a successful write', () => withTempDir((dir) => {
  const target = join(dir, 'settings.enc')
  writeFileAtomic(target, 'ok')
  const residue = readdirSync(dir).filter((entry) => entry.startsWith('settings.enc.tmp-'))
  assert.deepEqual(residue, [])
}))

test('writeFileAtomic leaves the original file intact if the write fails midway', () => withTempDir((dir) => {
  const target = join(dir, 'settings.enc')
  writeFileSync(target, 'stable-old', { mode: 0o600 })

  // Invalid mode forces open() to throw. The helper should reject
  // before touching the real target, so the stable "old" file survives.
  assert.throws(() => {
    // @ts-expect-error intentionally passing an invalid mode to trigger failure
    writeFileAtomic(target, 'new-bytes', { mode: 'not-a-number' })
  })

  assert.equal(readFileSync(target, 'utf-8'), 'stable-old')
  // Confirm any half-created temp was cleaned up.
  const residue = readdirSync(dir).filter((entry) => entry.startsWith('settings.enc.tmp-'))
  assert.deepEqual(residue, [])
}))

test('writeBufferFullySync keeps writing after short writes', () => {
  const calls: Array<{ offset: number; length: number }> = []
  const payload = Buffer.from('abcdef')

  writeBufferFullySync(1, payload, (_fd, _buffer, offset, length) => {
    calls.push({ offset, length })
    return Math.min(length, 2)
  })

  assert.deepEqual(calls, [
    { offset: 0, length: 6 },
    { offset: 2, length: 4 },
    { offset: 4, length: 2 },
  ])
})

test('writeBufferFullySync rejects stalled writes', () => {
  assert.throws(
    () => writeBufferFullySync(1, Buffer.from('payload'), () => 0),
    /before all bytes were written/,
  )
})
