local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Packages = ReplicatedStorage.Packages
local DevPackages = ReplicatedStorage.DevPackages

local React = require(Packages.React)
local ReactTestRenderer = require(DevPackages.ReactTestRenderer)

local JestGlobals = require(DevPackages.JestGlobals)
local it = JestGlobals.it
local expect = JestGlobals.expect

local Freeze = require(script.Parent.Parent.ReactFreeze).Freeze

local e = React.createElement
local useState = React.useState
local useEffect = React.useEffect

local create = ReactTestRenderer.create
local act = ReactTestRenderer.act

it("renders stuff not frozen", function()
	local function Content()
		return e("Frame")
	end
	local function A()
		return e(Freeze, {
			freeze = false,
		}, {
			Content = e(Content),
		})
	end

	local testRenderer = create(e(A))
	local testInstance = testRenderer.root
	expect(testInstance:findByType(Content)).toBeTruthy()
end)

it("does not render stuff when frozen", function()
	local function Content()
		return e("Frame")
	end
	local function A()
		return e(Freeze, {
			freeze = true,
		}, {
			Content = e(Content),
		})
	end

	local testRenderer = create(e(A))
	local testInstance = testRenderer.root
	expect(testInstance:findAllByType(Content)).toHaveLength(0)
end)

it("removes stuff after freeze", function()
	local function Content()
		return e("Frame")
	end
	local function A(props: { freeze: boolean })
		return e(Freeze, {
			freeze = props.freeze,
		}, {
			Content = e(Content),
		})
	end

	local testRenderer
	act(function()
		testRenderer = create(e(A, { freeze = false }))
	end)
	local testInstance = testRenderer.root
	expect(testInstance:findByType(Content)).toBeTruthy()
	act(function()
		testRenderer.update(e(A, { freeze = true }))
	end)
	expect(testRenderer.toJSON()).toBe(nil)
end)

it("updates work when not frozen", function()
	local subscription
	local function Inner(_props: { value: number })
		return e(React.Fragment)
	end
	local renderCount = 0
	local function Subscriber()
		local value, setValue = useState(0)
		useEffect(function()
			subscription = setValue
		end, {})
		renderCount += 1
		return e(Inner, { value = value })
	end
	local function Container(props: { freeze: boolean })
		return e(Freeze, {
			freeze = props.freeze,
		}, {
			Subscriber = e(Subscriber),
		})
	end

	local testRenderer
	act(function()
		testRenderer = create(e(Container, { freeze = false }))
	end)
	local testInstance = testRenderer.root
	expect(testInstance:findByType(Inner).props.value).toBe(0)
	act(function()
		subscription(1)
	end)
	expect(testInstance:findByType(Inner).props.value).toBe(1)
	expect(renderCount).toBe(2)
end)

it("does not propagate updates when frozen", function()
	local subscription
	local function Inner(_props: { value: number })
		return e(React.Fragment)
	end
	local renderCount = 0
	local function Subscriber()
		local value, setValue = useState(0)
		useEffect(function()
			subscription = setValue
		end, {})
		renderCount += 1
		return e(Inner, { value = value })
	end
	local function Container(props: { freeze: boolean })
		return e(Freeze, {
			freeze = props.freeze,
		}, {
			Subscriber = e(Subscriber),
		})
	end

	local testRenderer
	act(function()
		testRenderer = create(e(Container, { freeze = false }))
	end)
	local testInstance = testRenderer.root
	expect(testInstance:findByType(Inner).props.value).toBe(0)
	act(function()
		testRenderer.update(e(Container, { freeze = true }))
	end)
	act(function()
		subscription(1)
	end)
	expect(testInstance:findByType(Inner).props.value).toBe(0)
	expect(renderCount).toBe(1)
end)

it("persists state after defrost", function()
	local subscription
	local function Inner(_props: { value: number })
		return e("Frame")
	end
	local function Subscriber()
		local value, setValue = useState(0)
		useEffect(function()
			subscription = setValue
		end, {})
		return e(Inner, { value = value })
	end
	local function Container(props: { freeze: boolean })
		return e(Freeze, {
			freeze = props.freeze,
		}, {
			Subscriber = e(Subscriber),
		})
	end

	local testRenderer
	act(function()
		testRenderer = create(e(Container, { freeze = false }))
	end)
	local testInstance = testRenderer.root
	expect(testInstance:findByType(Inner).props.value).toBe(0)
	act(function()
		subscription(1)
	end)
	expect(testInstance:findByType(Inner).props.value).toBe(1)
	act(function()
		testRenderer.update(e(Container, { freeze = true }))
	end)
	expect(testRenderer.toJSON()).toBe(nil)
	act(function()
		testRenderer.update(e(Container, { freeze = false }))
	end)
	expect(testRenderer.toJSON().type).toBe("Frame")
	expect(testInstance:findByType(Inner).props.value).toBe(1)
end)

it("propagates updates after defrost", function()
	local subscription
	local function Inner(_props: { value: number })
		return e("Frame")
	end
	local renderCount = 0
	local function Subscriber()
		local value, setValue = useState(0)
		useEffect(function()
			subscription = setValue
		end, {})
		renderCount += 1
		return e(Inner, { value = value })
	end
	local function Container(props: { freeze: boolean })
		return e(Freeze, {
			freeze = props.freeze,
		}, {
			Subscriber = e(Subscriber),
		})
	end

	local testRenderer
	act(function()
		testRenderer = create(e(Container, { freeze = false }))
	end)
	local testInstance = testRenderer.root
	act(function()
		testRenderer.update(e(Container, { freeze = true }))
	end)
	act(function()
		subscription(1)
	end)
	act(function()
		subscription(2)
	end)
	act(function()
		subscription(3)
	end)
	expect(testInstance:findByType(Inner).props.value).toBe(0)
	act(function()
		testRenderer.update(e(Container, { freeze = false }))
	end)
	expect(testInstance:findByType(Inner).props.value).toBe(3)
	expect(renderCount).toBe(2)
end)
