require("async.test")

local Promise = require("promise") -- replace with your actual module path
local Timers = require("timers") -- replace with your actual module path

describe("Promise.race", function()
  async_it("resolves as soon as the first promise resolves", function(done)
    local promise1 = Promise:new(function(resolve)
      vim.defer_fn(function()
        resolve("First")
      end, 100)
    end)

    local promise2 = Promise:new(function(resolve)
      vim.defer_fn(function()
        resolve("Second")
      end, 50)
    end)

    Promise.race({ promise1, promise2 })
      :and_then(function(result)
        assert.are.equal("Second", result)
        done()
      end)
      :catch(function()
        error("Promise.race should not reject when one promise resolves.")
      end)
  end)

  async_it("rejects as soon as the first promise rejects", function(done)
    local promise1 = Promise:new(function(_, reject)
      vim.defer_fn(function()
        reject("First Error")
      end, 100)
    end)
    local promise2 = Promise:new(function(_, reject)
      vim.defer_fn(function()
        reject("Second Error")
      end, 50)
    end)

    Promise.race({ promise1, promise2 })
      :and_then(function()
        error("Promise.race should not resolve when one promise rejects.")
      end)
      :catch(function(reason)
        assert.are.equal("Second Error", reason)
        done()
      end)
  end)

  async_it("resolves immediately if the first item is not a promise", function(done)
    local nonPromise = "Immediate Value"
    local promise = Promise:new(function(resolve)
      vim.defer_fn(function()
        resolve("Delayed Value")
      end, 100)
    end)

    Promise.race({ nonPromise, promise })
      :and_then(function(result)
        assert.are.equal("Immediate Value", result)
        done()
      end)
      :catch(function()
        error("Promise.race should not reject if the first item is not a promise.")
      end)
  end)

  async_it("rejects immediately if the first item is a rejected promise", function(done)
    local promise = Promise:new(function(_, reject)
      vim.defer_fn(function()
        reject("Immediate Error")
      end, 100)
    end)

    Promise.race({ promise })
      :and_then(function()
        error("Promise.race should not resolve if the first item is a rejected promise.")
      end)
      :catch(function(reason)
        assert.are.equal("Immediate Error", reason)
        done()
      end)
  end)

  async_it("handles an empty array correctly, promise should be forever pending", function(done)
    Promise.race({})
      :and_then(function()
        assert.True(false, "code should not reach here")
      end)
      :catch(function()
        assert.True(false, "code should not reach here")
      end)

    Timers.set_timeout(function()
      done()
    end, 100)
  end)

  async_it("handles mixed promises and non-promises", function(done)
    local promise1 = Promise:new(function(resolve)
      vim.defer_fn(function()
        resolve("From Promise 1")
      end, 200)
    end)

    local promise2 = Promise:new(function(_, reject)
      vim.defer_fn(function()
        reject("From Promise 2")
      end, 100)
    end)

    local nonPromise = "Immediate Value"

    Promise.race({ nonPromise, promise1, promise2 })
      :and_then(function(result)
        assert.are.equal("Immediate Value", result)
        done()
      end)
      :catch(function()
        error("Promise.race should not reject if the first item is not a promise.")
      end)
  end)

  async_it("handles nested promises correctly", function(done)
    local innerPromise = Promise:new(function(resolve)
      vim.defer_fn(function()
        resolve("Nested Resolved")
      end, 100)
    end)

    local promise1 = Promise:new(function(resolve)
      resolve(innerPromise)
    end)

    local promise2 = Promise:new(function(_, reject)
      vim.defer_fn(function()
        reject("Nested Rejection")
      end, 50)
    end)

    Promise.race({ promise1, promise2 })
      :and_then(function(result)
        error("Promise.race should not proceed if the second promise rejects first.")
      end)
      :catch(function(result)
        assert.are.equal("Nested Rejection", result)
        done()
      end)
  end)

  async_it("handles large arrays with promises", function(done)
    local numPromises = 100
    local promises = {}
    for i = 1, numPromises do
      promises[i] = Promise:new(function(resolve)
        vim.defer_fn(function()
          resolve(i)
        end, 100)
      end)
    end

    Promise.race(promises)
      :and_then(function(result)
        assert.are.equal(1, result)
        done()
      end)
      :catch(function()
        error("Promise.race should not reject with a large array of promises.")
      end)
  end)
end)
