// Copyright 2017 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.package spec

package util

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"strings"

	"github.com/pingcap/tidb-cloud-manager/pkg/manager"
	"github.com/pingcap/tidb-cloud-manager/pkg/server"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
)

var (
	cmdHelpMsg  string
	rootPath    = "/pingcap.com/api/v1"
	ContentType = "application/json"
	handleFlag  = validCmdFlagFunc
	urlPattern  = "(https?://)?(\\w+\\.)+(\\w+):(\\w+)(/\\w+)*"
)

func validCmdFlagFunc(flag *pflag.Flag) {
	if flag.Name == "help" || flag.Name == "config" || len(flag.Value.String()) > 0 {
		return
	}
	OutputErrorInfo(cmdHelpMsg)
}

func ValidCmdFlags(cmd *cobra.Command) {
	cmdHelpMsg = fmt.Sprintf("error: some flags are missing.\nSee '%s -h for' help.", cmd.CommandPath())
	cmd.Flags().VisitAll(handleFlag)
}

func GetNodeEndpoint(Endpoint string) string {
	return fmt.Sprintf("%s/%s/nodes", strings.TrimRight(Endpoint, "/"), rootPath)
}

func GetClusterURL(Endpoint, suffix string) string {
	return fmt.Sprintf("%s/%s", GetClusterEndpoint(Endpoint), strings.TrimLeft(suffix, "/"))
}

func GetClusterEndpoint(Endpoint string) string {
	return fmt.Sprintf("%s/%s/clusters", strings.TrimRight(Endpoint, "/"), strings.TrimLeft(rootPath, "/"))
}

func ValidManagerAddr(manager string) error {
	_, err := url.Parse(manager)
	if err != nil {
		return fmt.Errorf("Parse manager address failed: %s, err: %s", manager, err.Error())
	}
	match, _ := regexp.MatchString(urlPattern, manager)
	if !match {
		return fmt.Errorf("Invalid manager address: %s", manager)
	}
	//TODO: In the future, we can add health check logic when the cloud-manager adds a health check interface
	return nil
}

func CheckErr(err error) {
	if err == nil {
		return
	}
	OutputErrorInfo(err.Error())
}

func OutputErrorInfo(msg string) {
	if len(msg) > 0 {
		msg = AddNewLine(msg)
		fmt.Fprint(os.Stderr, msg)
	}
	os.Exit(1)
}

//output resp info to stdout
func OutputResp(resp *server.Response) error {
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf(resp.Message)
	}
	respBytes, err := json.MarshalIndent(resp, "", "    ")
	if err != nil {
		return err
	}
	respStr := AddNewLine(string(respBytes))
	_, err = fmt.Print(respStr)
	return err
}

func AddNewLine(msg string) string {
	if !strings.HasSuffix(msg, "\n") {
		msg += "\n"
	}
	return msg
}

func LoadClusterConfig(clusterCfg string) (*manager.Cluster, error) {
	cluster := &manager.Cluster{}
	content, err := ioutil.ReadFile(clusterCfg)
	if err != nil {
		return nil, err
	}
	if err = json.Unmarshal(content, cluster); err != nil {
		return nil, err
	}
	return cluster, nil
}

func RunHelp(cmd *cobra.Command, args []string) {
	cmd.Help()
}
