package server

import (
	"context"

	apiv1 "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	"github.com/tidbcloud/cluster-service-ng/domain/entity"
	"github.com/tidbcloud/cluster-service-ng/domain/workflow"
	"github.com/tidbcloud/cluster-service-ng/domain/workflow/dag"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/encoding/protojson"
)

func (s *serverImpl) GetTiDBGroup(ctx context.Context, tidbClusterId, tidbGroupId string) (*entity.TiDBGroup, error) {

	if tidbClusterId == "" {
		return nil, status.Errorf(codes.InvalidArgument, "tidbClusterId is required")
	}

	if tidbGroupId == "" {
		return nil, status.Errorf(codes.InvalidArgument, "tidbGroupId is required")
	}

	_, err := s.tidbClusterRepo.Load(ctx, tidbClusterId, false)
	if err != nil {
		return nil, err
	}

	tidbGroup, err := s.tidbGroupRepo.Load(ctx, tidbGroupId, false)
	if err != nil {
		if status.Code(err) != codes.NotFound {
			return nil, status.Errorf(codes.Internal, "failed to load tidbGroup: %s", err)
		}

		// not found, check if it's in creating
		return nil, status.Errorf(codes.NotFound, "tidbGroup %s not found", tidbGroupId)
	}

	if tidbGroup.ClusterId != tidbClusterId {
		return nil, status.Errorf(codes.PermissionDenied, "tidb group %s does not belong to tidb cluster %s", tidbGroupId, tidbClusterId)
	}

	dagIns, err := s.getRunningDagIns(ctx, []string{dag.PlanUpdateTiDBGroup}, []string{tidbGroup.GetSharedPoolId()}, nil, nil, []string{tidbGroupId})
	if err != nil {
		return nil, status.Errorf(codes.Internal, "failed to get running update tidb group dag instance: %s", err)
	}

	for _, ins := range dagIns {
		tidbGroupStr, err := ins.ExecTask.Args.GetString(workflow.TiDBGroupKey)
		if err != nil {
			tilog.Warn(ctx, "failed to get tidb group from exec task args", zap.String("task name", ins.ExecTask.ExecId), zap.String("tidb group id", tidbGroupId))
			continue
		}

		var tempTidbGroup entity.TiDBGroup
		err = protojson.Unmarshal([]byte(tidbGroupStr), &tempTidbGroup)
		if err != nil {
			tilog.Error(ctx, "failed to unmarshal tidb group", zap.Error(err))
			return nil, status.Errorf(codes.Internal, "failed to unmarshal tidb group: %s", err)
		}

		tidbGroup.NodeQuota = tempTidbGroup.GetNodeQuota()
		if tempTidbGroup.GetSqlEndpoint().GetPublicLb() != nil {
			// Ensure SqlEndpoint is initialized
			if tidbGroup.SqlEndpoint == nil {
				tidbGroup.SqlEndpoint = &entity.SQLEndpoint{}
			}
			// Ensure PublicLb is initialized
			if tidbGroup.SqlEndpoint.PublicLb == nil {
				tidbGroup.SqlEndpoint.PublicLb = &entity.LoadBalancer{}
			}
			tidbGroup.SqlEndpoint.PublicLb.IpAllowList = tempTidbGroup.GetSqlEndpoint().GetPublicLb().GetIpAllowList()
		} else {
			if tidbGroup.SqlEndpoint == nil {
				tidbGroup.SqlEndpoint = &entity.SQLEndpoint{}
			}
			tidbGroup.SqlEndpoint.PublicLb = tempTidbGroup.GetSqlEndpoint().GetPublicLb()
		}
		tidbGroup.Phase = apiv1.TiDBGroup_MODIFYING
	}

	// Check for running scale/apply/create component DAGs targeting this tidbGroup
	componentDagIns, err := s.getRunningDagIns(ctx, []string{dag.PlanUpdateComponent, dag.PlanApplyComponent, dag.PlanCreateComponent}, []string{tidbGroup.GetSharedPoolId()}, nil, nil, []string{tidbGroupId})
	if err != nil {
		return nil, status.Errorf(codes.Internal, "failed to get running component dag instance: %s", err)
	}
	if len(componentDagIns) > 0 {
		tidbGroup.Phase = apiv1.TiDBGroup_MODIFYING
	}

	deleteIns, err := s.getRunningDagIns(ctx, []string{
		dag.PlanDeleteTiDBGroup}, []string{tidbGroup.GetSharedPoolId()}, nil, nil, []string{tidbGroupId})
	if err != nil {
		return nil, status.Errorf(codes.Internal, "failed to get running delete dag instance: %s", err)
	}

	if len(deleteIns) > 0 {
		tilog.Info(ctx, "delete tidb group dag is already running", zap.String("tidbGroupId", tidbGroupId))
		return nil, status.Errorf(codes.NotFound, "tidb group not found")
	}

	return tidbGroup, nil
}
