package br_svc

import (
	"context"

	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/pkgv2/log"
	"github.com/tidbcloud/pkgv2/tracing"
	"github.com/tidbcloud/serverless-service/idl/pbgen/proto/rpc"
	"github.com/tidbcloud/serverless-service/internal/domain/service"
	"github.com/tidbcloud/serverless-service/internal/domain/vo"
	"github.com/tidbcloud/serverless-service/internal/errorcode"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/attribute"
	"go.uber.org/zap"
)

var rpcTracer = otel.Tracer("br.rpc")

type backupRestoreRpcService struct {
	br service.BackupRestoreService
	rpc.UnsafeBackupRestoreServiceServer
}

func NewBackupRestoreRpcService(br service.BackupRestoreService) rpc.BackupRestoreServiceServer {
	return &backupRestoreRpcService{
		br: br,
	}
}

func (b *backupRestoreRpcService) ListBackupPolicies(
	ctx context.Context,
	request *rpc.ListBackupPoliciesRequest,
) (_ *rpc.ListBackupPoliciesResponse, err error) {
	ctx, span := rpcTracer.Start(ctx, "backupRestoreRpcService.ListBackupPolicies")
	logger := log.WithContextL(ctx)
	defer func() {
		tracing.EndSpan(span, err)
		err = errorcode.PrepareBackupErrorMessage(ctx, err)
	}()
	span.SetAttributes(
		attribute.String("clusterId", request.ClusterId),
	)
	if request.ClusterId == "" {
		logger.Error("cluster id is required")
		return nil, bizerr.New(errorcode.ErrCodeEmptyClusterID, "")
	}
	if request.OrgId == 0 {
		logger.Error("org id is required")
		return nil, bizerr.New(errorcode.ErrCodeEmptyOrgId, "org id is required")
	}

	backupScheduleVos, err := b.br.ListBackupSchedules(
		ctx,
		&vo.ListBackupSchedulesOpt{ClusterId: request.ClusterId, OrgId: request.OrgId},
	)
	if err != nil {
		logger.Error("list backup schedules failed", zap.Error(err))
		return nil, err
	}
	// build response
	policies := make([]*rpc.BackupPolicy, 0, len(backupScheduleVos))
	for _, backupScheduleVo := range backupScheduleVos {
		policies = append(policies, backupScheduleVo.ConvertToRPC())
	}
	return &rpc.ListBackupPoliciesResponse{
		BackupPolicies: policies,
	}, nil
}

func (b *backupRestoreRpcService) UpdateBackupPolicy(
	ctx context.Context,
	request *rpc.UpdateBackupPolicyRequest,
) (_ *rpc.BackupPolicy, err error) {
	ctx, span := rpcTracer.Start(ctx, "backupRestoreRpcService.UpdateBackupPolicy")
	logger := log.WithContextL(ctx)
	defer func() {
		tracing.EndSpan(span, err)
		err = errorcode.PrepareBackupErrorMessage(ctx, err)
	}()
	span.SetAttributes(
		attribute.String("clusterId", request.ClusterId),
		attribute.String("ScheduleTime", request.ScheduleTime),
	)

	if request.ClusterId == "" {
		logger.Error("cluster id is required")
		return nil, bizerr.New(errorcode.ErrCodeEmptyClusterID, "")
	}
	if request.ScheduleTime == "" {
		logger.Error("schedule time is required")
		return nil, bizerr.New(errorcode.ErrCodeEmptyScheduleTime, "")
	}

	backupScheduleVo, err := b.br.UpdateBackupSchedules(ctx, &vo.UpdateBackupSchedulesOpt{
		ClusterId:     request.ClusterId,
		ScheduleTime:  request.ScheduleTime,
		OrgId:         request.OrgId,
		Id:            request.Id,
		RetentionDays: int(request.Retention),
	})
	if err != nil {
		logger.Error("update backup schedules failed", zap.Error(err))
		return nil, err
	}
	return backupScheduleVo.ConvertToRPC(), nil
}
