import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"

const mutationMock = vi.fn()
const deleteManyMock = vi.fn()

vi.mock("convex/browser", () => {
  const ConvexHttpClient = vi.fn(function ConvexHttpClientMock() {
    return {
      mutation: mutationMock,
    }
  })

  return {
    ConvexHttpClient,
  }
})

vi.mock("@/lib/prisma", () => ({
  prisma: {
    authUser: {
      deleteMany: deleteManyMock,
    },
  },
}))

type AdminSessionForTests = {
  user: {
    email: string
    name: string
    role: string
    tenantId: string | null
    avatarUrl: string | null
  }
} | null

type AssertAdminSessionForTests = (fn: (() => Promise<AdminSessionForTests>) | null) => void

describe("POST /api/admin/devices/delete", () => {
  const originalEnv = process.env.NEXT_PUBLIC_CONVEX_URL

  let restoreConsole: (() => void) | undefined

  beforeEach(async () => {
    process.env.NEXT_PUBLIC_CONVEX_URL = "https://convex.example"
    mutationMock.mockReset()
    deleteManyMock.mockReset()
    mutationMock.mockImplementation(async (_ctx, payload) => {
      if (payload && typeof payload === "object" && "machineId" in payload) {
        return { ok: true }
      }
      return { _id: "user_123" }
    })
    const routeModule = (await import("./route")) as unknown as {
      __setAssertAdminSessionForTests: AssertAdminSessionForTests
    }

    routeModule.__setAssertAdminSessionForTests(async () => ({
      user: {
        email: "admin@example.com",
        name: "Admin",
        role: "ADMIN",
        tenantId: "tenant-1",
        avatarUrl: null,
      },
    }))
    const consoleSpy = vi.spyOn(console, "error").mockImplementation(function noop() {})
    restoreConsole = () => consoleSpy.mockRestore()
  })

  afterEach(() => {
    process.env.NEXT_PUBLIC_CONVEX_URL = originalEnv
    restoreConsole?.()
  })

  it("returns ok when the machine removal succeeds", async () => {
    const { POST } = await import("./route")
    const response = await POST(
      new Request("http://localhost/api/admin/devices/delete", {
        method: "POST",
        body: JSON.stringify({ machineId: "jn_machine" }),
      })
    )

    expect(response.status).toBe(200)
    await expect(response.json()).resolves.toEqual({ ok: true, machineMissing: false })
    expect(deleteManyMock).toHaveBeenCalledWith({ where: { email: "machine-jn_machine@machines.local" } })
    expect(mutationMock).toHaveBeenNthCalledWith(1, expect.anything(), expect.objectContaining({ email: "admin@example.com" }))
    expect(mutationMock).toHaveBeenNthCalledWith(2, expect.anything(), expect.objectContaining({ machineId: "jn_machine" }))
  })

  it("still succeeds when the Convex machine is already missing", async () => {
    mutationMock.mockImplementation(async (_ctx, payload) => {
      if (payload && typeof payload === "object" && "machineId" in payload) {
        throw new Error("Dispositivo não encontrada")
      }
      return { _id: "user_123" }
    })
    const { POST } = await import("./route")
    const response = await POST(
      new Request("http://localhost/api/admin/devices/delete", {
        method: "POST",
        body: JSON.stringify({ machineId: "jn_machine" }),
      })
    )

    expect(response.status).toBe(200)
    await expect(response.json()).resolves.toEqual({ ok: true, machineMissing: true })
    expect(deleteManyMock).toHaveBeenCalledWith({ where: { email: "machine-jn_machine@machines.local" } })
  })

  it("returns an error for other Convex failures", async () => {
    mutationMock.mockImplementation(async (_ctx, payload) => {
      if (payload && typeof payload === "object" && "machineId" in payload) {
        throw new Error("timeout error")
      }
      return { _id: "user_123" }
    })
    const { POST } = await import("./route")
    const response = await POST(
      new Request("http://localhost/api/admin/devices/delete", {
        method: "POST",
        body: JSON.stringify({ machineId: "jn_machine" }),
      })
    )

    expect(response.status).toBe(500)
    await expect(response.json()).resolves.toEqual({ error: "Falha ao remover dispositivo no Convex" })
    expect(deleteManyMock).not.toHaveBeenCalled()
  })
})
