import { afterEach, describe, expect, test } from "bun:test"
import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs"
import { tmpdir } from "node:os"
import path from "node:path"
import { persistProjectUpload, deleteProjectUpload } from "./uploads.adapter"

// A real 1×1 transparent PNG so file-type detects image/png from the buffer.
const PNG_BYTES = new Uint8Array(
  Buffer.from(
    "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+M8AAAMBAQDJ/pLvAAAAAElFTkSuQmCC",
    "base64",
  ),
)

describe("persistProjectUpload", () => {
  let dir = ""
  afterEach(() => {
    if (dir) rmSync(dir, { recursive: true, force: true })
    dir = ""
  })

  test("writes to .airaknit/uploads with image kind from detected mime", async () => {
    dir = mkdtempSync(path.join(tmpdir(), "airaknit-uploads-"))
    const att = await persistProjectUpload({ projectId: "w1", localPath: dir, fileName: "shot.png", bytes: PNG_BYTES })

    expect(att.relativePath).toBe("./.airaknit/uploads/shot.png")
    expect(att.kind).toBe("image")
    expect(att.mimeType).toBe("image/png")
    expect(att.size).toBe(PNG_BYTES.byteLength)
    expect(existsSync(path.join(dir, ".airaknit", "uploads", "shot.png"))).toBe(true)
  })

  test("writes a .airaknit/.gitignore so uploads stay out of the user's repo", async () => {
    dir = mkdtempSync(path.join(tmpdir(), "airaknit-uploads-"))
    await persistProjectUpload({ projectId: "w1", localPath: dir, fileName: "a.txt", bytes: Buffer.from("x") })
    const ignorePath = path.join(dir, ".airaknit", ".gitignore")
    expect(existsSync(ignorePath)).toBe(true)
    expect(readFileSync(ignorePath, "utf8")).toBe("*\n")
  })

  test("non-image falls back to file kind", async () => {
    dir = mkdtempSync(path.join(tmpdir(), "airaknit-uploads-"))
    const att = await persistProjectUpload({ projectId: "w1", localPath: dir, fileName: "notes.txt", bytes: Buffer.from("hello") })
    expect(att.kind).toBe("file")
    expect(att.relativePath).toBe("./.airaknit/uploads/notes.txt")
  })

  test("name collision appends a counter", async () => {
    dir = mkdtempSync(path.join(tmpdir(), "airaknit-uploads-"))
    const bytes = Buffer.from("hello")
    const a = await persistProjectUpload({ projectId: "w1", localPath: dir, fileName: "notes.txt", bytes })
    const b = await persistProjectUpload({ projectId: "w1", localPath: dir, fileName: "notes.txt", bytes })
    expect(a.relativePath).toBe("./.airaknit/uploads/notes.txt")
    expect(b.relativePath).toBe("./.airaknit/uploads/notes-1.txt")
  })

  test("deleteProjectUpload removes the file and rejects path traversal", async () => {
    dir = mkdtempSync(path.join(tmpdir(), "airaknit-uploads-"))
    const att = await persistProjectUpload({ projectId: "w1", localPath: dir, fileName: "a.txt", bytes: Buffer.from("x") })
    const storedName = att.relativePath.split("/").pop() as string

    expect(await deleteProjectUpload({ localPath: dir, storedName: "../escape" })).toBe(false)
    expect(await deleteProjectUpload({ localPath: dir, storedName })).toBe(true)
    expect(existsSync(att.absolutePath)).toBe(false)
  })
})
