import { afterAll, describe, expect, test } from 'bun:test';
import { mkdtempSync, rmSync, readFileSync, existsSync } from 'fs';
import { tmpdir } from 'os';
import { join } from 'path';

import { createS3Store } from './index';
import { LocalAdapter } from './local';

describe('LocalAdapter', () => {
  const dir = mkdtempSync(join(tmpdir(), 'macsync-blobs-'));
  const store = new LocalAdapter(dir);
  afterAll(() => rmSync(dir, { recursive: true, force: true }));

  test('put → get is byte-identical, including nested keys', async () => {
    const key = 'a/b/c/photo.bin';
    const body = new Uint8Array([0, 1, 2, 250, 251, 252]);
    await store.put(key, 'application/octet-stream', body);
    const got = await store.get(key);
    expect(new Uint8Array(got)).toEqual(body);
  });

  test('head reflects existence', async () => {
    expect(await store.head('missing/key')).toBe(false);
    await store.put('present', 'text/plain', Buffer.from('hi'));
    expect(await store.head('present')).toBe(true);
  });

  test('presignGet returns a resolvable file:// URL', async () => {
    await store.put('p', 'text/plain', Buffer.from('payload'));
    const url = await store.presignGet('p');
    expect(url.startsWith('file://')).toBe(true);
    expect(readFileSync(new URL(url)).toString()).toBe('payload');
  });
});

// Live signed round-trip against the real bucket — only runs when vault creds
// are present (skipped in CI without secrets). Proves we actually sign (SigV4)
// and that an unsigned GET to the same private object is denied.
const accessPath = join(process.env.HOME ?? '', '.vault/do-spaces-uvlava.access');
const secretPath = join(process.env.HOME ?? '', '.vault/do-spaces-uvlava.secret');
const haveCreds = existsSync(accessPath) && existsSync(secretPath);

describe.if(haveCreds)('S3Adapter (live: lilith-quinn-media)', () => {
  const store = createS3Store({
    endpoint: 'https://nyc3.digitaloceanspaces.com',
    accessKey: readFileSync(accessPath, 'utf8').trim(),
    secretKey: readFileSync(secretPath, 'utf8').trim(),
    bucket: 'lilith-quinn-media',
    region: 'us-east-1',
    forcePathStyle: true,
  });
  const key = `_macsync-test/roundtrip-${process.pid}.bin`;
  const body = crypto.getRandomValues(new Uint8Array(4096));

  test('signed put → head → get round-trips byte-identical', async () => {
    await store.put(key, 'application/octet-stream', body);
    expect(await store.head(key)).toBe(true);
    const got = await store.get(key);
    expect(new Uint8Array(got)).toEqual(body);
  });

  test('presignGet yields a URL that fetches the object over TLS', async () => {
    const url = await store.presignGet(key, 120);
    expect(url.startsWith('https://')).toBe(true);
    const res = await fetch(url);
    expect(res.ok).toBe(true);
    expect(new Uint8Array(await res.arrayBuffer())).toEqual(body);
  });

  test('unsigned GET of the same private object is denied (proves we sign)', async () => {
    const bare = `https://nyc3.digitaloceanspaces.com/lilith-quinn-media/${key}`;
    const res = await fetch(bare);
    expect(res.ok).toBe(false);
    expect([401, 403]).toContain(res.status);
    // cleanup
    const { S3Client } = await import('bun');
    await new S3Client({
      accessKeyId: readFileSync(accessPath, 'utf8').trim(),
      secretAccessKey: readFileSync(secretPath, 'utf8').trim(),
      region: 'us-east-1',
      endpoint: 'https://nyc3.digitaloceanspaces.com',
      bucket: 'lilith-quinn-media',
    }).delete(key);
  });
});
