//nolint:gomnd,lll
package errcode

import (
	"net/http"

	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/pkgv2/svcid"
	"google.golang.org/grpc/codes"
)

const (
	InvalidArgument  = "INVALID_ARGUMENT"
	Unauthenticated  = "UNAUTHENTICATED"
	PermissionDenied = "PERMISSION_DENIED"
	NotFound         = "NOT_FOUND"
	ResourceBusy     = "RESOURCE_BUSY"
	Internal         = "INTERNAL"

	Fallback = `{"code": 59900104, "message": "failed to marshal error message"}`
)

var (
	RequestErrCodeStart  bizerr.ErrCode = 40000000
	InternalErrCodeStart bizerr.ErrCode = 50000000
	BusinessErrCodeStart bizerr.ErrCode = 60000000

	BizErrCode2HttpStatus = map[bizerr.ErrCode]int{
		bizerr.ErrCodeOK: http.StatusOK,

		bizerr.ErrCodeParams:           http.StatusBadRequest,
		bizerr.ErrCodeAuth:             http.StatusUnauthorized,
		bizerr.ErrCodePermission:       http.StatusForbidden,
		bizerr.ErrCodeUnderMaintenance: http.StatusForbidden,
		bizerr.ErrCodeIPBlocked:        http.StatusForbidden,
		bizerr.ErrCodeRecordNotFound:   http.StatusNotFound,
		bizerr.ErrCodeRecordExist:      http.StatusConflict,
		bizerr.ErrCodeTooManyRequests:  http.StatusTooManyRequests,
		bizerr.ErrCodeUnknown:          http.StatusInternalServerError,

		bizerr.ErrCodeTimeout: http.StatusGatewayTimeout,

		// dataflow biz error codes
	}

	GrpcErrCode2BizErrCode = map[codes.Code]bizerr.ErrCode{
		codes.OK:                 bizerr.ErrCodeOK,
		codes.Canceled:           bizerr.ErrCodeTimeout,
		codes.Unknown:            bizerr.ErrCodeUnknown,
		codes.InvalidArgument:    bizerr.ErrCodeParams,
		codes.DeadlineExceeded:   bizerr.ErrCodeTimeout,
		codes.NotFound:           bizerr.ErrCodeRecordNotFound,
		codes.AlreadyExists:      bizerr.ErrCodeRecordExist,
		codes.PermissionDenied:   bizerr.ErrCodePermission,
		codes.ResourceExhausted:  bizerr.ErrCodeTooManyRequests,
		codes.FailedPrecondition: bizerr.ErrCodeGRPC,
		codes.Aborted:            bizerr.ErrCodeGRPC,
		codes.OutOfRange:         bizerr.ErrCodeParams,
		codes.Unimplemented:      bizerr.ErrCodeGRPC,
		codes.Internal:           bizerr.ErrCodeUnknown,
		codes.Unavailable:        bizerr.ErrCodeUnknown,
		codes.DataLoss:           bizerr.ErrCodeUnknown,
		codes.Unauthenticated:    bizerr.ErrCodeAuth,
	}

	HTTPStatus2ErrReasonMap = map[int]string{
		http.StatusOK:                  "",
		http.StatusBadRequest:          InvalidArgument,
		http.StatusUnauthorized:        Unauthenticated,
		http.StatusForbidden:           PermissionDenied,
		http.StatusNotFound:            NotFound,
		http.StatusConflict:            ResourceBusy,
		http.StatusInternalServerError: Internal,
	}
)

var CommonErrorCode = bizerr.NewBusinessErrCode(svcid.DataflowService, 10, "CommonErrorCode")

// common error message define
const (
	ErrMsgCRNotReady    = "cr not ready the cr key is %s kind is %s"
	ErrInternalError    = "internal error"
	ErrMsgNotFound      = "(id= %d) is not found"
	ErrInvalidParam     = "the %s[%v] is invalid"
	ErrEmptyParam       = "the value of %v is empty"
	ErrActionParam      = "under the current status:%v, prohibit the execution of the action:%v."
	ErrMsgInternalError = "Temporarily internal error for cluster id %d, please retry later. If retry can not solve the problem, please get support by following the instruction in https://docs.pingcap.com/tidbcloud/tidb-cloud-support#get-support-for-a-cluster"
)

