package dataapi

import (
	"context"
	"sync"

	"github.com/tidbcloud/dataservices/idl/pbgen/proto/dataapi"
	"github.com/tidbcloud/ecosystem-service/config"
	"github.com/tidbcloud/ecosystem-service/internal/service/grpc/dataapi/client"
	"github.com/tidbcloud/ecosystem-service/internal/utils"
	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/pkgv2/tracing"
	"go.opentelemetry.io/otel"
)

var Tracer = otel.Tracer("grpc-data-api")

type DataApiSvc interface {
	ShowDataApp(ctx context.Context, appID uint64) (*dataapi.AppInfo, error)
	CreateApiKey(ctx context.Context, appID uint64, userEmail, description string) (*dataapi.CreateAPIKeyResp, error)
	DeleteApiKey(ctx context.Context, appID, keyID uint64, userEmail string) error
}

var (
	dataApiSvcOnce sync.Once
	dataApiSvc     *dataApiSvcImpl
)

type dataApiSvcImpl struct {
	client dataapi.DataAPIServiceClient
}

// NewDataApiSvc needs to be called at runtime since it needs the grpc_addr configuration initialized at runtime
func NewDataApiSvc(conf *config.ConfigWrapper) DataApiSvc {
	dataApiSvcOnce.Do(func() {
		dataApiSvc = &dataApiSvcImpl{
			client: client.GetDataAPISvcClient(conf),
		}
	})
	return dataApiSvc
}

func (s *dataApiSvcImpl) ShowDataApp(ctx context.Context, appID uint64) (_ *dataapi.AppInfo, err error) {
	ctx, span := Tracer.Start(ctx, "dataApiSvcImpl.ShowDataApp")
	defer func() {
		tracing.EndSpan(span, err)
	}()

	req := &dataapi.ShowDataAppReq{
		AppId: appID,
	}
	resp, err := s.client.ShowDataApp(ctx, req)
	if err != nil {
		return nil, utils.WrapGrpcError(err)
	}
	if bizerr.ErrCode(resp.BaseResp.ErrCode) == bizerr.ErrCodeRecordNotFound {
		return nil, nil
	}
	if resp.BaseResp.ErrCode != 0 {
		return nil, bizerr.Restore(bizerr.ErrCode(resp.BaseResp.ErrCode), resp.BaseResp.ErrMsg)
	}
	return resp.AppInfo, nil
}

func (s *dataApiSvcImpl) CreateApiKey(ctx context.Context, appID uint64, userEmail, description string) (_ *dataapi.CreateAPIKeyResp, err error) {
	ctx, span := Tracer.Start(ctx, "dataApiSvcImpl.CreateApiKey")
	defer func() {
		tracing.EndSpan(span, err)
	}()

	req := &dataapi.CreateAPIKeyReq{
		DataappId:   appID,
		Description: description,
		CreatedBy:   userEmail,
		Source:      dataapi.APIKeySource_DataAPI_Source_DataAPI_Console,
	}
	resp, err := s.client.CreateAPIKey(ctx, req)
	if err != nil {
		return nil, utils.WrapGrpcError(err)
	}
	if resp.BaseResp.ErrCode != 0 {
		return nil, bizerr.Restore(bizerr.ErrCode(resp.BaseResp.ErrCode), resp.BaseResp.ErrMsg)
	}
	return resp, nil
}

func (s *dataApiSvcImpl) DeleteApiKey(ctx context.Context, appID, keyID uint64, userEmail string) (err error) {
	ctx, span := Tracer.Start(ctx, "dataApiSvcImpl.DeleteApiKey")
	defer func() {
		tracing.EndSpan(span, err)
	}()

	req := &dataapi.DeleteAPIKeyReq{
		ApikeyId:  keyID,
		DataappId: appID,
		CreatedBy: userEmail,
	}
	resp, err := s.client.DeleteAPIKey(ctx, req)
	if err != nil {
		return utils.WrapGrpcError(err)
	}
	if resp.BaseResp.ErrCode != 0 {
		return bizerr.Restore(bizerr.ErrCode(resp.BaseResp.ErrCode), resp.BaseResp.ErrMsg)
	}
	return nil
}
