#LoadTerraformComponents: {
	#provider: "terraform"
	#do:       "load-terraform-components"

	outputs: {
		components: [...multicluster.#Component]
	}
}

#GetConnectionStatus: {
	#provider: "terraform"
	#do:       "get-connection-status"

	inputs: {
		componentName: string
	}

	outputs: {
		healthy?: bool
	}
}

#PrepareTerraformEnvBinding: #Steps & {
	inputs: {
		env:    string
		policy: string
	}
	env_:    inputs.env
	policy_: inputs.policy

	prepare: multicluster.#PrepareEnvBinding & {
		inputs: {
			env:    env_
			policy: policy_
		}
	}                        @step(1)
	loadTerraformComponents: #LoadTerraformComponents @step(2)
	terraformComponentMap: {
		for _, comp in loadTerraformComponents.outputs.components {
			"\(comp.name)": comp
		}
		...
	}
	components_: [ for comp in prepare.outputs.components if terraformComponentMap["\(comp.name)"] != _|_ {comp}]
	outputs: {
		components: components_
		decisions:  prepare.outputs.decisions
	}
}

#loadSecretInfo: {
	component: {...}
	appNamespace: string
	name:         string
	namespace:    string
	env:          string
	if component.properties != _|_ && component.properties.writeConnectionSecretToRef != _|_ {
		if component.properties.writeConnectionSecretToRef.name != _|_ {
			name: component.properties.writeConnectionSecretToRef.name
		}
		if component.properties.writeConnectionSecretToRef.name == _|_ {
			name: component.name
		}
		if component.properties.writeConnectionSecretToRef.namespace != _|_ {
			namespace: component.properties.writeConnectionSecretToRef.namespace
		}
		if component.properties.writeConnectionSecretToRef.namespace == _|_ {
			namespace: appNamespace
		}
	}
	envName: "\(name)-\(env)"
}

#bindTerraformComponentToCluster: #Steps & {
	comp: {...}
	secret: {...}
	env: string
	decisions: [...{...}]

	status: terraform.#GetConnectionStatus & {
		inputs: componentName: "\(comp.name)-\(env)"
	} @step(1)

	read: kube.#Read & {
		value: {
			apiVersion: "v1"
			kind:       "Secret"
			metadata: {
				name:      secret.envName
				namespace: secret.namespace
				...
			}
			...
		}
	} @step(2)

	wait: {
		#do:      "wait"
		continue: status.outputs.healthy && read.err == _|_
	} @step(3)

	sync: #Steps & {
		for decision in decisions {
			"\(decision.cluster)-\(decision.namespace)": kube.#Apply & {
				cluster: decision.cluster
				value: {
					apiVersion: "v1"
					kind:       "Secret"
					metadata: {
						name: secret.name
						if decision.namespace != _|_ && decision.namespace != "" {
							namespace: decision.namespace
						}
						if decision.namespace == _|_ || decision.namespace == "" {
							namespace: secret.namespace
						}
						...
					}
					type: "Opaque"
					data: read.value.data
					...
				}
			}
		}
	} @step(4)
}

#DeployCloudResource: {
	#do: "steps"

	env:       string
	name:      string
	policy:    string
	namespace: string

	env_:          env
	policy_:       policy
	prepareDeploy: #PrepareTerraformEnvBinding & {
		inputs: {
			env:    env_
			policy: policy_
		}
	} @step(1)

	deploy: #Steps & {
		for comp in prepareDeploy.outputs.components {
			"\(comp.name)": #Steps & {

				secretMeta: #loadSecretInfo & {
					component:    comp
					env:          env_
					appNamespace: namespace
				}

				apply: #ApplyComponent & {
					value: {
						name: "\(comp.name)-\(env)"
						properties: {
							writeConnectionSecretToRef: {
								name:      secretMeta.envName
								namespace: secretMeta.namespace
							}
							if comp.properties != _|_ {
								for k, v in comp.properties {
									if k != "writeConnectionSecretToRef" {
										"\(k)": v
									}
								}
							}
							...
						}
						for k, v in comp {
							if k != "name" && k != "properties" {
								"\(k)": v
							}
						}
						...
					}
				} @step(1)

				comp_: comp
				bind:  #bindTerraformComponentToCluster & {
					comp:      comp_
					secret:    secretMeta
					env:       env_
					decisions: prepareDeploy.outputs.decisions
				} @step(2)

				secret: bind.read.value

				update: kube.#Apply & {
					value: {
						metadata: {
							for k, v in secret.metadata {
								if k != "labels" {
									"\(k)": v
								}
							}
							labels: {
								"app.oam.dev/name":       name
								"app.oam.dev/namespace":  namespace
								"app.oam.dev/component":  comp.name
								"app.oam.dev/env-name":   env
								"app.oam.dev/sync-alias": secretMeta.name
								if secret.metadata.labels != _|_ {
									for k, v in secret.metadata.labels {
										if k != "app.oam.dev/name" && k != "app.oam.dev/sync-alias" && k != "app.oam.dev/env-name" {
											"\(k)": v
										}
									}
								}
								...
							}
						}
						for k, v in secret {
							if k != "metadata" {
								"\(k)": v
							}
						}
						...
					}
				} @step(6)
			}
		}
		...
	} @step(2)
}

#ShareCloudResource: {
	#do: "steps"

	env:        string
	name:       string
	policy:     string
	namespace:  string
	namespace_: namespace
	placements: [...multicluster.#PlacementDecision]

	env_:        env
	policy_:     policy
	prepareBind: #PrepareTerraformEnvBinding & {
		inputs: {
			env:    env_
			policy: policy_
		}
	} @step(1)

	decisions_: [ for placement in placements {
		namespace: *"" | string
		if placement.namespace != _|_ {
			namespace: placement.namespace
		}
		if placement.namespace == _|_ {
			namespace: namespace_
		}
		cluster: *"local" | string
		if placement.cluster != _|_ {
			cluster: placement.cluster
		}
	}]

	deploy: #Steps & {
		for comp in prepareBind.outputs.components {
			"\(comp.name)": #Steps & {
				secretMeta: #loadSecretInfo & {
					component:    comp
					env:          env_
					appNamespace: namespace
				}
				comp_: comp
				bind:  #bindTerraformComponentToCluster & {
					comp:      comp_
					secret:    secretMeta
					env:       env_
					decisions: decisions_
				} @step(1)
			}
		}
	} @step(2)
}
