package cmd

import (
	"encoding/json"
	"fmt"
	"net/http"
	"regexp"

	"github.com/dimchansky/utfbom"
	"github.com/fatih/color"
	"github.com/hashicorp/go-multierror"
	"github.com/pkg/errors"
	"github.com/spf13/cobra"

	"github.com/pingcap/test-infra/tools/tcctl/pkg/resource"
	"github.com/pingcap/test-infra/tools/tcctl/pkg/util"
)

// applyCmd represents the apply command
var applyCmd = &cobra.Command{
	Use:          "apply",
	Short:        "Create or update resources",
	Long:         `Create or update resources`,
	SilenceUsage: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		reader, err := util.ReadFromFile(inputFile)
		if err != nil {
			return err
		}
		sr, enc := utfbom.Skip(reader)
		util.LogInfof("Detected encoding: %s\n", enc)

		r, _ := regexp.Compile("^.+(.csv|.yaml|.yml)$")
		matched := r.FindStringSubmatch(inputFile)

		var rs []resource.Resource
		if len(matched) == 2 && matched[1] == ".csv" {
			rs, err = resource.ResourcesFromCSVStream(serverConfig, sr)
		} else {
			rs, err = resource.ResourcesFromYAMLStream(serverConfig, sr)
		}
		if err != nil {
			return errors.Wrap(err, "resource from stream error")
		}
		err = applyFromStream(rs)
		if err != nil {
			if e, ok := err.(util.HttpError); ok && e.Code == http.StatusUnauthorized {
				util.LogErrorf(`Unauthorized: you may go to https://tcms.pingcap.net/dashboard/settings/tokens to fetch a token and execute tcctl config --token=<your token>`)
			}
			return err
		}
		util.LogInfof(color.GreenString("success"))
		return nil
	},
}

func init() {
	applyCmd.PersistentFlags().BoolVar(&dryrun, dryRunFlag, false, "dry run")
	rootCmd.AddCommand(applyCmd)
}

func applyFromStream(rs []resource.Resource) error {
	errs := &multierror.Error{}
	for _, r := range rs {
		exist, err := r.IsExist()
		if err != nil {
			errs = multierror.Append(errs, errors.Wrap(err, fmt.Sprintf("internal error %s", r.Name())))
			continue
		}
		if exist {
			o, err := r.Update(dryrun)
			if err != nil {
				errs = multierror.Append(errs, errors.Wrap(err, fmt.Sprintf("case update error %s", r.Name())))
				continue
			}
			content, _ := json.Marshal(o)
			util.LogInfof("%s: %s", color.GreenString("updated"), string(content))
		} else {
			o, err := r.Create(dryrun)
			if err != nil {
				errs = multierror.Append(errs, errors.Wrap(err, fmt.Sprintf("case create error %s", r.Name())))
				continue
			}
			content, _ := json.Marshal(o)
			util.LogInfof("%s: %s", color.GreenString("created"), string(content))
		}
	}
	return errs.ErrorOrNil()
}
