package terraform

import (
	"errors"
	"fmt"

	"github.com/hashicorp/go-multierror"
	"github.com/hashicorp/terraform/config"
	"github.com/hashicorp/terraform/config/module"
)

// ConfigTransformerOld is a GraphTransformer that adds the configuration
// to the graph. The module used to configure this transformer must be
// the root module. We'll look up the child module by the Path in the
// Graph.
type ConfigTransformerOld struct {
	Module *module.Tree
}

func (t *ConfigTransformerOld) Transform(g *Graph) error {
	// A module is required and also must be completely loaded.
	if t.Module == nil {
		return errors.New("module must not be nil")
	}
	if !t.Module.Loaded() {
		return errors.New("module must be loaded")
	}

	// Get the module we care about
	module := t.Module.Child(g.Path[1:])
	if module == nil {
		return nil
	}

	// Get the configuration for this module
	config := module.Config()

	// Create the node list we'll use for the graph
	nodes := make([]graphNodeConfig, 0,
		(len(config.Variables)+
			len(config.ProviderConfigs)+
			len(config.Modules)+
			len(config.Resources)+
			len(config.Outputs))*2)

	// Write all the variables out
	for _, v := range config.Variables {
		nodes = append(nodes, &GraphNodeConfigVariable{
			Variable:   v,
			ModuleTree: t.Module,
			ModulePath: g.Path,
		})
	}

	// Write all the provider configs out
	for _, pc := range config.ProviderConfigs {
		nodes = append(nodes, &GraphNodeConfigProvider{Provider: pc})
	}

	// Write all the resources out
	for _, r := range config.Resources {
		nodes = append(nodes, &GraphNodeConfigResource{
			Resource: r,
			Path:     g.Path,
		})
	}

	// Write all the modules out
	children := module.Children()
	for _, m := range config.Modules {
		path := make([]string, len(g.Path), len(g.Path)+1)
		copy(path, g.Path)
		path = append(path, m.Name)

		nodes = append(nodes, &GraphNodeConfigModule{
			Path:   path,
			Module: m,
			Tree:   children[m.Name],
		})
	}

	// Write all the outputs out
	for _, o := range config.Outputs {
		nodes = append(nodes, &GraphNodeConfigOutput{Output: o})
	}

	// Err is where the final error value will go if there is one
	var err error

	// Build the graph vertices
	for _, n := range nodes {
		g.Add(n)
	}

	// Build up the dependencies. We have to do this outside of the above
	// loop since the nodes need to be in place for us to build the deps.
	for _, n := range nodes {
		if missing := g.ConnectDependent(n); len(missing) > 0 {
			for _, m := range missing {
				err = multierror.Append(err, fmt.Errorf(
					"%s: missing dependency: %s", n.Name(), m))
			}
		}
	}

	return err
}

// varNameForVar returns the VarName value for an interpolated variable.
// This value is compared to the VarName() value for the nodes within the
// graph to build the graph edges.
func varNameForVar(raw config.InterpolatedVariable) string {
	switch v := raw.(type) {
	case *config.ModuleVariable:
		return fmt.Sprintf("module.%s.output.%s", v.Name, v.Field)
	case *config.ResourceVariable:
		return v.ResourceId()
	case *config.UserVariable:
		return fmt.Sprintf("var.%s", v.Name)
	default:
		return ""
	}
}
