package cli

import (
	"context"
	"time"

	"github.com/tidbcloud/account/idl/pbgen/proto/account"
	"google.golang.org/grpc"
)

type Client account.AccountAPIServiceClient
type CliEntry func(context.Context, Client, []string)

var entryMap = map[string]CliEntry{
	"GetUserByEmail":        MainGetUserByEmail,
	"GetUserByID":           MainGetUserByID,
	"ListUsers":             MainListUsers,
	"GetSystemUser":         MainGetSystemUser,
	"GetOrCreateSystemUser": MainGetOrCreateSystemUser,
	"GetAuthUser":           MainGetAuthUser,
	"GetProjectByID":        MainGetProjectByID,
	// "CreateProject":            MainCreateProject,
	"ListProjects":             MainListProjects,
	"ListProjectsByOrgs":       MainListProjectsByOrgs,
	"ListProjectsByOrgsInUser": MainListProjectsByOrgsInUser,
	"GetOrgByID":               MainGetOrgByID,
	"ListOrgs":                 MainListOrgs,
	"ListOrgsByUser":           MainListOrgsByUser,
	"CreateAuditLog":           MainCreateAuditLog,
	"ListTrials":               MainListTrials,
	"CreateTrial":              MainCreateTrail,
	"SendEmail":                MainSendEmail,
	"GetExpConfByName":         MainGetExpConfByName,
}

func NewClient(address string, opts ...grpc.DialOption) account.AccountAPIServiceClient {
	conn, err := grpc.Dial(address, opts...)
	if err != nil {
		panic(err)
	}

	return account.NewAccountAPIServiceClient(conn)
}

func getEntry(command string, conf *Config) (string, CliEntry) {
	if entry, ok := entryMap[command]; ok {
		return command, entry
	}

	commandList := SuppportedCommandList()
	matched, probables := FindMatchCommand(command, commandList)
	if matched.Score < 0 {
		OutputStep("no command matched to '%s'", command)
		return "", nil
	}

	OutputStep("guess probable commands")
	for _, probable := range probables {
		OutputNotice("matched %s", probable.Matched)
		OutputInfo("          %s", probable.Adjusted)
	}

	if len(probables) > 1 && !conf.NoConflict {
		OutputStep("ERROR: conflict command '%s'", command)
		return "", nil
	}

	OutputStep("call -> %s", matched.Matched)
	entry := entryMap[matched.Matched]
	return matched.Matched, entry
}

type Config struct {
	Address      string
	Context      context.Context
	OutputFormat string
	Benchmark    bool
	NoConflict   bool
}

func Main(conf *Config, command string, args []string) {
	opts := []grpc.DialOption{
		grpc.WithInsecure(),
	}
	client := NewClient(conf.Address, opts...)

	outputFormat = conf.OutputFormat

	var startTime, finishTime time.Time
	if conf.Benchmark {
		startTime = time.Now()
	}

	matched, entry := getEntry(command, conf)
	if len(matched) <= 0 {
		return
	}

	if entry == nil {
		OutputNotice("ERROR: handler of command '%s' is not implement", command)
	}

	entry(conf.Context, client, args)
	OutputLine()

	if conf.Benchmark {
		finishTime = time.Now()
		OutputStep("Benchmark: %v", finishTime.Sub(startTime))
	}
}
