package regionalserver

import (
	"context"
	"fmt"

	clusterngv1beta "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	dataplanemanagerv1 "github.com/tidbcloud/cluster-service-ng/api/tidbcloud/dataplane-manager/v1"

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

type ExecuteCommandResponse struct {
	StdoutMsg string `json:"stdoutMsg"`
	StderrMsg string `json:"stderrMsg"`
	ExitCode  *int32 `json:"exitCode,omitempty"`
}

//go:generate go run go.uber.org/mock/mockgen@v0.6.0 -source=$GOFILE -package=mock_$GOPACKAGE -imports=v1beta=github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta,v1=github.com/tidbcloud/cluster-service-ng/api/tidbcloud/dataplane-manager/v1 -destination=./mock_$GOPACKAGE/mock_$GOFILE
type Client interface {
	// Ping service
	// Ping(ctx context.Context, region *entity.Region, req *emptypb.Empty) (*regionaldedicatedv1.Pong, bizerr.BizError)

	// TidbCluster Service
	CreateTidbCluster(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.CreateTiDBClusterRequest,
	) (*clusterngv1beta.Cluster, bizerr.BizError)
	GetTidbCluster(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.GetTiDBClusterRequest,
	) (*clusterngv1beta.Cluster, bizerr.BizError)
	DeleteTidbCluster(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.DeleteTiDBClusterRequest,
	) bizerr.BizError
	UpdateTidbCluster(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.UpdateTiDBClusterRequest,
	) (*clusterngv1beta.Cluster, bizerr.BizError)
	ForceUpdateGatewayIdentity(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.ForceUpdateGatewayIdentityRequest,
	) bizerr.BizError
	GetProviderRole(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.GetProviderRoleRequest,
	) (*clusterngv1beta.ProviderRole, bizerr.BizError)
	SetAuditLogSetting(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.SetAuditLogSettingRequest,
	) (*clusterngv1beta.Cluster_AuditLogSetting, bizerr.BizError)

	// TidbGroup Service
	CreateTidbGroup(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.CreateTiDBGroupRequest,
	) (*clusterngv1beta.TiDBGroup, bizerr.BizError)
	GetTidbGroup(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.GetTiDBGroupRequest,
	) (*clusterngv1beta.TiDBGroup, bizerr.BizError)
	DeleteTidbGroup(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.DeleteTiDBGroupRequest,
	) bizerr.BizError
	UpdateTidbGroup(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.UpdateTiDBGroupRequest,
	) (*clusterngv1beta.TiDBGroup, bizerr.BizError)
	ScaleTiDBGroup(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.ScaleTiDBGroupRequest,
	) (*clusterngv1beta.TiDBGroup, bizerr.BizError)
	CreatePrivateLinkService(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.CreatePrivateLinkServiceRequest,
	) (*clusterngv1beta.PrivateLinkService, bizerr.BizError)
	DeletePrivateLinkService(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.DeletePrivateLinkServiceRequest,
	) bizerr.BizError
	UpdatePrivateLinkService(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.UpdatePrivateLinkServiceRequest,
	) (*clusterngv1beta.PrivateLinkService, bizerr.BizError)
	GetPrivateLinkService(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.GetPrivateLinkServiceRequest,
	) (*clusterngv1beta.PrivateLinkService, bizerr.BizError)
	ListVpcPeerings(
		ctx context.Context,
		region *entity.Region,
		req *dataplanemanagerv1.ListVpcPeeringsRequest,
	) (*dataplanemanagerv1.ListVpcPeeringsResponse, bizerr.BizError)
	GetVpcPeering(
		ctx context.Context,
		region *entity.Region,
		req *dataplanemanagerv1.GetVpcPeeringRequest,
	) (*dataplanemanagerv1.VpcPeering, bizerr.BizError)
	GetDataPlane(
		ctx context.Context,
		region *entity.Region,
		req *dataplanemanagerv1.GetDataPlaneRequest,
	) (*dataplanemanagerv1.DataPlane, bizerr.BizError)

	// BR Service
	ListLightweightBackups(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.ListLightweightBackupsRequest,
	) (*clusterngv1beta.ListLightweightBackupsResponse, bizerr.BizError)
	CreateLightweightRestore(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.CreateLightweightRestoreRequest,
	) bizerr.BizError
	GetLightweightRestore(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.GetLightweightRestoreRequest,
	) (*clusterngv1beta.LightweightRestore, bizerr.BizError)
	GetLightweightRestorePostActions(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.GetLightweightRestorePostActionsRequest,
	) (*clusterngv1beta.GetLightweightRestorePostActionsResponse, bizerr.BizError)
	DeleteLightweightRestore(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.DeleteLightweightRestoreRequest,
	) bizerr.BizError
	CreateClassicRestore(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.CreateClassicRestoreRequest,
	) bizerr.BizError
	GetClassicRestore(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.GetClassicRestoreRequest,
	) (*clusterngv1beta.ClassicRestore, bizerr.BizError)
	DeleteClassicRestore(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.DeleteClassicRestoreRequest,
	) bizerr.BizError
	TestClassicBackupAccess(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.TestClassicBackupAccessRequest,
	) (*clusterngv1beta.TestClassicBackupAccessResponse, bizerr.BizError)
	GetBackupGcSetting(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.GetBackupGcSettingRequest,
	) (*clusterngv1beta.BackupGcSetting, bizerr.BizError)
	UpdateBackupGcSetting(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.UpdateBackupGcSettingRequest,
	) (*clusterngv1beta.BackupGcSetting, bizerr.BizError)
	UpdateAutoBackupMetering(
		ctx context.Context,
		region *entity.Region,
		req *AutoBackupMeteringRequest,
	) bizerr.BizError
	ReportAutoBackupMetering(
		ctx context.Context,
		region *entity.Region,
		req *ReportAutoBackupMeteringRequest,
	) bizerr.BizError

	// Manual Backup
	CreateManualBackup(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.CreateManualBackupRequest,
	) bizerr.BizError
	GetManualBackup(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.GetManualBackupRequest,
	) (*clusterngv1beta.ManualBackup, bizerr.BizError)
	DeleteManualBackup(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.DeleteManualBackupRequest,
	) bizerr.BizError

	// Manual Backup Restore
	CreateManualBackupRestore(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.CreateManualBackupRestoreRequest,
	) bizerr.BizError
	GetManualBackupRestore(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.GetManualBackupRestoreRequest,
	) (*clusterngv1beta.ManualBackupRestore, bizerr.BizError)
	GetManualBackupRestorePostActions(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.GetManualBackupRestorePostActionsRequest,
	) (*clusterngv1beta.GetManualBackupRestorePostActionsResponse, bizerr.BizError)
	DeleteManualBackupRestore(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.DeleteManualBackupRestoreRequest,
	) bizerr.BizError

	// cluster-agent Service
	GetClusterAgentPublicKey(
		ctx context.Context,
		region *entity.Region,
		clusterId, sharedPoolId string,
	) ([]byte, bizerr.BizError)

	// GetClusterAgentPublicKeyViaSharedPool gets public key via shared pool route (for System TiDB)
	GetClusterAgentPublicKeyViaSharedPool(
		ctx context.Context,
		region *entity.Region,
		sharedPoolId string,
	) ([]byte, bizerr.BizError)

	SetPassword(
		ctx context.Context,
		region *entity.Region,
		clusterId, sharedPoolId string,
		req *SetPasswordRequest,
	) bizerr.BizError

	// SetPasswordViaSharedPool sets password via shared pool route (for System TiDB)
	SetPasswordViaSharedPool(
		ctx context.Context,
		region *entity.Region,
		sharedPoolId string,
		req *SetPasswordRequest,
	) bizerr.BizError

	// cluster agent executeCommand
	ExecuteClusterAgentCommand(
		ctx context.Context,
		region *entity.Region,
		clusterId, sharedPoolId string,
		command string,
	) (*ExecuteCommandResponse, bizerr.BizError)

	UpdateAuditLogSettingViaAgent(
		ctx context.Context,
		region *entity.Region,
		clusterId, sharedPoolId string,
		req *UpdateAuditLogSettingViaAgentRequest,
	) bizerr.BizError
	SetGlobalCloudStorageURIViaAgent(
		ctx context.Context,
		region *entity.Region,
		clusterId, sharedPoolId string,
		req *SetGlobalCloudStorageURIViaAgentRequest,
	) bizerr.BizError
	CreateAuditLogFilterRuleViaAgent(
		ctx context.Context,
		region *entity.Region,
		clusterId, sharedPoolId string,
		req *CreateAuditLogFilterRuleViaAgentRequest,
	) bizerr.BizError
	DeleteAuditLogFilterRuleViaAgent(
		ctx context.Context,
		region *entity.Region,
		clusterId, sharedPoolId string,
		req *DeleteAuditLogFilterRuleViaAgentRequest,
	) bizerr.BizError
	UpdateAuditLogFilterRuleViaAgent(
		ctx context.Context,
		region *entity.Region,
		clusterId, sharedPoolId string,
		req *UpdateAuditLogFilterRuleViaAgentRequest,
	) bizerr.BizError
	UpdateLogRedactionPolicyViaAgent(
		ctx context.Context,
		region *entity.Region,
		clusterId, sharedPoolId string,
		req *UpdateLogRedactionPolicyViaAgentRequest,
	) bizerr.BizError
	GetLogRedactionPolicyViaAgent(
		ctx context.Context,
		region *entity.Region,
		clusterId, sharedPoolId string,
		req *GetLogRedactionPolicyViaAgentRequest,
	) (*GetLogRedactionPolicyViaAgentResponse, bizerr.BizError)

	// SharedPool Service
	CreateSharedPool(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.CreateSharedPoolRequest,
	) (*clusterngv1beta.SharedPool, bizerr.BizError)
	GetSharedPool(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.GetSharedPoolRequest,
	) (*clusterngv1beta.SharedPool, bizerr.BizError)
	ValidateSharedPoolPause(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.ValidateSharedPoolPauseRequest,
	) (*clusterngv1beta.ValidateSharedPoolPauseResponse, bizerr.BizError)
	PauseSharedPool(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.PauseSharedPoolRequest,
	) (*clusterngv1beta.SharedPool, bizerr.BizError)
	ResumeSharedPool(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.ResumeSharedPoolRequest,
	) (*clusterngv1beta.SharedPool, bizerr.BizError)
	GetPauseResumeOperation(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.GetPauseResumeOperationRequest,
	) (*clusterngv1beta.PauseResumeOperation, bizerr.BizError)
	DeleteSharedPool(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.DeleteSharedPoolRequest,
	) bizerr.BizError
	UpdateSharedPoolVcpusLimit(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.UpdateSharedPoolVcpusLimitRequest,
	) (*clusterngv1beta.SharedPool, bizerr.BizError)
	GetSharedPoolVcpusUsage(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.GetSharedPoolVcpusUsageRequest,
	) (*clusterngv1beta.GetSharedPoolVcpusUsageResponse, bizerr.BizError)
	GetSharedPoolScaleConstraints(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.GetSharedPoolScaleConstraintsRequest,
	) (*clusterngv1beta.GetSharedPoolScaleConstraintsResponse, bizerr.BizError)
	ScaleSharedPool(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.ScaleSharedPoolRequest,
	) (*clusterngv1beta.SharedPool, bizerr.BizError)
	ListSharedPools(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.ListSharedPoolsRequest,
	) (*clusterngv1beta.ListSharedPoolsResponse, bizerr.BizError)
	ListNodeSpecs(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.ListNodeSpecsRequest,
	) (*clusterngv1beta.ListNodeSpecsResponse, bizerr.BizError)
	GetSharedPoolProvisionConfiguration(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.GetSharedPoolProvisionConfigurationRequest,
	) (*clusterngv1beta.SharedPoolProvisionConfiguration, bizerr.BizError)
	EvaluateSharedPoolNetworkCapacity(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.EvaluateSharedPoolNetworkCapacityRequest,
	) (*clusterngv1beta.EvaluateSharedPoolNetworkCapacityResponse, bizerr.BizError)
	UpdateSharedPoolCloudResourceTags(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.UpdateSharedPoolCloudResourceTagsRequest,
	) (*clusterngv1beta.SharedPool, bizerr.BizError)
	SetKongAuditLogSetting(
		ctx context.Context,
		region *entity.Region,
		req *dataplanemanagerv1.SetKongAuditLogSettingRequest,
	) bizerr.BizError
	CreateDefaultEncryptionKey(
		ctx context.Context,
		region *entity.Region,
		req *dataplanemanagerv1.CreateKeyRequest,
	) bizerr.BizError

	// CheckKms asks the regional data-plane to verify it can actually access
	// the supplied KMS key (currently AWS only). 200 with empty body = success;
	// any other status is returned as a BizError.
	CheckKms(
		ctx context.Context,
		region *entity.Region,
		req *CheckKmsRequest,
	) bizerr.BizError
	GetDefaultEncryptionKey(
		ctx context.Context,
		region *entity.Region,
		req *dataplanemanagerv1.GetKeyRequest,
	) (*dataplanemanagerv1.Key, bizerr.BizError)
	AddEncryptionKeyTrustedDataplane(
		ctx context.Context,
		region *entity.Region,
		req *dataplanemanagerv1.AddTrustedDataplaneRequest,
	) bizerr.BizError
	GetEncryptionKeyTrustedDataplane(
		ctx context.Context,
		region *entity.Region,
		req *dataplanemanagerv1.GetTrustedDataplaneRequest,
	) (*dataplanemanagerv1.GetTrustedDataplaneResponse, bizerr.BizError)

	// Export Service
	CreateExport(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.CreateExportRequest,
	) (*clusterngv1beta.Export, bizerr.BizError)
	GetExport(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.GetExportRequest,
	) (*clusterngv1beta.Export, bizerr.BizError)
	CancelExport(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.CancelExportRequest,
	) bizerr.BizError
	CleanExport(
		ctx context.Context,
		region *entity.Region,
		req *clusterngv1beta.CancelExportRequest,
	) bizerr.BizError
}

// AutoBackupMeteringRequest intentionally mirrors the Regional API JSON body.
// Keeping this request local lets Global remain compatible while Regional is
// rolled out independently with the new optional flexible fields.
type AutoBackupMeteringRequest struct {
	ClusterID          string  `json:"clusterId"`
	SkuName            string  `json:"skuName"`
	SkuValue           string  `json:"skuValue"`
	Mode               string  `json:"mode"`
	RetentionDays      *uint32 `json:"retentionDays,omitempty"`
	DailyBackupTimeUTC *string `json:"dailyBackupTimeUtc,omitempty"`
}

type AutoBackupMeteringBackup struct {
	BackupID        string `json:"backupId"`
	BackupSizeBytes uint64 `json:"backupSizeBytes"`
}

type ReportAutoBackupMeteringRequest struct {
	ClusterID    string                     `json:"clusterId"`
	SharedPoolID string                     `json:"sharedPoolId"`
	Timestamp    int64                      `json:"timestamp,omitempty"`
	Backups      []AutoBackupMeteringBackup `json:"backups"`
}

// PasswordResetTarget identifies which database user's password to reset.
type PasswordResetTarget int32

const (
	PasswordResetTargetUnspecified      PasswordResetTarget = 0
	PasswordResetTargetRoot             PasswordResetTarget = 1
	PasswordResetTargetCloudAdmin       PasswordResetTarget = 2
	PasswordResetTargetPlatformAdmin    PasswordResetTarget = 3
	PasswordResetTargetMaintenanceAdmin PasswordResetTarget = 4
)

// MapUsernameToTarget maps username to PasswordResetTarget.
// Note: The returned numeric values must stay in sync with the cluster-agent
// PasswordResetTarget enum definition (server-side API contract).
func MapUsernameToTarget(username string) (PasswordResetTarget, error) {
	switch username {
	case clusterproxy.RootUser:
		return PasswordResetTargetRoot, nil
	case clusterproxy.CloudAdminUser:
		return PasswordResetTargetCloudAdmin, nil
	case clusterproxy.PlatformAdminUser:
		return PasswordResetTargetPlatformAdmin, nil
	case clusterproxy.MaintenanceAdminUser:
		return PasswordResetTargetMaintenanceAdmin, nil
	default:
		return PasswordResetTargetUnspecified, fmt.Errorf("unsupported username for password reset: %s", username)
	}
}

type SetPasswordRequest struct {
	EncryptedPassword []byte              `json:"encrypted_password"`
	Target            PasswordResetTarget `json:"target"`
	UseTidbWorker     bool                `json:"use_tidb_worker,omitempty"`
}

type UpdateAuditLogSettingViaAgentRequest struct {
	EncryptedUsername []byte `json:"encrypted_username"`
	EncryptedPassword []byte `json:"encrypted_password"`
	Enabled           bool   `json:"enabled"`
	Unredacted        bool   `json:"unredacted"`
	Format            string `json:"format"`
	MaxSizeMib        int32  `json:"max_size_mib"`
	MaxAgeSecond      int32  `json:"max_age_second"`
}

type SetGlobalCloudStorageURIViaAgentRequest struct {
	EncryptedUsername []byte `json:"encrypted_username"`
	EncryptedPassword []byte `json:"encrypted_password"`
	CloudStorageUri   string `json:"cloud_storage_uri"`
}

type CreateAuditLogFilterRuleViaAgentRequest struct {
	EncryptedUsername []byte   `json:"encrypted_username"`
	EncryptedPassword []byte   `json:"encrypted_password"`
	UserExpr          string   `json:"user_expr"`
	DisplayName       string   `json:"display_name"`
	EventTypes        []string `json:"event_types"`
	Tables            []string `json:"tables"`
	StatusCodes       []int32  `json:"status_codes"`
	Disabled          bool     `json:"disabled"`
}

type DeleteAuditLogFilterRuleViaAgentRequest struct {
	EncryptedUsername []byte `json:"encrypted_username"`
	EncryptedPassword []byte `json:"encrypted_password"`
	UserExpr          string `json:"user_expr"`
}

type UpdateAuditLogFilterRuleViaAgentRequest struct {
	EncryptedUsername []byte   `json:"encrypted_username"`
	EncryptedPassword []byte   `json:"encrypted_password"`
	UserExpr          string   `json:"user_expr"`
	DisplayName       string   `json:"display_name"`
	EventTypes        []string `json:"event_types"`
	Tables            []string `json:"tables"`
	StatusCodes       []int32  `json:"status_codes"`
	Disabled          bool     `json:"disabled"`
}

type UpdateLogRedactionPolicyViaAgentRequest struct {
	EncryptedUsername []byte `json:"encrypted_username"`
	EncryptedPassword []byte `json:"encrypted_password"`
	RedactLog         bool   `json:"redact_log"`
}

type GetLogRedactionPolicyViaAgentRequest struct {
	EncryptedUsername []byte `json:"encrypted_username"`
	EncryptedPassword []byte `json:"encrypted_password"`
}

type GetLogRedactionPolicyViaAgentResponse struct {
	// cluster-agent's grpc-gateway emits camelCase keys; proto field redact_log -> JSON redactLog.
	RedactLog bool `json:"redactLog"`
}

// CheckKmsRequest is sent through the regional dataplane proxy to POST
// /dataplane/proxy/sharedPools/{sharedPoolId}/check with targetType "kms".
// KMS field names and shape mirror the service-proxy checker
// `pkg/checker/core.KMSParams` upstream.
type CheckKmsRequest struct {
	SharedPoolId string `json:"-"`

	Provider          string            `json:"provider"`
	KeyId             string            `json:"keyId"`
	Region            string            `json:"region"`
	EncryptionContext map[string]string `json:"encryptionContext,omitempty"`
}
