package porter_app

import (
	"context"
	"fmt"

	"github.com/mitchellh/mapstructure"
	api "github.com/porter-dev/porter/api/client"
	"github.com/porter-dev/porter/cli/cmd/deploy"
	"github.com/porter-dev/porter/internal/integrations/preview"

	switchboardTypes "github.com/porter-dev/switchboard/pkg/types"
)

func createPreDeployResource(client *api.Client, release *Service, stackName, buildResourceName, pushResourceName string, projectID, clusterID uint, env map[string]string) (*switchboardTypes.Resource, string, error) {
	var finalCmd string
	if release != nil && release.Run != nil {
		finalCmd = *release.Run
	} else {
		finalCmd = getPredeployStartCommandFromRelease(client, stackName, projectID, clusterID)
		if finalCmd == "" {
			return nil, "", nil
		}
	}

	config := &preview.ApplicationConfig{}

	config.Build.Method = "registry"
	config.Build.Image = fmt.Sprintf("{ .%s.image }", buildResourceName)
	config.Build.Env = CopyEnv(env)
	config.WaitForJob = true
	config.InjectBuild = true

	helm_values := make(map[string]interface{})
	if release != nil && release.Config != nil {
		helm_values = release.Config
	}
	helm_values["container"] = map[string]interface{}{
		"command": finalCmd,
		"env": map[string]interface{}{
			"normal": CopyEnv(env),
		},
	}
	helm_values["paused"] = false
	config.Values = convertMap(helm_values).(map[string]interface{})

	rawConfig := make(map[string]any)
	err := mapstructure.Decode(config, &rawConfig)
	if err != nil {
		return nil, "", fmt.Errorf("could not decode config for release: %w", err)
	}

	return &switchboardTypes.Resource{
		Name:      fmt.Sprintf("%s-r", stackName),
		DependsOn: []string{"get-env", buildResourceName, pushResourceName},
		Source: map[string]any{
			"name": "job",
		},
		Target: map[string]any{
			"app_name":  fmt.Sprintf("%s-r", stackName),
			"namespace": fmt.Sprintf("porter-stack-%s", stackName),
		},
		Config: rawConfig,
	}, finalCmd, nil
}

func getPredeployStartCommandFromRelease(client *api.Client, stackName string, projectID uint, clusterID uint) string {
	namespace := fmt.Sprintf("porter-stack-%s", stackName)
	releaseName := fmt.Sprintf("%s-r", stackName)
	release, err := client.GetRelease(
		context.Background(),
		projectID,
		clusterID,
		namespace,
		releaseName,
	)

	if err != nil || release == nil || release.Config == nil {
		return ""
	}

	containerMap, err := deploy.GetNestedMap(release.Config, "container")
	if err == nil {
		if command, ok := containerMap["command"]; ok {
			if commandString, ok := command.(string); ok {
				return commandString
			}
		}
	}

	return ""
}

func convertMap(m interface{}) interface{} {
	switch m := m.(type) {
	case map[string]interface{}:
		for k, v := range m {
			m[k] = convertMap(v)
		}
	case map[interface{}]interface{}:
		result := map[string]interface{}{}
		for k, v := range m {
			result[k.(string)] = convertMap(v)
		}
		return result
	case []interface{}:
		for i, v := range m {
			m[i] = convertMap(v)
		}
	}
	return m
}