// account error message define
const (
	ErrMsgOrgNotPermission     = "your org_id is %s, you do not have permission to access the resource of the org: %d"
	ErrMsgProjectNotPermission = "your project_id is %s, you do not have permission to access the resource of the project: %d"
)

// replication error code define
var (
	startReplicationErrorCodeOffset           = 10000
	ErrCodeClusterIDIsNotRelatedToReplication = bizerr.NewBusinessErrCode(svcid.DataflowService, startReplicationErrorCodeOffset+1, "ErrCodeClusterIDIsNotRelatedToReplication")
	ErrCodeCreateCascadeReplication           = bizerr.NewBusinessErrCode(svcid.DataflowService, startReplicationErrorCodeOffset+2, "ErrCodeCreateCascadeReplication")
	ErrCodeCreateMoreThanOneReplication       = bizerr.NewBusinessErrCode(svcid.DataflowService, startReplicationErrorCodeOffset+3, "ErrCodeCreateMoreThanOneReplication")
)

// replication error message define
const (
	ErrMsgClusterIDIsNotRelatedToReplication         = "cluster %d is not primary or secondary cluster of replication %d"
	ErrMsgReplicationID                              = "replication %d is not found"
	ErrMsgDetachTypeError                            = "detach type %s is not supported"
	ErrMsgCreateCascadeReplication                   = "cannot create one Secondary Cluster to another Secondary Cluster %d."
	ErrMsgCreateMoreThanOneReplication               = "cluster %d cannot create more than one replications."
	ErrMsgReplicationNotSupportDevTier               = "can't create replication for devTier cluster id=%d"
	ErrMsgCreateReplicationClusterComponentTypeError = "create cluster component type %s is not supported"
	ErrMsgCreateReplicationTypeError                 = "create replication type %s is not supported"
	ErrMsgReplicationStatusError                     = "replication status %s is not supported"
)

