package mapper

import (
	"sort"

	"github.com/rancher/norman/types"
	"github.com/rancher/norman/types/convert"
	v1 "k8s.io/api/core/v1"
)

type EnvironmentMapper struct {
}

func (e EnvironmentMapper) FromInternal(data map[string]interface{}) {
	var env []v1.EnvVar
	var envFrom []v1.EnvFromSource

	envMap := map[string]interface{}{}
	var envFromMaps []interface{}

	if err := convert.ToObj(data["env"], &env); err == nil {
		for _, envVar := range env {
			if envVar.ValueFrom == nil {
				envMap[envVar.Name] = envVar.Value
				continue
			}

			if envVar.ValueFrom.FieldRef != nil {
				envFromMaps = append(envFromMaps, map[string]interface{}{
					"source":     "field",
					"sourceName": envVar.ValueFrom.FieldRef.FieldPath,
					"targetKey":  envVar.Name,
				})
			}
			if envVar.ValueFrom.ResourceFieldRef != nil {
				envFromMaps = append(envFromMaps, map[string]interface{}{
					"source":     "resource",
					"sourceName": envVar.ValueFrom.ResourceFieldRef.ContainerName,
					"sourceKey":  envVar.ValueFrom.ResourceFieldRef.Resource,
					"divisor":    envVar.ValueFrom.ResourceFieldRef.Divisor,
					"targetKey":  envVar.Name,
				})
			}
			if envVar.ValueFrom.ConfigMapKeyRef != nil {
				envFromMaps = append(envFromMaps, map[string]interface{}{
					"source":     "configMap",
					"sourceName": envVar.ValueFrom.ConfigMapKeyRef.Name,
					"sourceKey":  envVar.ValueFrom.ConfigMapKeyRef.Key,
					"optional":   getValue(envVar.ValueFrom.ConfigMapKeyRef.Optional),
					"targetKey":  envVar.Name,
				})
			}
			if envVar.ValueFrom.SecretKeyRef != nil {
				envFromMaps = append(envFromMaps, map[string]interface{}{
					"source":     "secret",
					"sourceName": envVar.ValueFrom.SecretKeyRef.Name,
					"sourceKey":  envVar.ValueFrom.SecretKeyRef.Key,
					"optional":   getValue(envVar.ValueFrom.SecretKeyRef.Optional),
					"targetKey":  envVar.Name,
				})
			}
		}
	}

	if err := convert.ToObj(data["envFrom"], &envFrom); err == nil {
		for _, envVar := range envFrom {
			if envVar.SecretRef != nil {
				envFromMaps = append(envFromMaps, map[string]interface{}{
					"source":     "secret",
					"sourceName": envVar.SecretRef.Name,
					"prefix":     envVar.Prefix,
					"optional":   getValue(envVar.SecretRef.Optional),
					"type":       "/v3/project/schemas/environmentFrom",
				})
			}
			if envVar.ConfigMapRef != nil {
				envFromMaps = append(envFromMaps, map[string]interface{}{
					"source":     "configMap",
					"sourceName": envVar.ConfigMapRef.Name,
					"prefix":     envVar.Prefix,
					"optional":   getValue(envVar.ConfigMapRef.Optional),
					"type":       "/v3/project/schemas/environmentFrom",
				})
			}
		}
	}

	delete(data, "env")
	delete(data, "envFrom")

	if len(envMap) > 0 {
		data["environment"] = envMap
	}
	if len(envFromMaps) > 0 {
		data["environmentFrom"] = envFromMaps
	}
}

func (e EnvironmentMapper) ToInternal(data map[string]interface{}) error {
	var envVar []map[string]interface{}
	var envVarFrom []map[string]interface{}

	var orderedKeys []string
	environment := convert.ToMapInterface(data["environment"])
	for k := range environment {
		orderedKeys = append(orderedKeys, k)
	}
	sort.Strings(orderedKeys)
	for _, key := range orderedKeys {
		envVar = append(envVar, map[string]interface{}{
			"name":  key,
			"value": environment[key],
		})
	}

	for _, value := range convert.ToMapSlice(data["environmentFrom"]) {
		source := convert.ToString(value["source"])
		if source == "" {
			continue
		}

		targetKey := convert.ToString(value["targetKey"])
		sourceKey := convert.ToString(value["sourceKey"])
		if targetKey == "" && sourceKey == "" {
			switch source {
			case "secret":
				envVarFrom = append(envVarFrom, map[string]interface{}{
					"prefix": value["prefix"],
					"secretRef": map[string]interface{}{
						"name":     value["sourceName"],
						"optional": value["optional"],
					},
				})
			case "configMap":
				envVarFrom = append(envVarFrom, map[string]interface{}{
					"prefix": value["prefix"],
					"configMapRef": map[string]interface{}{
						"name":     value["sourceName"],
						"optional": value["optional"],
					},
				})
			}
		} else {
			if targetKey == "" {
				targetKey = sourceKey
			}
			switch source {
			case "field":
				envVar = append(envVar, map[string]interface{}{
					"name": targetKey,
					"valueFrom": map[string]interface{}{
						"fieldRef": map[string]interface{}{
							"fieldPath": value["sourceName"],
						},
					},
				})
			case "resource":
				envVar = append(envVar, map[string]interface{}{
					"name": targetKey,
					"valueFrom": map[string]interface{}{
						"resourceFieldRef": map[string]interface{}{
							"containerName": value["sourceName"],
							"resource":      value["sourceKey"],
							"divisor":       value["divisor"],
						},
					},
				})
			case "configMap":
				envVar = append(envVar, map[string]interface{}{
					"name": targetKey,
					"valueFrom": map[string]interface{}{
						"configMapKeyRef": map[string]interface{}{
							"name":     value["sourceName"],
							"key":      value["sourceKey"],
							"optional": value["optional"],
						},
					},
				})
			case "secret":
				envVar = append(envVar, map[string]interface{}{
					"name": targetKey,
					"valueFrom": map[string]interface{}{
						"secretKeyRef": map[string]interface{}{
							"name":     value["sourceName"],
							"key":      value["sourceKey"],
							"optional": value["optional"],
						},
					},
				})
			}
		}
	}

	delete(data, "environment")
	delete(data, "environmentFrom")
	data["env"] = envVar
	data["envFrom"] = envVarFrom

	return nil
}

func (e EnvironmentMapper) ModifySchema(schema *types.Schema, schemas *types.Schemas) error {
	delete(schema.ResourceFields, "env")
	delete(schema.ResourceFields, "envFrom")
	return nil
}

func getValue(optional *bool) bool {
	if optional != nil {
		return *optional
	}
	return false
}
