times := import("times")

ll := import("@platforma-sdk/workflow-tengo:ll")
oop := import("@platforma-sdk/workflow-tengo:oop")
validation := import("@platforma-sdk/workflow-tengo:validation")
assets := import("@platforma-sdk/workflow-tengo:assets")
smart := import("@platforma-sdk/workflow-tengo:smart")
render := import("@platforma-sdk/workflow-tengo:render")
pkg := import("@platforma-sdk/workflow-tengo:exec.package")
internal := import("@platforma-sdk/workflow-tengo:exec.python.internal")
runenv := import("@platforma-sdk/workflow-tengo:exec.runenv")
desc := import("@platforma-sdk/workflow-tengo:exec.descriptor")

_OUTPUT_RUN_ENV       := "runEnv"
_OUTPUT_BIN_DIR       := "binDir"
_OUTPUT_OS_DELIMETER  := "osPathDelimeter"

createVenvTpl := assets.importTemplate("@platforma-sdk/workflow-tengo:exec.python.create-venv")

pythonVenvBuilder := func() {
	self := undefined

	toolset := "pip"
	pythonDescriptor := undefined
	softwareRef := undefined
	dependencies := undefined
	cache := 0

	self = {
		useToolset: func(name) {
			validation.assertType(name, "string",
				"exec.python.useToolset: <name> must be valid name of python packaging toolset (pip, pipenv and so on)")
			ll.assert(name != "", "exec.python.useToolset: toolset name must not be empty")

			toolset = name
			return self
		},

		runEnvDescriptor: func(descriptor) {
			validation.assertType(descriptor, desc.runEnvScheme, "exec.python.runEnvDescriptor: <descriptor> must be run environment specification")
			ll.assert(descriptor.type == "python", "exec.python.runEnvDescriptor: not a 'python' run environment: %q", descriptor.type)

			pythonDescriptor = ll.fromStrict(descriptor)
			return self
		},

		software: func(pkgRef) {
			validation.assertType(pkgRef, validation.reference, "exec.python.software: <pkgRef> must be a reference to software package")

			softwareRef = pkgRef
			return self
		},

		dependencies: func(deps) {
			validation.assertType(deps, { "any": "string" }, "exec.python.dependencies: <deps> must be map of reference file paths")

			dependencies = deps
			return self
		},






		cacheMillis: func(millis) {
			ll.assert(is_int(millis) && millis > 0, "exec.python.cacheMillis: cache time must be a number of milliseconds")
			cache = millis * times.millisecond
			return self
		},






		cacheSeconds: func(seconds) {
			ll.assert(is_int(seconds) && seconds > 0, "exec.python.cacheSeconds: cache time must be a number of seconds")
			cache = seconds * times.second
			return self
		},






		cacheMinutes: func(minutes) {
			ll.assert(is_int(minutes) && minutes > 0, "exec.python.cacheMinutes: cache time must be a number of minutes")
			cache = minutes * times.minute
			return self
		},






		cacheHours: func(hours) {
			ll.assert(is_int(hours) && hours > 0, "exec.python.cacheHours: cache time must be a number of hours")
			cache = hours * times.hour
			return self
		},






		cacheDays: func(days) {
			ll.assert(is_int(days) && days > 0, "exec.python.cacheDays: cache time must be a number of days")
			cache = days * times.hour * 24
			return self
		},







		cache: func(time) {
			ll.assert(
				is_int(time),
				"exec.python.cache: cache time must be an integer. " +
					"Did you forget to import a standard tengo library 'times'?")
			cache = time
			return self
		},

		build: func() {
			depsMap := runenv.loadFilesFromPackage(softwareRef, dependencies)

			archive := pkg.get(pythonDescriptor.registry, pythonDescriptor.package).archive()
			pythonPkg := pkg.install(archive).package()

			pyVenvInputs := {
				toolset: toolset,
				dependencies: depsMap,

				pythonDescriptor: pythonDescriptor,
				python: pythonPkg,
				forceRecreate: 1 // increment to force venv re-creation in blocks
			}

			validation.assertType(pyVenvInputs, internal.pythonVenvInputSchema, "incomplete python runenv configuration")

			tpl := render.create(createVenvTpl, pyVenvInputs)

			self := undefined
			self = ll.toStrict(oop.inherit(tpl, {




				python: func() {
					return pythonPkg
				},





				binDir: func() {
					return tpl.output(_OUTPUT_BIN_DIR, cache)
				},





                pathDelimeter: func(){
            		return tpl.output(_OUTPUT_OS_DELIMETER, cache)
                },






				venv: func() {
					return tpl.output(_OUTPUT_RUN_ENV, cache)
				}
			}))

			return self
		}
	}

	return self
}

export ll.toStrict({
	venvBuilder                         : pythonVenvBuilder
})