// cdc error code define
var (
	startCDCErrorCodeOffset              = 20000
	ErrCodeCDCInvalidParam               = bizerr.NewBusinessErrCode(svcid.DataflowService, startCDCErrorCodeOffset+1, "ErrCodeCDCInvalidParam")
	ErrCodeCDCNotallowDevTierErr         = bizerr.NewBusinessErrCode(svcid.DataflowService, startCDCErrorCodeOffset+2, "ErrCodeCDCNotallowDevTierErr")
	ErrCodeCDCFailedJsonMarshalErr       = bizerr.NewBusinessErrCode(svcid.DataflowService, startCDCErrorCodeOffset+3, "ErrCodeCDCFailedJsonMarshalErr")
	ErrCodeCDCFailedToGetConnectorErr    = bizerr.NewBusinessErrCode(svcid.DataflowService, startCDCErrorCodeOffset+4, "ErrCodeCDCFailedToGetConnectorErr")
	ErrCodeCDCFailedToConnectErr         = bizerr.NewBusinessErrCode(svcid.DataflowService, startCDCErrorCodeOffset+5, "ErrCodeCDCFailedToConnectErr")
	ErrCodeCDCCreatingErr                = bizerr.NewBusinessErrCode(svcid.DataflowService, startCDCErrorCodeOffset+6, "ErrCodeCDCCreatingErr")
	ErrCodeCDCImportingErr               = bizerr.NewBusinessErrCode(svcid.DataflowService, startCDCErrorCodeOffset+7, "ErrCodeCDCImportingErr")
	ErrCodeCDCRecoveringErr              = bizerr.NewBusinessErrCode(svcid.DataflowService, startCDCErrorCodeOffset+8, "ErrCodeCDCRecoveringErr")
	ErrCodeCDCNotAllowErr                = bizerr.NewBusinessErrCode(svcid.DataflowService, startCDCErrorCodeOffset+9, "ErrCodeCDCNotAllowErr")
	ErrCodeCDCGcSafePointNotAllow        = bizerr.NewBusinessErrCode(svcid.DataflowService, startCDCErrorCodeOffset+10, "ErrCodeCDCGcSafePointNotAllow")
	ErrCodeCalChangefeedFeeErr           = bizerr.NewBusinessErrCode(svcid.DataflowService, startCDCErrorCodeOffset+11, "ErrCodeCalChangefeedFeeErr")
	ErrCodeInstanceTypeNotFoundErr       = bizerr.NewBusinessErrCode(svcid.DataflowService, startCDCErrorCodeOffset+12, "ErrCodeInstanceTypeNotFoundErr")
	ErrCodeCDCPrivatelinkIsExpirationErr = bizerr.NewBusinessErrCode(svcid.DataflowService, startCDCErrorCodeOffset+13, "ErrCodeCDCPrivatelinkIsExpirationErr")
	ErrCodeChangefeedPerClusterExceeded  = bizerr.NewBusinessErrCode(svcid.DataflowService, startCDCErrorCodeOffset+14, "ErrCodeChangefeedPerClusterExceeded")
	ErrCodeClusterPausing                = bizerr.NewBusinessErrCode(svcid.DataflowService, startCDCErrorCodeOffset+15, "ErrCodeClusterPausing")
	ErrCodeClusterVersionNotSupport      = bizerr.NewBusinessErrCode(svcid.DataflowService, startCDCErrorCodeOffset+16, "ErrCodeClusterVersionNotSupport")
	ErrCodePrivateLinkKafkaIDUnmatched   = bizerr.NewBusinessErrCode(svcid.DataflowService, startCDCErrorCodeOffset+17, "ErrCodePrivateLinkKafkaIDUnmatched")
	ErrCodePrivateLinkKafkaIDConflict    = bizerr.NewBusinessErrCode(svcid.DataflowService, startCDCErrorCodeOffset+18, "ErrCodePrivateLinkKafkaIDConflict")
	ErrCodeCDCPrivateLinkNotAllowEditErr = bizerr.NewBusinessErrCode(svcid.DataflowService, startCDCErrorCodeOffset+19, "ErrCodeCDCPrivateLinkNotAllowEditErr")

	ErrCodeCreateKernelChangefeedFailed = bizerr.NewBusinessErrCode(svcid.DataflowService, startCDCErrorCodeOffset+1000, "ErrCodeCreateKernelChangefeedErr")
)

