package main

import (
	"strings"

	vault "github.com/hashicorp/vault/api"
	"github.com/pkg/errors"
	ga "github.com/sethvargo/go-githubactions"
	"github.com/sirupsen/logrus"
)

var vaultClient *vault.Client

func main() {
	var err error

	vaultClient, err = vault.NewClient(&vault.Config{
		Address: ga.GetInput("url"),
	})

	if err != nil {
		logrus.WithError(err).Fatal("error creating vault client")
	}

	switch ga.GetInput("method") {
	case "approle":
		if err := setVaultTokenFromRoleID(); err != nil {
			logrus.WithError(err).Fatal("error setting vault token from role id")
		}
	case "token":
		vaultClient.SetToken(ga.GetInput("token"))
	default:
		logrus.Fatal("no credentials found")
	}

	exprs := strings.Split(ga.GetInput("secrets"), ";")
	for _, expr := range exprs {
		p, k, o := parseExpression(strings.TrimSpace(expr))

		s, err := getVaultSecretKey(p, k)
		if err != nil {
			logrus.WithError(err).Fatal("error reading credential")
		}

		ga.SetOutput(o, s)
		ga.SetEnv(o, s)
		ga.AddMask(s)
	}
}

func parseExpression(i string) (path, key, outputName string) {
	input := strings.TrimSpace(strings.Trim(i, "\""))

	if strings.Contains(input, "|") {
		oSplit := strings.Split(strings.TrimSpace(input), "|")
		if len(oSplit) > 1 {
			outputName = strings.TrimSpace(oSplit[1])
			input = strings.TrimSpace(oSplit[0])
		}
	}

	if strings.Contains(input, " ") {
		iSplit := strings.Split(strings.TrimSpace(input), " ")
		if len(iSplit) > 1 {
			path = strings.TrimSpace(iSplit[0])
			key = strings.TrimSpace(iSplit[1])

			if outputName == "" {
				outputName = strings.TrimSpace(iSplit[1])
			}
		}
	}

	outputName = strings.TrimSpace(outputName)
	outputName = strings.ReplaceAll(outputName, " ", "_")
	outputName = strings.ToUpper(outputName)

	return path, key, outputName
}

func getVaultSecretKey(p, k string) (string, error) {
	s, err := getVaultSecret(p)
	if err != nil {
		return "", errors.Wrap(err, "error getting path")
	}

	if s == nil || s.Data == nil || s.Data["data"] == nil {
		return "", errors.New("nil secret or secret data")
	}

	v, ok := s.Data["data"].(map[string]any)[k].(string)
	if !ok {
		logrus.Infof("Data: %#v", s.Data["data"])
		return "", errors.New("key in secret not found")
	}

	return v, nil
}

func getVaultSecret(p string) (*vault.Secret, error) {
	return vaultClient.Logical().Read(p)
}

func setVaultTokenFromRoleID() error {
	data := map[string]any{
		"role_id": ga.GetInput("roleid"),
	}

	if ga.GetInput("secretid") != "" {
		data["secret_id"] = ga.GetInput("secretid")
	}

	loginSecret, err := vaultClient.Logical().Write("auth/approle/login", data)
	if err != nil || loginSecret.Auth == nil {
		return errors.Wrap(err, "fetching authentication token")
	}

	vaultClient.SetToken(loginSecret.Auth.ClientToken)

	return nil
}
