



tx := import("tx")
ll := import("@platforma-sdk/workflow-tengo:ll")
assets := import("@platforma-sdk/workflow-tengo:assets")
text := import("text")
json := import("json")

constants := import("@platforma-sdk/workflow-tengo:workflow.constants")
render := import("@platforma-sdk/workflow-tengo:render")
smart := import("@platforma-sdk/workflow-tengo:smart")
sets := import("@platforma-sdk/workflow-tengo:sets")
oop := import("@platforma-sdk/workflow-tengo:oop")
bobject := import("@platforma-sdk/workflow-tengo:workflow.bobject")
bquery := import("@platforma-sdk/workflow-tengo:workflow.bquery")
pBundle := import("@platforma-sdk/workflow-tengo:pframes.bundle")


self := import("@platforma-sdk/workflow-tengo:tpl")


_RESULTS_FIELD_NAME := "result"


_INPUT_CONTEXT_FIELD_NAME := "context"


_OUTPUT_CONTEXT_FIELD_NAME := "context"



_PREPARED_INPUTS_PREFIX := "prepared/"





self.defineOutputs([_RESULTS_FIELD_NAME, _OUTPUT_CONTEXT_FIELD_NAME])

_isProduction := func() {

	return self.rawInputs().isProduction.getValueAsJson() == true
}

_isStaging := func() {
	return !_isProduction()
}




_args := undefined




args := func() {
	if is_undefined(_args) {

		inputs := self.inputs()


		_args = inputs.args
		if is_undefined(_args) || smart.isReference(_args) {
			ll.panic("Unexpected args type: %v", _args)
		}


		for k, v in inputs {

			if text.has_prefix(k, _PREPARED_INPUTS_PREFIX) {

				name := text.trim_prefix(k, _PREPARED_INPUTS_PREFIX)

				ll.assert(is_undefined(_args[name]), "prepare name is already in use in inputs: ", name)

				_args[name] = v
			}
		}
	}
	return _args
}

_blockId := undefined




blockId := func() {
	if _blockId == undefined {
		_blockId = self.inputs().blockId
		ll.assert(!is_undefined(_blockId), "block id is not set in workflow inputs")
	}

	return _blockId
}

__parentContext := undefined




_parentContext := func() {
	if __parentContext == undefined {
		__parentContext = self.inputs()[_INPUT_CONTEXT_FIELD_NAME]
		if __parentContext == undefined {
			ll.panic("parent context is not set in inputs")
		}
	}
	return __parentContext
}




_buildOutputs := func(results) {
	ll.assert(ll.isMap(results), "expected to see map result from body function")
	ll.assert(
		sets.fromSlice(["outputs", "exports"]) == sets.fromMapKeys(results),
		"expected only results and exports in the returned map, found", sets.fromMapKeys(results)
	)

	ll.assert(!is_undefined(results.outputs), "outputs are not defined in the results")
	ll.assert(!is_undefined(results.exports), "exports are not defined in the results")

	ll.assert(ll.isMap(results.outputs), "expected a map in outputs returned from workflow.body")
	ll.assert(ll.isMap(results.exports), "expected a map in exports returned from workflow.body")


	o := results.outputs

	e := results.exports


	resBuilder := smart.mapBuilder()
	for name, value in o {
		ll.assert(!is_undefined(value), "value is undefined for output ", name)
		resBuilder.add(name, value)
	}


	eData := {}
	eData[constants.NO_PREFIX_EXPORT] = {}

	for name, value in e {

		if smart.isReference(value) {
			eData[name] = value
			continue
		}


		ll.assert(
			sets.fromSlice(["spec", "data"]) == sets.fromMapKeys(value),
			"expected spec and data export structure"
		)

		spec := value.spec
		ll.assert(!is_undefined(spec), "spec must be defined")

		if !smart.isReference(spec) {
			spec = bobject.createSpec(spec)
		}
		eData[constants.NO_PREFIX_EXPORT][name + ".spec"] = spec
		eData[constants.NO_PREFIX_EXPORT][name + ".data"] = value.data
	}


	ctxBuilder := render.createEphemeral(
		assets.importTemplate("@platforma-sdk/workflow-tengo:workflow.build-ctx"),
		{
			blockId: blockId(),
			parentContext: _parentContext(),
			data: eData
		}
	)


	tplOutputs := {}
	tplOutputs[_RESULTS_FIELD_NAME] = resBuilder.build()
	tplOutputs[_OUTPUT_CONTEXT_FIELD_NAME] = ctxBuilder.output("ctx")

	return tplOutputs
}



















query := func(spec, ...options) {
	return bquery.create(spec, _parentContext(), options...)
}

















resolve := func(ref, ...options) {
	r := bquery.resolve(ref, _parentContext(), options...)

	return ll.toStrict(oop.inherit(r, {
		spec: r.getFutureInputField("spec"),
		data: r.getFutureInputField("data")
	}))
}






















anchoredQuery := func(anchors, queryMap) {
	return bquery.anchoredQuery(_parentContext(), anchors, queryMap)
}


_preRunTpl := undefined

_prepare := func(cb) {
	self.prepare(func(_){
		raw := cb(args())
		prefixed := {}

		for k, v in raw {
			prefixed[_PREPARED_INPUTS_PREFIX + k] = v
		}
		return prefixed
	})
}







prepare := func(cb) {
	if !_isProduction() {
		return
	}
	_prepare(cb)
}







preparePreRun := func(cb) {
	if !_isStaging() {
		return
	}
	_prepare(cb)
}




setPreRun := func(tplId) {
	ll.assert(is_undefined(_preRunTpl), "pre run template is already set")

	ll.assert(!is_undefined(tplId), "prerun template is undefined")
	_preRunTpl = tplId
}




validateArgs := func(argsSchema) {
	self.validateInputs({
		args: argsSchema
	})
}



































createPBundleBuilder := func() {
	return pBundle.createBuilder(_parentContext())
}




body := func(bodyFn) {

	if _isProduction() {

		self.body(func(_) {

			workflowInputArgs := args()
			result := bodyFn(workflowInputArgs)

			return _buildOutputs(result)
		})

	} else if !is_undefined(_preRunTpl) {



		self.delegate(_preRunTpl, { isProduction: true }, [_RESULTS_FIELD_NAME, _OUTPUT_CONTEXT_FIELD_NAME])

	} else {



		self.body(func(_) {
			return _buildOutputs({
				outputs: {},
				exports: {}
			})
		})

	}
}

export ll.toStrict({
	query: query,
	resolve: resolve,
	anchoredQuery: anchoredQuery,
	prepare: prepare,
	body: body,
	setPreRun: setPreRun,
	blockId: blockId,
	validateArgs: validateArgs,
	createPBundleBuilder: createPBundleBuilder
})