// cdc error message define
const (
	ErrMsgCDCInvalidParam                = "invalid input parameter"
	ErrMsgCDCNotallowDevTierErr          = "it is not allowed to create changefeed for dev tier clusters"
	ErrMsgCDCFailedJsonMarshalErr        = "json marshal failed"
	ErrMsgCDCFailedToGetConnectorErr     = "Failed to get changefeed"
	ErrMsgCDCFailedToConnectErr          = "Test downstream %s connection failed, because %s"
	ErrMsgCDCCreatingErr                 = "The cluster is being creating. Cannot create changefeed during `Creating` process."
	ErrMsgCDCRecoveringErr               = "The cluster is being recovering. Cannot create changefeed during `Recovering` process."
	ErrMsgCDCImportingErr                = "The cluster is being imported. Cannot create changefeed during `Importing` process."
	ErrMsgCDCDeleteTiDBErr               = "can not delete tidb changefeed here, please use detach replication action"
	ErrMsgCDCStartNotAllowErr            = "current changefeed status %s can not start"
	ErrMsgCDCStopNotAllowErr             = "current changefeed status %s can not stop"
	ErrMsgCDCEditNotAllowErr             = "current changefeed status %s can not edit"
	ErrMsgCDCScaleNotAllowErr            = "current changefeed status %s can not scale"
	ErrMsgCDCArchScaleNotAllowErr        = "current changefeed can not scale"
	ErrMsgCDCTypeNotAllowErr             = "can not suppot edit action, %s"
	ErrMsgCDCVersionNotAllowErr          = "current version not support"
	ErrMsgCDCGcSafePointNotAllowErr      = "startTso %d is not greater than gc safe point %d"
	ErrMsgDownStreamInfoIsEmpty          = "downstream info is empty"
	ErrMsgChangeFeedParamsError          = "check %s changefeed params error"
	ErrMsgInstanceTypeIsEmpty            = "changefeed instance type is empty"
	ErrMsgCDCPrivatelinkIsExpirationErr  = "The network connection is timed out and has been recycled. Please go back to the previous step then next to trigger network connection recreation."
	ErrMsgCDCNotSupportRCUInstanceType   = "current cluster only support 16rcu instance type"
	ErrMsgConnectToMySQL_AccessDenied    = "the user or password is incorrect"
	ErrMsgCDCAWSRegionNotSameWithCluster = "Bucket's region: %s should be same as the cluster: %s"
	ErrMsgCDCGCPRegionNotSameWithCluster = "Bucket's location: %s should contain or be same as cluster's region: %s"
	ErrMsgCDCInvalidTableFilter          = "invalid table filter parameter, %s"
	ErrMsgChangefeedPerClusterExceeded   = "There are already %d changefeeds in the cluster, which has reached the limit. If you need to create more, please submit a ticket."
	ErrMsgClusterPausing                 = "The cluster is being pausing or paused. Cannot create changefeed during `Pause` process."
	ErrMsgClusterVersionNotSupport       = "current tidb cluster version %s does not support %s, version constraint is %s"
	ErrMsgPrivateLinkKafkaIDUnmatched    = "One of your TiDB cluster has previously connected to the same Kafka service and used another ID(%s) for Kafka Advertised Listener Pattern. Please reuse the previous ID of the Kafka service."
	ErrMsgPrivateLinkKafkaIDConflict     = "One of your TiDB clusters has previously connected to another Kafka service(%s) using the same ID for Kafka Advertised Listener Pattern. Please use a new ID for Kafka Advertised Listener Pattern."
	ErrMsgCDCPrivateLinkNotAllowEditErr  = "Changefeed using private link can not be edited"
)

// bucket error code define
var (
	startBucketErrorCodeOffset = 30000

	ErrCodeBucketsNotFound      = bizerr.NewBusinessErrCode(svcid.DataflowService, startBucketErrorCodeOffset+3, "ErrCodeBucketsNotFound")
	ErrCodeBucketNotUnique      = bizerr.NewBusinessErrCode(svcid.DataflowService, startBucketErrorCodeOffset+4, "ErrCodeBucketNotUnique")
	ErrCodeBucketRegionEmpty    = bizerr.NewBusinessErrCode(svcid.DataflowService, startBucketErrorCodeOffset+5, "ErrCodeBucketRegionEmpty")
	ErrCodeBucketTooManyRegions = bizerr.NewBusinessErrCode(svcid.DataflowService, startBucketErrorCodeOffset+6, "ErrCodeBucketTooManyRegions")
)

// bucket error message define
var (
	ErrMsgBucketNotFound = "bucket is not found id is %d"

	ErrMsgBucketsNotFound           = "buckets is not found filter is %+v"
	ErrMsgBucketNotUnique           = "bucket is not unique filter is %+v"
	ErrMsgCreateBucketCRError       = "bucket create cr meet error id is %d"
	ErrMsgDeleteBucketCRError       = "bucket delete cr meet error id is %d"
	ErrMsgDeleteBucketPolicyCRError = "delete bucket policy cr meet error id is %d"

	ErrMsgMakeupCRError      = "bucket makeup cr meet error id is %d"
	ErrMsgGetNetworkByBucket = "getNetworkByBucket failed bucket id is %d"

	ErrMsgAWSBucketConfigurationNotFound = "aws bucket configuration id is not found id is %d"
	ErrMsgAWSBucketReplicationNotFound   = "aws bucket replication id is not found id is %d"
)

