-- Compiled with roblox-ts v2.3.0
local TS = _G[script]
local createAiComponent = TS.import(script, script.Parent, "ai", "component").createAiComponent
local create
local function computeFieldValue(state, field, instanceCache, dependencies)
	local kind = field[1]
	if kind == "variable" then
		local _dependencies = dependencies
		local _arg0 = field[2]
		_dependencies[_arg0] = true
		return state[field[2]]
	elseif kind == "udim2" then
		return UDim2.new(computeFieldValue(state, field[2], instanceCache, dependencies), computeFieldValue(state, field[3], instanceCache, dependencies), computeFieldValue(state, field[4], instanceCache, dependencies), computeFieldValue(state, field[5], instanceCache, dependencies))
	elseif kind == "udim" then
		return UDim.new(computeFieldValue(state, field[2], instanceCache, dependencies), computeFieldValue(state, field[3], instanceCache, dependencies))
	elseif kind == "enum" then
		-- This is a string since it is simplest to implement but it will get coerced into an enum.
		return field[2]
	elseif kind == "vector2" then
		return Vector2.new(computeFieldValue(state, field[2], instanceCache, dependencies), computeFieldValue(state, field[3], instanceCache, dependencies))
	elseif kind == "color3" then
		return Color3.fromRGB(computeFieldValue(state, field[2], instanceCache, dependencies), computeFieldValue(state, field[3], instanceCache, dependencies), computeFieldValue(state, field[4], instanceCache, dependencies))
	elseif kind == "string" then
		return field[2]
	elseif kind == "number" then
		return field[2]
	elseif kind == "boolean" then
		return field[2]
	elseif kind == "undefined" then
		return nil
	elseif kind == "instance" then
		local _instanceCache = instanceCache
		local _field = field
		local cached = _instanceCache[_field]
		if cached then
			return cached
		end
		local component = create(state, field[2])
		component.isNestedInstance = true
		local _instanceCache_1 = instanceCache
		local _field_1 = field
		_instanceCache_1[_field_1] = component
		return component
	elseif kind == "if" then
		if computeFieldValue(state, field[2], instanceCache, dependencies) == true then
			return computeFieldValue(state, field[3], instanceCache, dependencies)
		else
			return computeFieldValue(state, field[4], instanceCache, dependencies)
		end
	elseif kind == "eq" then
		return computeFieldValue(state, field[2], instanceCache, dependencies) == computeFieldValue(state, field[3], instanceCache, dependencies)
	elseif kind == "ai" then
		local _instanceCache = instanceCache
		local _field = field
		local cached = _instanceCache[_field]
		if cached then
			return cached
		end
		local component = createAiComponent(create, field[2])
		local _instanceCache_1 = instanceCache
		local _field_1 = field
		_instanceCache_1[_field_1] = component
		return component
	end
end
function create(defaultState, metadata)
	local instance = Instance.new(metadata[1])
	local effects = {}
	local dependencies = {}
	local effectCleanup = {}
	local signalConnections = {}
	local nestedComponents = {}
	local previouslyResolved = {}
	-- Caches the nested instances so that we don't have to recreate them on every state change.
	local instanceCache = {}
	local currentState = defaultState
	local function updateState(state)
		local previousState = currentState
		currentState = state
		for key, data in pairs(metadata[2]) do
			local knownDependencies = dependencies[key]
			-- If none of the dependencies have changed, don't compute a new value.
			if knownDependencies then
				local isChanged = false
				for dependency in knownDependencies do
					if currentState[dependency] ~= previousState[dependency] then
						isChanged = true
						break
					end
				end
				if not isChanged then
					continue
				end
			end
			local newDependencies = {}
			local value = computeFieldValue(currentState, data[2], instanceCache, newDependencies)
			dependencies[key] = newDependencies
			-- The value of this field hasn't changed, so we don't need to perform extra work.
			-- We still update the dependencies above as the path to this value *could* have changed, affecting the dependencies.
			if previouslyResolved[key] == value then
				continue
			end
			local kind = data[1]
			if kind == "signal" then
				local _result = signalConnections[key]
				if _result ~= nil then
					_result:Disconnect()
				end
				if value ~= nil then
					local connection = (instance[key]):Connect(value)
					signalConnections[key] = connection
				end
			elseif kind == "component" then
				local componentsValue = value or {}
				local components = if componentsValue.isNestedInstance ~= nil then { componentsValue } else componentsValue
				local previousComponents = nestedComponents[key] or {}
				for _, component in previousComponents do
					if not (table.find(components, component) ~= nil) then
						component.unmount()
					end
				end
				for _, component in components do
					if not (table.find(previousComponents, component) ~= nil) then
						component.mount(instance)
					end
				end
				nestedComponents[key] = components
			elseif kind == "field" then
				instance[key] = value
			end
			previouslyResolved[key] = value
		end
		-- Update the state of any children.
		for _, components in nestedComponents do
			for _, component in components do
				if component.isNestedInstance then
					component.updateState(currentState)
				end
			end
		end
	end
	local cleanup
	local function mount(parent)
		if instance.Parent == parent then
			return nil
		end
		if instance.Parent ~= nil then
			cleanup()
		end
		instance.Parent = parent
		updateState(defaultState)
		for _, effect in effects do
			local cleanup = effect()
			if cleanup then
				table.insert(effectCleanup, cleanup)
			end
		end
	end
	local function unmount()
		instance.Parent = nil
		cleanup()
	end
	function cleanup()
		for _, components in nestedComponents do
			for _, component in components do
				component.unmount()
			end
		end
		for _, cleanup in effectCleanup do
			cleanup()
		end
		for _, connection in signalConnections do
			connection:Disconnect()
		end
		table.clear(effectCleanup)
		table.clear(signalConnections)
		table.clear(nestedComponents)
		table.clear(previouslyResolved)
		table.clear(dependencies)
	end
	return {
		getInstance = function()
			return instance
		end,
		getState = function()
			return currentState
		end,
		effect = function(callback)
			local _callback = callback
			table.insert(effects, _callback)
			return #effects
		end,
		patchState = function(state)
			local _object = {}
			if currentState then
				for _k, _v in currentState do
					_object[_k] = _v
				end
			end
			for _k, _v in state do
				_object[_k] = _v
			end
			return updateState(_object)
		end,
		mount = mount,
		unmount = unmount,
		updateState = updateState,
		isNestedInstance = false,
	}
end
return {
	create = create,
}
