import { expect, test } from "bun:test"
import fs from "node:fs/promises"
import os from "node:os"
import path from "node:path"
import { OPENCODE_VERSION } from "../src/version"
import { writeExport } from "../src/commands/handlers/export"

const info = {
  id: "ses_export_test",
  projectID: "global",
  cost: 0,
  tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
  time: { created: 1, updated: 2 },
  title: "Exported session",
  location: { directory: "/project" },
}
const transfer = {
  info,
  messages: [
    { id: "msg_first", type: "user", text: "First", time: { created: 1 } },
    { id: "msg_second", type: "user", text: "Second", time: { created: 2 } },
  ],
}
const sanitizedTransfer = {
  info: {
    ...info,
    title: "[redacted:session-title:ses_export_test]",
    location: { directory: "/[redacted:session-directory:ses_export_test]" },
  },
  messages: [
    {
      id: "msg_first",
      type: "user",
      text: "[redacted:text:msg_first]",
      time: { created: 1 },
    },
    {
      id: "msg_second",
      type: "user",
      text: "[redacted:text:msg_second]",
      time: { created: 2 },
    },
  ],
}

const health = () => Response.json({ healthy: true, version: OPENCODE_VERSION, pid: process.pid })

function run(args: string[], stdin?: string) {
  const child = Bun.spawn([process.execPath, "run", "src/index.ts", ...args], {
    cwd: path.join(import.meta.dir, ".."),
    stdin: stdin === undefined ? undefined : new Blob([stdin]),
    stdout: "pipe",
    stderr: "pipe",
  })
  return Promise.all([new Response(child.stdout).text(), new Response(child.stderr).text(), child.exited])
}

test("export is raw by default and supports explicit sanitization", async () => {
  const sanitization: string[] = []
  const server = Bun.serve({
    port: 0,
    fetch(request) {
      const url = new URL(request.url)
      if (url.pathname === "/api/health") return health()
      if (url.pathname === `/api/session/${info.id}`) return Response.json({ data: info })
      if (url.pathname === `/api/session/${info.id}/export`) {
        sanitization.push(url.searchParams.get("sanitize") ?? "")
        return Response.json({ data: url.searchParams.get("sanitize") === "true" ? sanitizedTransfer : transfer })
      }
      return new Response("Not found", { status: 404 })
    },
  })

  try {
    const [stdout, , exitCode] = await run(["export", "-s", info.id, "--server", server.url.toString()])
    const exported = JSON.parse(stdout)

    expect(exitCode).toBe(0)
    expect(exported).toEqual(transfer)

    const [sanitized, , sanitizedExitCode] = await run([
      "export",
      "-s",
      info.id,
      "--sanitize",
      "--server",
      server.url.toString(),
    ])
    expect(sanitizedExitCode).toBe(0)
    expect(JSON.parse(sanitized)).toEqual(sanitizedTransfer)
    expect(sanitization).toEqual(["false", "true"])
  } finally {
    await server.stop(true)
  }
}, 15_000)

test("export reports an empty session list without a stack trace", async () => {
  const server = Bun.serve({
    port: 0,
    fetch(request) {
      const url = new URL(request.url)
      if (url.pathname === "/api/health") return health()
      if (url.pathname === "/api/location") {
        return Response.json({
          directory: "/project",
          project: { id: "global", directory: "/project", canonical: "/project" },
        })
      }
      if (url.pathname === "/api/session") return Response.json({ data: [], cursor: {} })
      return new Response("Not found", { status: 404 })
    },
  })

  try {
    const [stdout, stderr, exitCode] = await run(["export", "--server", server.url.toString()])

    expect(exitCode).toBe(0)
    expect(stdout).toBe("")
    expect(stderr).toBe(`No sessions found${os.EOL}`)
  } finally {
    await server.stop(true)
  }
})

test("interactive export writes a temporary JSON file", async () => {
  const output = await writeExport(transfer, info.id, false)
  const file = output.trim()

  try {
    expect(path.dirname(file)).toBe(os.tmpdir())
    expect(await Bun.file(file).json()).toEqual(transfer)
  } finally {
    await fs.rm(file, { force: true })
  }
})

test("import validates a file and sends it to the resolved location", async () => {
  const root = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-import-"))
  const file = path.join(root, "session.json")
  await fs.writeFile(file, JSON.stringify(transfer))
  let imported: unknown
  const server = Bun.serve({
    port: 0,
    async fetch(request) {
      const url = new URL(request.url)
      if (url.pathname === "/api/health") return health()
      if (url.pathname === "/api/location") {
        return Response.json({
          directory: root,
          project: { id: "global", directory: root, canonical: root },
        })
      }
      if (url.pathname === "/api/session/import") {
        imported = await request.json()
        return Response.json({ data: { ...info, location: { directory: root } } })
      }
      return new Response("Not found", { status: 404 })
    },
  })

  try {
    const [stdout, , exitCode] = await run([
      "import",
      file,
      "--directory",
      root,
      "--server",
      server.url.toString(),
    ])

    expect(exitCode).toBe(0)
    expect(stdout).toBe(`Imported session: ${info.id}${os.EOL}`)
    expect(imported).toEqual({ ...transfer, location: { directory: root } })
  } finally {
    await server.stop(true)
    await fs.rm(root, { recursive: true, force: true })
  }
})

test("import reports an existing session without a stack trace", async () => {
  const root = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-import-conflict-"))
  const file = path.join(root, "session.json")
  await fs.writeFile(file, JSON.stringify(transfer))
  const server = Bun.serve({
    port: 0,
    fetch(request) {
      const url = new URL(request.url)
      if (url.pathname === "/api/health") return health()
      if (url.pathname === "/api/location") {
        return Response.json({
          directory: root,
          project: { id: "global", directory: root, canonical: root },
        })
      }
      if (url.pathname === "/api/session/import") return new Response("Conflict", { status: 409 })
      return new Response("Not found", { status: 404 })
    },
  })

  try {
    const [stdout, stderr, exitCode] = await run(["import", file, "--server", server.url.toString()])

    expect(exitCode).toBe(0)
    expect(stdout).toBe("")
    expect(stderr).toBe(`Session already exists${os.EOL}`)
  } finally {
    await server.stop(true)
    await fs.rm(root, { recursive: true, force: true })
  }
})
