import {mkdir, rm, writeFile} from 'node:fs/promises'
import {dirname, join} from 'node:path'

import {S3Client} from 'bun'

import {env} from '../env'

// public, immutable object storage for processed media (avatars).
// keys are content-addressed, so urls change when content changes.
export type BlobStore = {
  put: (
    key: string,
    bytes: Uint8Array,
    contentType: string,
  ) => Promise<void>
  delete: (key: string) => Promise<void>
  publicUrl: (key: string) => string
}

const stripTrailingSlash = (s: string) => s.replace(/\/+$/, '')

const r2Store = (): BlobStore => {
  const client = new S3Client({
    endpoint: env.R2_ENDPOINT,
    accessKeyId: env.R2_ACCESS_KEY_ID,
    secretAccessKey: env.R2_SECRET_ACCESS_KEY,
    bucket: env.R2_BUCKET,
    region: 'auto',
  })
  const base = stripTrailingSlash(env.R2_PUBLIC_BASE_URL!)
  return {
    put: async (key, bytes, contentType) => {
      await client.write(key, bytes, {type: contentType})
    },
    delete: async (key) => {
      await client.delete(key)
    },
    publicUrl: (key) => `${base}/${key}`,
  }
}

// dev/test fallback: write under UPLOADS_DIR, served by hono at /static
const diskStore = (): BlobStore => {
  const root = env.UPLOADS_DIR
  const base = `${stripTrailingSlash(env.PUBLIC_API_URL)}/static`
  return {
    put: async (key, bytes) => {
      const path = join(root, key)
      await mkdir(dirname(path), {recursive: true})
      await writeFile(path, bytes)
    },
    delete: async (key) => {
      await rm(join(root, key), {force: true})
    },
    publicUrl: (key) => `${base}/${key}`,
  }
}

export const blobStore: BlobStore =
  env.STORAGE_DRIVER === 'r2' ? r2Store() : diskStore()