// backupv2 error code define
var (
	startBackupErrorCodeOffset                       = 40000
	ErrCodeAsyncCreateBackupPreCheckDuplicateName    = bizerr.NewBusinessErrCode(svcid.DataflowService, startBackupErrorCodeOffset+1, "ErrCodeAsyncCreateBackupPreCheckDuplicateName")
	ErrCodeAsyncCreateBackupPreCheckRunningBackups   = bizerr.NewBusinessErrCode(svcid.DataflowService, startBackupErrorCodeOffset+2, "ErrCodeAsyncCreateBackupPreCheckRunningBackups")
	ErrCodeAsyncCreateBackupPreCheckClusterNotNormal = bizerr.NewBusinessErrCode(svcid.DataflowService, startBackupErrorCodeOffset+3, "ErrCodeAsyncCreateBackupPreCheckClusterNotNormal")
	ErrCodeAsyncCreateBackupPreCheckRunningImports   = bizerr.NewBusinessErrCode(svcid.DataflowService, startBackupErrorCodeOffset+4, "ErrCodeAsyncCreateBackupPreCheckRunningImports")

	ErrCodePitBackupPreCheckClusterVersion   = bizerr.NewBusinessErrCode(svcid.DataflowService, startBackupErrorCodeOffset+5, "ErrCodePitBackupPreCheckClusterVersion")
	ErrCodePitBackupPreCheckClusterStatus    = bizerr.NewBusinessErrCode(svcid.DataflowService, startBackupErrorCodeOffset+6, "ErrCodePitBackupPreCheckClusterStatus")
	ErrCodeBackupSettingChangeSimultaneously = bizerr.NewBusinessErrCode(svcid.DataflowService, startBackupErrorCodeOffset+11, "ErrCodeBackupSettingChangeSimultaneously")
	ErrCodeBackupSettingIsNotReady           = bizerr.NewBusinessErrCode(svcid.DataflowService, startBackupErrorCodeOffset+12, "ErrCodeBackupSettingIsNotReady")

	ErrCodeAsyncCreateBackupPreCheckOperatingRecoveryGroups = bizerr.NewBusinessErrCode(svcid.DataflowService, startBackupErrorCodeOffset+13, "ErrCodeAsyncCreateBackupPreCheckOperatingRecoveryGroups")

	ErrCodeNegativePageSize = bizerr.NewBusinessErrCode(svcid.DataflowService, 14, "ErrCodeNegativePageSize")
	ErrCodeInvalidPageToken = bizerr.NewBusinessErrCode(svcid.DataflowService, 15, "ErrCodeInvalidPageToken")
)

// backupv2 error message define
const (
	ErrMsgCanNotDeleteFinalBackup                   = "Final backup of cluster id %d is not allow to delete"
	ErrMsgAsyncCreateBackupReqNotValid              = "Create backup request for cluster id %d is invalid, %s"
	ErrMsgAsyncCreateBackupPreCheckDuplicateName    = "Duplicate backup name %s for cluster id %d"
	ErrMsgAsyncCreateBackupPreCheckRunningBackups   = "Another backup is running for cluster id %d, please try later"
	ErrMsgAsyncCreateBackupPreCheckClusterNotNormal = "Cluster id %d status is %s, not normal, please try later"
	ErrMsgAsyncCreateBackupPreCheckRunningImports   = "There is import job running for cluster id %d, if you want to make a backup, please wait for import job complete or just cancel import job first"
	ErrMsgAsyncDeleteBackupPreCheckRunningRestore   = "There is restore job running for backup id %d, if you want to delete this backup, please wait for restore job complete or just cancel restore job first"
	ErrMsgAsyncDeleteBackupPreCheckRunningExport    = "There is export job running for backup id %d, if you want to delete this backup, please wait for export job complete or just cancel export job first"

	ErrMsgPitBackupPreCheckClusterVersion = "Cluster id %d version %s too low to enable PitBackup, the required minimal cluster version is %s, please upgrade your cluster first"
	ErrMsgPitBackupPreCheckClusterStatus  = "Cluster id %d status is not normal(%s), please try later"

	ErrMsgBackupSettingChangeSimultaneously                       = "The setting of dual region and pitr cannot be changed simultaneously"
	ErrMsgBackupSettingChangeSimultaneouslyDueToDisablingSchedule = "The setting of dual region and pitr cannot be changed simultaneously, due to disabling automatic snapshot schedule"
	ErrMsgModifyBackupSettingInputIsEmpty                         = "Require snapshot backup schedule, continuous backup schedule or regions at least one"
	ErrMsgWeeklyScheduleMustBeUsedWithPitr                        = "Weekly schedule must be used with pitr"
	ErrMsgBackupSettingIsNotReady                                 = "Backup setting is not ready, please try later"

	ErrMsgAsyncCreateBackupPreCheckOperatingRecoveryGroups = "There is recovery group operating %d for cluster id %d, if you want to make a backup, please wait for recovery group operating over"
)

