package supervisorclient

import (
	"context"
	"fmt"
	"time"

	"github.com/guo-shaoge/tiflash-autoscale/pkg/util/log/logger"
	"github.com/guo-shaoge/tiflash-autoscale/supervisor_proto/supervisor"
	"go.uber.org/zap"
	"google.golang.org/grpc"
	"google.golang.org/protobuf/types/known/emptypb"
)

const (
	SupervisorPort         string = "7000"
	GrpcCommonTimeOutSec          = 10
	GrpcAssignTimeOutSec          = 120
	GrpcUnassignTimeOutSec        = 120
)

var (
	IsSupClientMock = false

	HardCodeEnvPdAddr       string
	HardCodeSupervisorImage string

	// TODO fix
	EnvEnableIstioProxy bool
	EnvEnableTLS        bool
)

func AssignTenant(podIP string, tenantName string, pdAddr string, tiflashVer string, parentLogger *zap.Logger) (resp *supervisor.Result, err error) {
	cnAddr := podIP + ":" + SupervisorPort
	logger := parentLogger.With(zap.String(logger.LogIDMod, "supervisor_client")).
		With(zap.String("rpc", "AssignTenant")).With(zap.String("cnAddr", cnAddr))
	defer func() {
		if err != nil || (resp != nil && resp.HasErr) {
			var err1, err2 string
			if err != nil {
				err1 = err.Error()
			}
			if resp != nil && resp.HasErr {
				err2 = resp.ErrInfo
			}
			logger.Error("grpc failed", zap.String("err1", err1), zap.String("err2", err2))
		}
	}()

	if IsSupClientMock {
		time.Sleep(500 * time.Millisecond)
		return &supervisor.Result{HasErr: false}, nil
	}

	logger.Info("start grpc")
	connctx, conncancel := context.WithTimeout(context.Background(), time.Duration(GrpcCommonTimeOutSec)*time.Second)
	defer conncancel()
	conn, err := grpc.DialContext(connctx, cnAddr, grpc.WithInsecure(), grpc.FailOnNonTempDialError(true), grpc.WithBlock())
	if err != nil {
		return nil, err
	}
	if conn != nil {
		defer conn.Close()
	}
	cli := supervisor.NewAssignClient(conn)
	ctx, cancel := context.WithTimeout(context.Background(), GrpcAssignTimeOutSec*time.Second)
	defer cancel()
	r, err := cli.AssignTenant(ctx, &supervisor.AssignRequest{TenantID: tenantName, TidbStatusAddr: "", PdAddr: pdAddr, TiflashVer: tiflashVer})
	if err != nil {
		logger.Error("AssignTenant fail", zap.Error(err))
		return r, err
	}
	respStr := r.TenantID
	if r.TenantID == "" {
		respStr = "empty"
	}
	logger.Info("rpc success", zap.String("tenant", respStr))
	return r, err
}

func UnassignTenant(podIP string, tenantName string, forceShutdown bool, parentLogger *zap.Logger) (resp *supervisor.Result, err error) {
	cnAddr := podIP + ":" + SupervisorPort
	logger := parentLogger.With(zap.String(logger.LogIDMod, "supervisor_client")).With(zap.String("rpc", "UnassignTenant")).With(zap.String("cnAddr", cnAddr))
	defer func() {
		if err != nil || (resp != nil && resp.HasErr) {
			var err1, err2 string
			if err != nil {
				err1 = err.Error()
			}
			if resp != nil && resp.HasErr {
				err2 = resp.ErrInfo
			}
			logger.Error("failed to UnassignTenant", zap.String("err1", err1), zap.String("err2", err2))
		}
	}()
	if IsSupClientMock {
		time.Sleep(500 * time.Millisecond)
		return &supervisor.Result{HasErr: false}, nil
	}
	logger.Info("start grpc")
	connctx, conncancel := context.WithTimeout(context.Background(), GrpcCommonTimeOutSec*time.Second)
	defer conncancel()
	conn, err := grpc.DialContext(connctx, cnAddr, grpc.WithInsecure(), grpc.WithBlock())
	if err != nil {
		return nil, err
	}
	if conn != nil {
		defer conn.Close()
	}
	c := supervisor.NewAssignClient(conn)

	// Contact the server and print out its response.
	ctx, cancel := context.WithTimeout(context.Background(), GrpcUnassignTimeOutSec*time.Second)
	defer cancel()
	r, err := c.UnassignTenant(ctx, &supervisor.UnassignRequest{AssertTenantID: tenantName, ForceShutdown: forceShutdown})
	if err != nil {
		logger.Error("UnassignTenant fail", zap.Error(err))
		return r, err
	}
	respStr := r.TenantID
	if r.TenantID == "" {
		respStr = "empty"
	}
	logger.Info("rpc success", zap.String("tenant", respStr))
	return r, err
}

func GetCurrentTenant(podIP string, parentLogger *zap.Logger) (resp *supervisor.GetTenantResponse, err error) {
	cnAddr := podIP + ":" + SupervisorPort
	logger := parentLogger.With(zap.String(logger.LogIDMod, "supervisor_client")).
		With(zap.String("rpc", "GetCurrentTenant")).With(zap.String("cnAddr", cnAddr))
	defer func() {
		if err != nil {
			logger.Error("GetCurrentTenant fail", zap.Error(err))
		}
	}()
	if IsSupClientMock {
		time.Sleep(500 * time.Millisecond)
		return nil, fmt.Errorf("mock GetCurrentTenant")
	}

	logger.Info("start grpc")
	ctx, cancel := context.WithTimeout(context.Background(), GrpcCommonTimeOutSec*time.Second)
	defer cancel()
	conn, err := grpc.DialContext(ctx, cnAddr, grpc.WithInsecure(), grpc.WithBlock())
	if err != nil {
		return nil, err
	}
	if conn != nil {
		defer conn.Close()
	}
	c := supervisor.NewAssignClient(conn)

	// Contact the server and print out its response.
	ctx2, cancel2 := context.WithTimeout(context.Background(), GrpcCommonTimeOutSec*time.Second)
	defer cancel2()
	r, err := c.GetCurrentTenant(ctx2, &emptypb.Empty{})
	if err != nil {
		logger.Error("GetCurrentTenant fail", zap.Error(err))
		return r, err
	}
	respStr := r.TenantID
	if r.TenantID == "" {
		respStr = "empty"
	}
	logger.Info("rpc success", zap.String("tenant", respStr))
	return r, nil
}
