package git

import (
	"bytes"
	"fmt"
	"reflect"

	"code.justin.tv/d8a/buddy/lib/config"

	"github.com/go-yaml/yaml"

	"github.com/google/go-github/github"
	"github.com/kr/logfmt"
)

func makeDbConfEntry(client *github.Client, repo *github.Repository, entry *github.TreeEntry, viewModel ViewModel, cluster *config.Cluster) (*github.TreeEntry, error) {
	data := make(map[string]interface{})
	renderYaml := false
	targetEntry := entry

	if targetEntry != nil {
		retrievedContent := []byte("")
		contents, _, _, err := client.Repositories.GetContents(*repo.Owner.Login, *repo.Name, *targetEntry.Path, &github.RepositoryContentGetOptions{})
		if err != nil {
			fmt.Println(err)
		}
		if err == nil && contents != nil {
			retrievedContent, err = contents.Decode()
			if err != nil {
				fmt.Println(err)
				retrievedContent = []byte("")
			}
		}

		if len(retrievedContent) == 0 {
			targetEntry = nil
		} else {
			err = yaml.Unmarshal(retrievedContent, &data)

			if err != nil {
				targetEntry = nil
				fmt.Println(err)
			}
		}
	}

	if targetEntry == nil {
		renderYaml = true
		writer := bytes.NewBuffer([]byte(""))
		err := dbConfTemplate.Execute(writer, viewModel)
		if err != nil {
			return nil, err
		}

		err = yaml.Unmarshal(writer.Bytes(), &data)
		if err != nil {
			return nil, err
		}
	}

	unTypedEnv, ok := data[cluster.Environment]
	var typedEnv map[string]interface{}
	if ok {
		typedEnv, ok = unTypedEnv.(map[string]interface{})
	}
	if !ok {
		typedEnv = make(map[string]interface{})
		data[cluster.Environment] = typedEnv
		renderYaml = true
	}

	untypedDriver, ok := typedEnv["driver"]
	var typedDriver string
	if ok {
		typedDriver, ok = untypedDriver.(string)
	}
	if !ok || typedDriver != cluster.Driver {
		typedEnv["driver"] = cluster.Driver
		renderYaml = true
	}

	untypedDb, ok := typedEnv["database"]
	var typedDb string
	if ok {
		typedDb, ok = untypedDb.(string)
	}
	if !ok || !compareDbConfString(typedDb, cluster) {
		typedEnv["database"] = dbConfString(cluster)
		renderYaml = true
	}

	if !renderYaml {
		return entry, nil
	}

	rendered, err := yaml.Marshal(data)
	return &github.TreeEntry{
		Path:    github.String("iceman/dbconf.yaml"),
		Mode:    github.String("100644"),
		Content: github.String(string(rendered)),
		Type:    github.String("blob"),
	}, err
}

func dbConfString(cluster *config.Cluster) string {
	if cluster.Driver == "postgres" {
		return fmt.Sprintf("user=%s dbname=%s host=%s port=%d sslmode=disable", cluster.SuperUser, cluster.Database, cluster.Host, cluster.Port)
	}

	return fmt.Sprintf("%s@(%s:%d)/%s", cluster.SuperUser, cluster.Host, cluster.Port, cluster.Database)
}

func defaultDbConfString(cluster *config.Cluster) string {
	fmt.Printf("Building default conf string from %s\n", cluster.Driver)
	if cluster.Driver == "postgres" {
		return fmt.Sprintf("user=%s dbname=%s host=/var/run/postgresql sslmode=disable", cluster.SuperUser, cluster.Database)
	}

	return fmt.Sprintf("%s@/%s", cluster.SuperUser, cluster.Database)
}

func compareDbConfString(dbNode string, cluster *config.Cluster) bool {
	dbProperties := make(map[string]string)

	err := logfmt.Unmarshal([]byte(dbNode), &dbProperties)
	if err != nil {
		return false
	}

	expectedProperties := cluster.GetDbConfMap()
	return reflect.DeepEqual(dbProperties, expectedProperties)
}
