package dedicated

import (
	"context"
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/sercand/kuberesolver/v5"
	tigrpc "github.com/tidbcloud/tiinf/transport/tigrpc/client"
	"google.golang.org/grpc"
	"google.golang.org/protobuf/types/known/emptypb"

	globaldedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	"github.com/tidbcloud/tidb-management-service/tempo-wrapper/internal/config"
	grpcutil "github.com/tidbcloud/tidb-management-service/utilities/grpc"
)

var (
	dbAuditLogSvcOnce sync.Once
	dbAuditLogSvcCli  globaldedicatedv1.DatabaseAuditLogServiceClient
)

func getDBAuditLogClient() globaldedicatedv1.DatabaseAuditLogServiceClient {
	dbAuditLogSvcOnce.Do(func() {
		timeout := time.Duration(config.Config().Grpc.Timeout) * time.Second
		ctx, cancel := context.WithTimeout(context.Background(), timeout)
		defer cancel()

		addr := config.Config().Grpc.DedicatedGlobalAddr
		interceptors := []grpc.UnaryClientInterceptor{
			grpcutil.TimeoutInterceptor(timeout),
			grpcutil.ClientPayloadLogInterceptor,
		}
		opts := []tigrpc.Option{
			tigrpc.WithDefault(),
			tigrpc.WithUnaryInterceptors(interceptors...),
		}
		if strings.HasPrefix(addr, "kubernetes://") {
			kuberesolver.RegisterInCluster()
			opts = append(opts, tigrpc.WithLoadBalance())
		}

		conn, err := tigrpc.NewClientContext(ctx, addr, opts...)
		if err != nil {
			panic(fmt.Sprintf("dial core-portal: %s", err))
		}
		dbAuditLogSvcCli = globaldedicatedv1.NewDatabaseAuditLogServiceClient(conn)
	})

	return dbAuditLogSvcCli
}

func GetAuditLogConfig(
	ctx context.Context,
	req *globaldedicatedv1.GetAuditLogConfigRequest,
) (*globaldedicatedv1.AuditLogConfig, error) {
	client := getDBAuditLogClient()

	resp, err := client.GetAuditLogConfig(ctx, req)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func UpdateAuditLogConfig(
	ctx context.Context,
	req *globaldedicatedv1.UpdateAuditLogConfigRequest,
) (*globaldedicatedv1.AuditLogConfig, error) {
	client := getDBAuditLogClient()

	resp, err := client.UpdateAuditLogConfig(ctx, req)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func PrecheckAuditLogConfig(
	ctx context.Context,
	req *globaldedicatedv1.PrecheckAuditLogConfigRequest,
) (*globaldedicatedv1.PrecheckAuditLogConfigResponse, error) {
	client := getDBAuditLogClient()

	resp, err := client.PrecheckAuditLogConfig(ctx, req)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func ListAuditLogFilterRules(
	ctx context.Context,
	req *globaldedicatedv1.ListAuditLogFilterRulesRequest,
) (*globaldedicatedv1.ListAuditLogFilterRulesResponse, error) {
	client := getDBAuditLogClient()

	resp, err := client.ListAuditLogFilterRules(ctx, req)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func CreateAuditLogFilterRule(
	ctx context.Context,
	req *globaldedicatedv1.CreateAuditLogFilterRuleRequest,
) (*globaldedicatedv1.AuditLogFilterRule, error) {
	client := getDBAuditLogClient()

	resp, err := client.CreateAuditLogFilterRule(ctx, req)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func DeleteAuditLogFilterRule(
	ctx context.Context,
	req *globaldedicatedv1.DeleteAuditLogFilterRuleRequest,
) (*emptypb.Empty, error) {
	client := getDBAuditLogClient()

	resp, err := client.DeleteAuditLogFilterRule(ctx, req)
	if err != nil {
		return nil, err
	}
	return resp, nil
}