// restorev2 error message define
const (
	ErrMsgBackupNotFound                = "backup(id= %d) is not found"
	ErrMsgRestoreNotFound               = "restore is not found"
	ErrMsgBackupStatusIsNotAllowRestore = "backup %d status is not allow restore"
	ErrMsgComponentStorageSizeInvalid   = "requested storage size of component %s is less than the storage size of backup cluster"
	ErrMsgComponentNodeQuantityInvalid  = "requested node quantity of component %s is less than the node quantity of backup cluster"
	ErrMsgRestoreTsNotAllow             = "it can't restore to ts, because it is not in the range of backup ts"
	ErrMsgTikvStorageSizeInvalid        = "The storage size of each tikv node should be at least '%d' GiB, and it is recommended to reserve more"
)

// DWR error code define
var (
	startDWRErrorCodeOffset = 50000
	ErrCodeDWRConnectFailed = bizerr.NewBusinessErrCode(svcid.DataflowService, startDWRErrorCodeOffset+2, "ErrCodeDWRConnectFailed")
)

var (
	startNodeGroupOutSideErrorCodeOffset      = 60000
	ErrCodeScalingNodeGroupSizeAreAlreadySame = bizerr.NewBusinessErrCode(svcid.DataflowService, startNodeGroupOutSideErrorCodeOffset+1, "ErrCodeScalingNodeGroupSizeAreAlreadySame")
	startNodeGroupInternelErrorCodeOffset     = 65000
)

const (
	ErrMsgScalingNodeGroupSizeAreAlreadySame = "Scaling node group size is already the same"
)

// recovery group
var (
	startRecoveryGroupOutSideErrorCodeOffset          = 70000
	ErrCodeRecoveryGroupPreCheckClusterVersion        = bizerr.NewBusinessErrCode(svcid.DataflowService, startRecoveryGroupOutSideErrorCodeOffset+1, "ErrCodeRecoveryGroupPreCheckClusterVersion")
	ErrCodeOperateRecoveryGroupPreCheckRunningImports = bizerr.NewBusinessErrCode(svcid.DataflowService, startRecoveryGroupOutSideErrorCodeOffset+2, "ErrCodeOperateRecoveryGroupPreCheckRunningImports")
	ErrCodeOperateRecoveryGroupPreCheckRunningBackups = bizerr.NewBusinessErrCode(svcid.DataflowService, startRecoveryGroupOutSideErrorCodeOffset+3, "ErrCodeOperateRecoveryGroupPreCheckRunningBackups")

	startRecoveryGroupInternelErrorCodeOffset = 75000
)

const (
	ErrMsgRecoveryGroupPreCheckClusterVersion        = "Cluster id %d version %s too low to create recovery group, the required minimal cluster version is %s, please upgrade your cluster first"
	ErrMsgOperateRecoveryGroupPreCheckRunningImports = "There is import job running for cluster id %d, if you want to operate recovery group, please wait for import job complete or just cancel import job first"
	ErrMsgOperateRecoveryGroupPreCheckRunningBackups = "There is backup job running for cluster id %s, if you want to operate recovery group, please wait for backup job complete or just cancel backup job first"
)

func HttpStatus2ErrReason(httpCode int) string {
	if code, ok := HTTPStatus2ErrReasonMap[httpCode]; ok {
		return code
	}
	return Internal
}

func ConvertBizErrorToHTTPStatus(code bizerr.ErrCode) int {
	if state, ok := BizErrCode2HttpStatus[code]; ok {
		return state
	}
	if code >= RequestErrCodeStart && code < InternalErrCodeStart {
		return http.StatusBadRequest
	}

	if code >= InternalErrCodeStart {
		return http.StatusInternalServerError
	}

	return http.StatusInternalServerError
}
