package activator

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"

	"github.com/pingcap/log"
	"github.com/tidbcloud/aws-shared-provider/pkg/tidbworker/resourcemanager/poolmanager/factory"
	rm "github.com/tidbcloud/aws-shared-provider/pkg/tidbworker/resourcemanager/types"
	"github.com/tidbcloud/aws-shared-provider/pkg/utils/tidb"
	"go.uber.org/zap"
	"sigs.k8s.io/controller-runtime/pkg/client"
)

const (
	activatePath = "/tidb-pool/activate"
	exitPath     = "/tidb-pool/exit"
)

type activatorImpl struct {
	manager    rm.PoolManager
	namespace  string
	clusterCli client.Client
}

func NewActivator(manager rm.PoolManager) rm.Activator {
	return &activatorImpl{
		manager:    manager,
		namespace:  manager.GetNamespace(),
		clusterCli: manager.GetClusterClient(),
	}
}

func (a *activatorImpl) ActivateWorker(
	ctx context.Context,
	_ rm.WorkerPool,
	worker rm.Worker,
	parameter tidb.ActivateRequest,
) error {
	data, err := json.Marshal(parameter)
	if err != nil {
		log.Error("marshal activate request error", zap.Error(err))
		return err
	}
	url := factory.WorkerStatusEndpoint(worker, a.namespace)
	req, err := a.manager.NewRequest(ctx, http.MethodPost, url, activatePath, bytes.NewReader(data))
	if err != nil {
		log.Error("create http request error", zap.Error(err))
		return err
	}

	resp, err := a.manager.Do(req)
	if err != nil {
		log.Error("do http request error", zap.Error(err))
		return err
	}

	defer resp.Body.Close() //nolint:errcheck // no need
	switch resp.StatusCode {
	case http.StatusOK:
		return nil
	case http.StatusPreconditionFailed:
		return tidb.ErrActivateByOtherToken
	case http.StatusRequestTimeout:
		return tidb.ErrActivateTimeout
	default:
		body, err := io.ReadAll(resp.Body)
		if err != nil {
			log.Error("read body error", zap.Error(err))
			return err
		}
		return fmt.Errorf("activate worker error: %s", string(body))
	}
}

func (a *activatorImpl) RecycleWorker(
	ctx context.Context,
	_ rm.WorkerPool,
	worker rm.Worker,
	keyspaceName string,
) error {
	path := exitPath
	if keyspaceName != "" {
		path += fmt.Sprintf("?keyspace=%s", keyspaceName)
	}
	url := factory.WorkerStatusEndpoint(worker, a.namespace)
	req, err := a.manager.NewRequest(ctx, http.MethodPost, url, path, nil)
	if err != nil {
		log.Error("create http request error", zap.Error(err))
		return err
	}

	resp, err := a.manager.Do(req)
	if err != nil {
		log.Error("do http request error", zap.Error(err))
		return err
	}

	defer resp.Body.Close() //nolint:errcheck // no need
	if resp.StatusCode != http.StatusOK {
		body, err := io.ReadAll(resp.Body)
		if err != nil {
			log.Error("read body error", zap.Error(err))
			return err
		}
		return fmt.Errorf("recycle worker error: status: %s, body: %s", resp.Status, body)
	}
	return nil
}
