package adapter

import (
	"context"
	"fmt"

	dplanemgrv1 "github.com/tidbcloud/cluster-service-ng/api/dataplane-manager/v1"
	"github.com/tidbcloud/cluster-service-ng/pkg/dataplanemgr"
	repository "github.com/tidbcloud/cluster-service-ng/pkg/storage/repo"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
)

type SharedPoolAdapter interface {
	GetKongAddressByClusterId(ctx context.Context, clusterId string) (string, string, error)
	GetKongAddressBySharedPoolId(ctx context.Context, sharedPoolId string) (string, string, error)
}

type sharedPoolAdapter struct {
	tidbClusterRepo repository.TiDBClusterRepo
	sharedPoolRepo  repository.SharedPoolRepo
	dataplaneClient dataplanemgr.Client
}

func NewSharedPoolAdapter(
	tidbClusterRepo repository.TiDBClusterRepo,
	sharedPoolRepo repository.SharedPoolRepo,
	dataplaneClient dataplanemgr.Client,
) SharedPoolAdapter {
	return &sharedPoolAdapter{
		tidbClusterRepo: tidbClusterRepo,
		sharedPoolRepo:  sharedPoolRepo,
		dataplaneClient: dataplaneClient,
	}
}

func (s *sharedPoolAdapter) GetKongAddressByClusterId(ctx context.Context, clusterId string) (string, string, error) {
	tilog.PlainInfo("GetKongAddressByClusterID called clusterId", zap.String("clusterId", clusterId))

	cluster, err := s.tidbClusterRepo.Load(ctx, clusterId, false)
	if err != nil {
		tilog.Error(ctx, "failed to list TiDBCluster clusterId", zap.String("clusterId", clusterId), zap.Error(err))
		return "", "", fmt.Errorf("failed to list TiDBCluster by clusterId: %w", err)
	}

	if cluster.GetSharedPoolId() == "" {
		tilog.Error(ctx, "cluster SharedPoolId is empty", zap.String("clusterId", clusterId))
		return "", "", fmt.Errorf("cluster SharedPoolId is empty for clusterId: %s", clusterId)
	}

	return s.GetKongAddressBySharedPoolId(ctx, cluster.GetSharedPoolId())
}

func (s *sharedPoolAdapter) GetKongAddressBySharedPoolId(ctx context.Context, sharedPoolId string) (string, string, error) {
	tilog.PlainInfo("GetKongAddressBySharedPoolID called sharedPoolId", zap.String("sharedPoolId", sharedPoolId))

	sharedPool, err := s.sharedPoolRepo.Load(ctx, sharedPoolId, false)
	if err != nil {
		tilog.Error(ctx, "failed to load SharedPool sharedPoolId", zap.String("sharedPoolId", sharedPoolId), zap.Error(err))
		return "", "", fmt.Errorf("failed to load SharedPool by sharedPoolId: %w", err)
	}

	if sharedPool.GetDataplaneName() == "" {
		tilog.Error(ctx, "sharedPool dataplaneName is empty", zap.String("sharedPoolId", sharedPoolId))
		return "", "", fmt.Errorf("sharedPool dataplaneName is empty for sharedPoolId: %s", sharedPoolId)
	}

	dataplaneName := sharedPool.GetDataplaneName()
	tilog.Info(ctx, "found SharedPool", zap.String("sharedPoolId", sharedPoolId), zap.String("dataplaneName", dataplaneName))

	req := &dplanemgrv1.GetDataPlaneRequest{
		DataPlaneId: dataplaneName,
	}
	resp, err := s.dataplaneClient.GetDataPlaneServiceClient().GetDataPlane(ctx, req)
	if err != nil {
		tilog.Error(ctx, "GetDataPlane failed", zap.String("dataplaneName", dataplaneName), zap.Error(err))
		return "", "", fmt.Errorf("GetDataPlane failed: %w", err)
	}

	tilog.Info(ctx, "GetDataPlane response", zap.String("dataplaneName", dataplaneName), zap.String("ApiGateway", resp.ApiGateway))

	if resp.ApiGateway == "" {
		tilog.PlainError("apigateway not found in dataplane", zap.String("dataplaneName", dataplaneName))
		return "", "", fmt.Errorf("apigateway not found in dataplane %s", dataplaneName)
	}

	return resp.GetApiGateway(), resp.GetAuthToken(), nil
}
