package clusteragent

import (
	"context"
	"crypto/tls"
	"fmt"
	"net/http"
	"time"

	"github.com/go-resty/resty/v2"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	"github.com/tidbcloud/ticdc-service-ng/pkg/regionalservice"
)

// Model resolves a cluster ID to a ready-to-use Client.
type Model interface {
	GetClient(ctx context.Context, clusterID string, timeout int) (Client, error)
}

var _ Model = &modelImpl{}

type modelImpl struct {
	regionalCli regionalservice.Client
}

// NewModel creates a new Model backed by the given regional service client.
func NewModel(regionalCli regionalservice.Client) Model {
	return &modelImpl{
		regionalCli: regionalCli,
	}
}

func (m *modelImpl) GetClient(ctx context.Context, clusterID string, timeout int) (Client, error) {
	logger := tilog.WithContextL(ctx)

	tidbGroup, err := m.regionalCli.GetOneTiDBGroupByClusterID(ctx, clusterID)
	if err != nil || tidbGroup == nil {
		logger.Error("Failed to get tidbGroup", zap.String("clusterID", clusterID), zap.Error(err))
		if regionalservice.IsNotFound(err) {
			return nil, status.Errorf(codes.NotFound, "tidbGroup %s not found", clusterID)
		}
		if err == nil {
			return nil, status.Errorf(codes.Internal, "tidbGroup %s is nil", clusterID)
		}
		return nil, status.Errorf(codes.Internal, "Failed to get tidbGroup %s: %v", clusterID, err)
	}

	dataplane, err := m.regionalCli.GetDataPlaneByID(ctx, tidbGroup.DataplaneName)
	if err != nil {
		logger.Error("Failed to get dataplane", zap.Error(err))
		if regionalservice.IsNotFound(err) {
			return nil, status.Errorf(codes.NotFound, "dataplane %s not found", tidbGroup.DataplaneName)
		}
		return nil, status.Errorf(codes.Internal, "Failed to get dataplane %s: %v", tidbGroup.DataplaneName, err)
	}
	if dataplane == nil {
		return nil, status.Errorf(codes.Internal, "dataplane %s is nil", tidbGroup.DataplaneName)
	}
	if dataplane.ApiGateway == "" {
		return nil, status.Errorf(codes.Internal,
			"Failed to get ApiGateway for dataplane %s, please try again later", dataplane.Id)
	}

	namespace := fmt.Sprintf("tidb%s", tidbGroup.SharedPoolId)

	cli := resty.New().
		SetBaseURL(dataplane.ApiGateway).
		SetTimeout(time.Duration(timeout) * time.Second).
		SetAuthScheme("Bearer").
		SetAuthToken(dataplane.AuthToken).
		SetTransport(&http.Transport{
			//nolint:gosec // skip G402 - internal service communication
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		})

	return &clientImpl{
		cli:       cli,
		namespace: namespace,
	}, nil
}
