package publicconnectionsetting

import (
	"context"
	"errors"
	"fmt"
	"strconv"

	"github.com/samber/lo"

	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	openapingv1beta2 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/nextgen/v1beta2"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

func (s *serverImpl) GetPublicConnectionSetting(
	ctx context.Context,
	req *openapingv1beta2.GetPublicConnectionSettingRequest,
) (*openapingv1beta2.PublicConnectionSetting, error) {
	ctx, span := tracer.Start(ctx, "GetPublicConnectionSetting")
	defer span.End()

	tidbId, err := strconv.ParseUint(req.TidbId, 10, 64)
	if err != nil {
		return nil, bizerr.WrapErrorWithCode(err, bizerr.ErrCodeParams, "invalid tidb id")
	}

	setting, err := s.nextgenCli.GetPublicConnectionSetting(ctx, &globalngv1.GetPublicConnectionSettingRequest{
		ClusterId: tidbId,
	})
	if err != nil {
		var berr bizerr.BizError
		if ok := errors.As(err, &berr); ok && berr != nil {
			if berr.ErrCode() == bizerr.ErrCodeRecordNotFound {
				return defaultPublicConnectionSetting(tidbId), nil
			}
		}
		return nil, bizerr.WrapErrorWithCode(err, bizerr.ErrCodeUnknown, "failed to get public connection setting")
	}

	if setting == nil {
		return defaultPublicConnectionSetting(tidbId), nil
	}

	return cvtGlobalToOpenapiPublicConnectionSetting(setting), nil
}

func cvtGlobalToOpenapiPublicConnectionSetting(
	setting *globalngv1.PublicConnectionSetting,
) *openapingv1beta2.PublicConnectionSetting {
	return &openapingv1beta2.PublicConnectionSetting{
		Name:         fmt.Sprintf("tidbs/%d/publicConnectionSetting", setting.GetClusterId()),
		TidbId:       strconv.FormatUint(setting.GetClusterId(), 10),
		Enabled:      lo.ToPtr(setting.GetEnable()),
		IpAccessList: cvtGlobalToOpenapiPublicConnectionSettingIpAccessList(setting.GetIpAllowList()),
		EgressIps:    append([]string(nil), setting.GetEgressIps()...),
	}
}

func cvtGlobalToOpenapiPublicConnectionSettingIpAccessList(
	ipAccessList []*globalngv1.Endpoint_IpAddress,
) []*openapingv1beta2.PublicConnectionSetting_IpAccessList {
	return lo.Map(
		ipAccessList,
		func(ipAccessList *globalngv1.Endpoint_IpAddress, _ int) *openapingv1beta2.PublicConnectionSetting_IpAccessList {
			return &openapingv1beta2.PublicConnectionSetting_IpAccessList{
				CidrNotation: ipAccessList.GetCidrBlock(),
				Description:  ipAccessList.GetDescription(),
			}
		},
	)
}

func defaultPublicConnectionSetting(tidbId uint64) *openapingv1beta2.PublicConnectionSetting {
	return &openapingv1beta2.PublicConnectionSetting{
		TidbId:       strconv.FormatUint(tidbId, 10),
		Enabled:      lo.ToPtr(false),
		IpAccessList: []*openapingv1beta2.PublicConnectionSetting_IpAccessList{},
		EgressIps:    []string{},
	}
}
