func buildConfig(g *graph.Graph, cfg map[string]map[string]interface{}, nodeCanIDs map[string][]string) (map[string]interface{}, error) {
	endCfg := make(map[string]interface{})
	for _, n := range g.Nodes {
		c, ok := cfg[n.ID]
		if !ok {
			return nil, fmt.Errorf("could not find config of node %q: %w", n.Canonical, errcode.ErrInvalidTFStateFile)
		}
		path := strings.Split(n.Canonical, ".")
		rn := path[len(path)-1]
		rt := strings.Join(path[:len(path)-1], ".")
		if _, ok := endCfg[rt]; !ok {
			endCfg[rt] = make(map[string]interface{})
		}
		if _, ok := endCfg[rt].(map[string]interface{})[rn]; ok {
			return nil, fmt.Errorf("repeated config node for %q: %w", n.Canonical, errcode.ErrInvalidTFStateFile)
		}
		endCfg[rt].(map[string]interface{})[rn] = c
	}
	for _, e := range g.Edges {
		for _, can := range e.Canonicals {
			pv, _, err := factory.GetProviderAndResource(can)
			if err != nil {
				return nil, fmt.Errorf("could not get provider from %s: %w", can, err)
			}
			if pv.Type() == provider.IM {
				continue
			}
			ids, ok := nodeCanIDs[can]
			if !ok {
				return nil, fmt.Errorf("could not find the ID of the canonical %q: %w", can, errcode.ErrInvalidTFStateFile)
			}
			c, ok := cfg[ids[0]]
			if !ok {
				return nil, fmt.Errorf("could not find config of the Node %q: %w", can, errcode.ErrInvalidTFStateFile)
			}
			path := strings.Split(can, ".")
			rn := path[len(path)-1]
			rt := strings.Join(path[:len(path)-1], ".")
			if _, ok := endCfg[rt]; !ok {
				endCfg[rt] = make(map[string]interface{})
			}
			if _, ok := endCfg[rt].(map[string]interface{})[rn]; ok {
				continue
			}
			endCfg[rt].(map[string]interface{})[rn] = c
		}
	}
	endCfg = map[string]interface{}{
		"resource": endCfg,
	}
	return endCfg, nil
}
buildConfig takes the Graph g and the config cfg with the nodeCanIDs and returns a config with
the configuration of each resource mapped to the canonical they have
package generate
import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"regexp"
	"strings"
	"github.com/cycloidio/flatmap"
	"github.com/cycloidio/inframap/errcode"
	"github.com/cycloidio/inframap/graph"
	"github.com/cycloidio/inframap/provider"
	"github.com/cycloidio/inframap/provider/factory"
	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/states/statefile"
	uuid "github.com/satori/go.uuid"
)
func FromState(tfstate json.RawMessage, opt Options) (*graph.Graph, map[string]interface{}, error) {
	hasDependsOn := bytes.Contains(tfstate, []byte("\"depends_on\""))
	tfstate = bytes.ReplaceAll(tfstate, []byte("\"depends_on\""), []byte("\"dependencies\""))
	err := ValidateTFStateVersion(tfstate)
	if err != nil {
		return nil, nil, fmt.Errorf("error while validating TFStateVersion: %w", err)
	}
	buf := bytes.NewBuffer(tfstate)
	file, err := statefile.Read(buf)
	if err != nil {
		return nil, nil, fmt.Errorf("error while reading TFState: %w", err)
	}
	migrateVersions(tfstate, file)
	g := graph.New()
	cfg := make(map[string]map[string]interface{})
	nodeCanIDs := make(map[string][]string)
	nodeIDEdges := make(map[string][]string)
	if !opt.Raw {
		opt, err = checkProviders(file, opt)
		if err != nil {
			return nil, nil, err
		}
	}
	for _, m := range file.State.Modules {
		for rk, rv := range m.Resources {
			if rv.Addr.Resource.Mode != addrs.ManagedResourceMode {
				continue
			}
			pv, rs, err := getProviderAndResource(rk, opt)
			if err != nil {
				if errors.Is(err, errcode.ErrProviderNotFound) {
					continue
				}
				return nil, nil, err
			}
			if !pv.IsNode(rs) && !pv.IsEdge(rs) {
				continue
			}
			for id, iv := range rv.Instances {
				if id != nil && id.String() != "[0]" {
					continue
				}
				deps := make([]string, 0)
				if len(iv.Current.Dependencies) != 0 {
					deps = append(deps, instanceCurrentDependenciesToString(iv.Current.Dependencies)...)
				}
				if hasDependsOn {
					for i, d := range deps {
						if !strings.HasPrefix(d, "module.") {
							deps[i] = prefixWithModule(m.Addr.String(), d)
						}
					}
				}
				aux := make(map[string]interface{})
				if iv.Current.AttrsJSON != nil {
					err = json.Unmarshal(iv.Current.AttrsJSON, &aux)
					if err != nil {
						return nil, nil, fmt.Errorf("invalid fomrat JSON for resource %q with AttrsJSON %s: %w", string(iv.Current.AttrsJSON), rk, err)
					}
				} else {
					keys := make(map[string]struct{})
					for k := range iv.Current.AttrsFlat {
						kk := strings.Split(k, ".")
						keys[kk[0]] = struct{}{}
					}
					for k := range keys {
						aux[k] = flatmap.Expand(iv.Current.AttrsFlat, k)
					}
				}
				res, err := pv.Resource(rs)
				if err != nil {
					return nil, nil, err
				}
				tfid, ok := aux["id"]
				if !ok {
					return nil, nil, fmt.Errorf("resource %q: %w", rk, errcode.ErrInvalidTFStateFileMissingResourceID)
				}
				n := &graph.Node{
					ID:        uuid.NewV4().String(),
					Canonical: prefixWithModule(m.Addr.String(), rk),
					TFID:      tfid.(string),
					Resource:  *res,
				}
				err = g.AddNode(n)
				if err != nil {
					return nil, nil, err
				}
				nodeCanIDs[n.Canonical] = append(nodeCanIDs[n.Canonical], n.ID)
				nodeIDEdges[n.ID] = deps
				cfg[n.ID] = aux
			}
		}
	}
	for sourceID, edges := range nodeIDEdges {
		edgeIDs := make([]string, 0)
		for _, e := range edges {
			if IDs, ok := nodeCanIDs[e]; ok {
				for _, nid := range IDs {
					edgeIDs = append(edgeIDs, nid)
				}
			}
		}
		for _, targetID := range edgeIDs {
			err := g.AddEdge(&graph.Edge{
				ID:     uuid.NewV4().String(),
				Source: sourceID,
				Target: targetID,
			})
			if err != nil {
				return nil, nil, err
			}
		}
	}
	if err := preprocess(g, cfg, opt); err != nil {
		return nil, nil, err
	}
	if opt.Clean {
		g.Clean()
	}
	err = fixEdges(g, cfg, opt)
	if err != nil {
		return nil, nil, err
	}
	if opt.Connections {
		err = mutate(g, opt)
		if err != nil {
			return nil, nil, err
		}
	}
	if opt.Clean {
		err = cleanHangingEdges(g, opt)
		if err != nil {
			return nil, nil, err
		}
	}
	endCfg, err := buildConfig(g, cfg, nodeCanIDs)
	if err != nil {
		return nil, nil, err
	}
	return g, endCfg, nil
}
func migrateVersions(src []byte, f *statefile.File) error {
	version, err := tfStateVersion(src)
	if err != nil {
		return fmt.Errorf("could not read version: %w", err)
	}
	deps := make(map[string][]addrs.ConfigResource)
	var state map[string]interface{}
	if version == 3 {
		err := json.Unmarshal(src, &state)
		if err != nil {
			return fmt.Errorf("error while reading TFState: %w", err)
		}
		if _, ok := state["modules"]; ok {
			for _, m := range state["modules"].([]interface{}) {
				rs, ok := m.(map[string]interface{})["resources"]
				if !ok {
					continue
				}
				for rn, rv := range rs.(map[string]interface{}) {
					dps, ok := rv.(map[string]interface{})["dependencies"]
					if !ok && dps == nil {
						continue
					}
					for _, d := range dps.([]interface{}) {
						ds := strings.Split(d.(string), ".")
						deps[rn] = append(deps[rn], addrs.ConfigResource{
							Resource: addrs.Resource{
								Mode: addrs.ManagedResourceMode,
								Type: ds[0],
								Name: ds[1],
							},
						})
					}
				}
			}
		}
		for _, v := range f.State.Modules {
			for rk, rv := range v.Resources {
				for _, iv := range rv.Instances {
					if d, ok := deps[rk]; ok {
						iv.Current.Dependencies = d
					}
				}
			}
		}
	}
	return nil
}
func ValidateTFStateVersion(b []byte) error {
	v, err := tfStateVersion(b)
	if err != nil {
		return fmt.Errorf("could not read version: %w", err)
	}
	if v != 4 && v != 3 {
		return fmt.Errorf("could not read version %d: %w", v, errcode.ErrInvalidTFStateVersion)
	}
	return nil
}
func tfStateVersion(b []byte) (uint64, error) {
	var v struct {
		Version uint64 `json:"version"`
	}
	err := json.Unmarshal(b, &v)
	if err != nil {
		return 0, fmt.Errorf("error while reading TFState version: %w", err)
	}
	return v.Version, nil
}
func cleanHangingEdges(g *graph.Graph, opt Options) error {
	for _, n := range g.Nodes {
		pv, rs, err := getProviderAndResource(n.Canonical, opt)
		if err != nil {
			return err
		}
		if pv.IsEdge(rs) {
			edges := g.GetEdgesForNode(n.ID)
			if len(edges) == 1 {
				repID := edges[0].Source
				if edges[0].Source == n.ID {
					repID = edges[0].Target
				}
				err = g.Replace(n.ID, repID)
				if err != nil {
					return fmt.Errorf("could not replace node: %w", err)
				}
			}
		}
	}
	if opt.Connections {
	RESTART:
		for _, n := range g.Nodes {
			pv, rs, err := getProviderAndResource(n.Canonical, opt)
			if err != nil {
				return err
			}
			if pv.IsEdge(rs) {
				if err := g.RemoveNodeByID(n.ID); err != nil {
					return err
				}
				goto RESTART
			}
		}
	}
	return nil
}