package regionalserver

import (
	"context"
	"encoding/json"
	"net/http"

	"github.com/go-resty/resty/v2"

	regionalserveradapter "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/adapter/regionalserver"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

type GetClusterAgentPublicKeyResponse struct {
	Pem []byte `json:"pem"`
}

func (s *regionalServerCliImpl) GetClusterAgentPublicKey(
	ctx context.Context,
	region *entity.Region,
	clusterId, sharedPoolId string,
) ([]byte, bizerr.BizError) {
	resp, err := s.callRegion(
		ctx,
		region,
		http.MethodGet,
		operationMap["GetClusterAgentPublicKey"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(clusterIdParameter, clusterId)
			request.SetPathParam(sharedPoolIdPathParameter, sharedPoolId)
			return request
		},
	)
	if err != nil {
		return nil, err
	}

	var out GetClusterAgentPublicKeyResponse
	if err := json.Unmarshal(resp.Body(), &out); err != nil {
		return nil, bizerr.New(bizerr.ErrCodeRegionalAPI, "%s", err.Error())
	}

	return out.Pem, nil
}

func (s *regionalServerCliImpl) GetClusterAgentPublicKeyViaSharedPool(
	ctx context.Context,
	region *entity.Region,
	sharedPoolId string,
) ([]byte, bizerr.BizError) {
	resp, err := s.callRegion(
		ctx,
		region,
		http.MethodGet,
		operationMap["GetClusterAgentPublicKeyViaSharedPool"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(sharedPoolIdPathParameter, sharedPoolId)
			return request
		},
	)
	if err != nil {
		return nil, err
	}

	var out GetClusterAgentPublicKeyResponse
	if err := json.Unmarshal(resp.Body(), &out); err != nil {
		return nil, bizerr.New(bizerr.ErrCodeRegionalAPI, "%s", err.Error())
	}

	return out.Pem, nil
}

func (s *regionalServerCliImpl) SetPassword(
	ctx context.Context,
	region *entity.Region,
	clusterId, sharedPoolId string,
	req *regionalserveradapter.SetPasswordRequest,
) bizerr.BizError {
	if req == nil {
		return bizerr.New(bizerr.ErrCodeParams, "SetPasswordRequest is nil")
	}
	if len(req.EncryptedPassword) == 0 {
		return bizerr.New(bizerr.ErrCodeParams, "EncryptedPassword is required")
	}
	if req.Target == 0 {
		return bizerr.New(bizerr.ErrCodeParams, "Target is required")
	}
	// Use the long-timeout client: the cluster-agent V1 fallback (privileged TiDB)
	// can take ~30s to deactivate from another keyspace before it can run ALTER USER.
	_, err := s.callRegionWithClient(
		ctx,
		s.setPasswordClient,
		region,
		http.MethodPost,
		operationMap["SetPassword"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(clusterIdParameter, clusterId)
			request.SetPathParam(sharedPoolIdPathParameter, sharedPoolId)
			request.SetBody(req)
			return request
		},
	)
	return err
}

func (s *regionalServerCliImpl) SetPasswordViaSharedPool(
	ctx context.Context,
	region *entity.Region,
	sharedPoolId string,
	req *regionalserveradapter.SetPasswordRequest,
) bizerr.BizError {
	if req == nil {
		return bizerr.New(bizerr.ErrCodeParams, "SetPasswordRequest is nil")
	}
	if len(req.EncryptedPassword) == 0 {
		return bizerr.New(bizerr.ErrCodeParams, "EncryptedPassword is required")
	}
	if req.Target == 0 {
		return bizerr.New(bizerr.ErrCodeParams, "Target is required")
	}
	_, err := s.callRegionWithClient(
		ctx,
		s.setPasswordClient,
		region,
		http.MethodPost,
		operationMap["SetPasswordViaSharedPool"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(sharedPoolIdPathParameter, sharedPoolId)
			request.SetBody(req)
			return request
		},
	)
	return err
}

func (s *regionalServerCliImpl) ExecuteClusterAgentCommand(
	ctx context.Context,
	region *entity.Region,
	clusterId, sharedPoolId string,
	command string,
) (*regionalserveradapter.ExecuteCommandResponse, bizerr.BizError) {
	resp, err := s.callRegion(
		ctx,
		region,
		http.MethodPost,
		operationMap["ExecuteClusterAgentCommand"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(clusterIdParameter, clusterId)
			request.SetPathParam(sharedPoolIdPathParameter, sharedPoolId)
			request.SetBody(map[string]string{
				"command": command,
			})
			return request
		},
	)
	if err != nil {
		return nil, err
	}

	var out regionalserveradapter.ExecuteCommandResponse
	if unmarshalErr := json.Unmarshal(resp.Body(), &out); unmarshalErr != nil {
		return nil, bizerr.New(bizerr.ErrCodeRegionalAPI, "failed to unmarshal executeCommand response: %s", unmarshalErr.Error())
	}
	return &out, nil
}

func (s *regionalServerCliImpl) UpdateAuditLogSettingViaAgent(
	ctx context.Context,
	region *entity.Region,
	clusterId, sharedPoolId string,
	req *regionalserveradapter.UpdateAuditLogSettingViaAgentRequest,
) bizerr.BizError {
	_, err := s.callRegion(
		ctx,
		region,
		http.MethodPost,
		operationMap["UpdateAuditLogSettingViaAgent"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(clusterIdParameter, clusterId)
			request.SetPathParam(sharedPoolIdPathParameter, sharedPoolId)
			request.SetBody(req)
			return request
		},
	)
	return err
}

func (s *regionalServerCliImpl) SetGlobalCloudStorageURIViaAgent(
	ctx context.Context,
	region *entity.Region,
	clusterId, sharedPoolId string,
	req *regionalserveradapter.SetGlobalCloudStorageURIViaAgentRequest,
) bizerr.BizError {
	_, err := s.callRegion(
		ctx,
		region,
		http.MethodPost,
		operationMap["SetGlobalCloudStorageURIViaAgent"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(clusterIdParameter, clusterId)
			request.SetPathParam(sharedPoolIdPathParameter, sharedPoolId)
			request.SetBody(req)
			return request
		},
	)
	return err
}

func (s *regionalServerCliImpl) CreateAuditLogFilterRuleViaAgent(
	ctx context.Context,
	region *entity.Region,
	clusterId, sharedPoolId string,
	req *regionalserveradapter.CreateAuditLogFilterRuleViaAgentRequest,
) bizerr.BizError {
	_, err := s.callRegion(
		ctx,
		region,
		http.MethodPost,
		operationMap["CreateAuditLogFilterRuleViaAgent"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(clusterIdParameter, clusterId)
			request.SetPathParam(sharedPoolIdPathParameter, sharedPoolId)
			request.SetBody(req)
			return request
		},
	)
	return err
}

func (s *regionalServerCliImpl) DeleteAuditLogFilterRuleViaAgent(
	ctx context.Context,
	region *entity.Region,
	clusterId, sharedPoolId string,
	req *regionalserveradapter.DeleteAuditLogFilterRuleViaAgentRequest,
) bizerr.BizError {
	_, err := s.callRegion(
		ctx,
		region,
		http.MethodPost,
		operationMap["DeleteAuditLogFilterRuleViaAgent"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(clusterIdParameter, clusterId)
			request.SetPathParam(sharedPoolIdPathParameter, sharedPoolId)
			request.SetBody(req)
			return request
		},
	)
	return err
}

func (s *regionalServerCliImpl) UpdateAuditLogFilterRuleViaAgent(
	ctx context.Context,
	region *entity.Region,
	clusterId, sharedPoolId string,
	req *regionalserveradapter.UpdateAuditLogFilterRuleViaAgentRequest,
) bizerr.BizError {
	_, err := s.callRegion(
		ctx,
		region,
		http.MethodPost,
		operationMap["UpdateAuditLogFilterRuleViaAgent"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(clusterIdParameter, clusterId)
			request.SetPathParam(sharedPoolIdPathParameter, sharedPoolId)
			request.SetBody(req)
			return request
		},
	)
	return err
}

func (s *regionalServerCliImpl) UpdateLogRedactionPolicyViaAgent(
	ctx context.Context,
	region *entity.Region,
	clusterId, sharedPoolId string,
	req *regionalserveradapter.UpdateLogRedactionPolicyViaAgentRequest,
) bizerr.BizError {
	_, err := s.callRegion(
		ctx,
		region,
		http.MethodPost,
		operationMap["UpdateLogRedactionPolicyViaAgent"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(clusterIdParameter, clusterId)
			request.SetPathParam(sharedPoolIdPathParameter, sharedPoolId)
			request.SetBody(req)
			return request
		},
	)
	return err
}

func (s *regionalServerCliImpl) GetLogRedactionPolicyViaAgent(
	ctx context.Context,
	region *entity.Region,
	clusterId, sharedPoolId string,
	req *regionalserveradapter.GetLogRedactionPolicyViaAgentRequest,
) (*regionalserveradapter.GetLogRedactionPolicyViaAgentResponse, bizerr.BizError) {
	resp, err := s.callRegion(
		ctx,
		region,
		http.MethodPost,
		operationMap["GetLogRedactionPolicyViaAgent"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(clusterIdParameter, clusterId)
			request.SetPathParam(sharedPoolIdPathParameter, sharedPoolId)
			request.SetBody(req)
			return request
		},
	)
	if err != nil {
		return nil, err
	}
	var out regionalserveradapter.GetLogRedactionPolicyViaAgentResponse
	if uerr := json.Unmarshal(resp.Body(), &out); uerr != nil {
		return nil, bizerr.New(bizerr.ErrCodeRegionalAPI, "%s", uerr.Error())
	}
	return &out, nil
}
