package cvt

import (
	"encoding/json"
	"fmt"

	"google.golang.org/protobuf/types/known/timestamppb"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/utils/ptr"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	apiv1 "github.com/tidbcloud/infra-provider/api/tidbcloud/cluster-server/v1"
)

func SnapshotBackup2PB(id string, backup *cloudv1.Backup) *apiv1.SnapshotBackup {
	var status apiv1.SnapshotBackup_Status
	switch backup.Status.Phase {
	case cloudv1.BackupComplete:
		status = apiv1.SnapshotBackup_COMPLETED
	case cloudv1.BackupFailed:
		status = apiv1.SnapshotBackup_FAILED
	default:
		status = apiv1.SnapshotBackup_RUNNING
	}
	return &apiv1.SnapshotBackup{
		Id:         id,
		Status:     status,
		BackupSize: uint64(backup.Status.BackupSize),
		CommitTs:   backup.Status.BackupTS,
	}
}

func Restore2PB(id string, restore *cloudv1.Restore) *apiv1.Restore {
	var status apiv1.Restore_Status
	switch restore.Status.Phase {
	case cloudv1.RestoreComplete:
		status = apiv1.Restore_COMPLETED
	case cloudv1.RestoreFailed:
		status = apiv1.Restore_FAILED
	default:
		status = apiv1.Restore_RUNNING
	}
	var progresses []*apiv1.Restore_Progress
	for _, p := range restore.Status.Progresses {
		progress := &apiv1.Restore_Progress{
			Step:               p.Step,
			Progress:           p.Progress,
			LastTransitionTime: timestamppb.New(p.LastTransitionTime.Time),
		}
		progresses = append(progresses, progress)
	}
	return &apiv1.Restore{
		Id:         id,
		Status:     status,
		Progresses: progresses,
	}
}

func LogBackup2PB(id string, backup *cloudv1.Backup) *apiv1.LogBackup {
	pb := &apiv1.LogBackup{
		Id:            id,
		LogStatus:     logBackupPhaseToPB(backup.Status.Phase),
		StartTs:       backup.Status.BackupTS,
		Bucket:        backup.Status.BackupPath,
		TruncateUntil: backup.Status.LogTruncateUntil,
		CheckpointTs:  backup.Status.LogCheckpointTs,
	}
	if len(backup.Status.LogSubCommandStatuses) > 0 {
		pb.LogSubCommandStatuses = make(map[string]*apiv1.LogBackup_SubCommandStatus, len(backup.Status.LogSubCommandStatuses))
		for command, status := range backup.Status.LogSubCommandStatuses {
			pb.LogSubCommandStatuses[string(command)] = logSubCommandStatusToPB(status)
		}
	}
	return pb
}

func logBackupPhaseToPB(phase cloudv1.BackupConditionType) apiv1.LogBackup_Status {
	switch phase {
	case cloudv1.BackupFailed:
		return apiv1.LogBackup_FAILED
	case cloudv1.BackupRunning:
		return apiv1.LogBackup_RUNNING
	case cloudv1.BackupStopped:
		return apiv1.LogBackup_STOPPED
	case cloudv1.BackupPrepare:
		return apiv1.LogBackup_PREPARE
	case cloudv1.BackupScheduled:
		return apiv1.LogBackup_SCHEDULED
	case cloudv1.BackupTruncating:
		return apiv1.LogBackup_TRUNCATING
	case cloudv1.BackupTruncated:
		return apiv1.LogBackup_TRUNCATED
	case cloudv1.BackupStopping:
		return apiv1.LogBackup_STOPPING
	default:
		return apiv1.LogBackup_STATUS_UNSPECIFIED
	}
}

func logSubCommandStatusToPB(status cloudv1.LogSubCommandStatus) *apiv1.LogBackup_SubCommandStatus {
	pb := &apiv1.LogBackup_SubCommandStatus{
		Command:            logSubCommandTypeToPB(status.Command),
		LogTruncatingUntil: status.LogTruncatingUntil,
		Phase:              logBackupPhaseToPB(status.Phase),
	}
	if !status.TimeStarted.IsZero() {
		pb.TimeStarted = timestamppb.New(status.TimeStarted.Time)
	}
	if !status.TimeCompleted.IsZero() {
		pb.TimeCompleted = timestamppb.New(status.TimeCompleted.Time)
	}
	for _, condition := range status.Conditions {
		pb.Conditions = append(pb.Conditions, &apiv1.LogBackup_SubCommandCondition{
			Command:            logSubCommandTypeToPB(condition.Command),
			Type:               logBackupPhaseToPB(condition.Type),
			Status:             conditionStatusToPB(condition.Status),
			LastTransitionTime: timestamppb.New(condition.LastTransitionTime.Time),
			Reason:             condition.Reason,
			Message:            condition.Message,
		})
	}
	return pb
}

func logSubCommandTypeToPB(command cloudv1.LogSubCommandType) apiv1.LogBackup_SubCommandType {
	switch command {
	case cloudv1.LogStartCommand:
		return apiv1.LogBackup_LOG_START
	case cloudv1.LogTruncateCommand:
		return apiv1.LogBackup_LOG_TRUNCATE
	case cloudv1.LogStopCommand:
		return apiv1.LogBackup_LOG_STOP
	case cloudv1.LogPauseCommand:
		return apiv1.LogBackup_LOG_PAUSE
	case cloudv1.LogResumeCommand:
		return apiv1.LogBackup_LOG_RESUME
	default:
		return apiv1.LogBackup_LOG_UNKNOWN
	}
}

func conditionStatusToPB(status corev1.ConditionStatus) apiv1.ClusterCondition_ConditionStatus {
	switch status {
	case corev1.ConditionTrue:
		return apiv1.ClusterCondition_True
	case corev1.ConditionFalse:
		return apiv1.ClusterCondition_False
	default:
		return apiv1.ClusterCondition_CONDITION_STATUS_UNSPECIFIED
	}
}

// convertK8sResourceRequirementsToProto converts k8s ResourceRequirements to proto ResourceRequirements
func convertK8sResourceRequirementsToProto(k8sRR *corev1.ResourceRequirements) *apiv1.ResourceRequirements {
	if k8sRR == nil {
		return nil
	}

	protoRR := &apiv1.ResourceRequirements{}

	// Convert limits
	if len(k8sRR.Limits) > 0 {
		protoRR.Limits = make(map[string]string)
		for key, value := range k8sRR.Limits {
			protoRR.Limits[string(key)] = value.String()
		}
	}

	// Convert requests
	if len(k8sRR.Requests) > 0 {
		protoRR.Requests = make(map[string]string)
		for key, value := range k8sRR.Requests {
			protoRR.Requests[string(key)] = value.String()
		}
	}

	return protoRR
}

func CompactBackup2PB(id string, compactBackup *cloudv1.CompactBackup) *apiv1.CompactBackup {
	pb := &apiv1.CompactBackup{
		Id:        id,
		ClusterId: compactBackup.Spec.ClusterName,
		Status:    convertCompactBackupPhaseToProtobuf(compactBackup.Status.Phase),
		CreatedAt: timestamppb.New(compactBackup.CreationTimestamp.Time),
	}

	// Set optional fields from spec
	if compactBackup.Spec.StartTs != nil {
		pb.StartTs = compactBackup.Spec.StartTs
	}
	if compactBackup.Spec.EndTs != nil {
		pb.EndTs = compactBackup.Spec.EndTs
	}
	if compactBackup.Spec.Concurrency != nil {
		pb.Concurrency = compactBackup.Spec.Concurrency
	}
	if compactBackup.Spec.InstanceType != nil {
		pb.InstanceType = compactBackup.Spec.InstanceType
	}

	// 设置配置信息
	if compactBackup.Spec.Configuration != nil {
		config := &apiv1.CompactBackupConfiguration{}
		if compactBackup.Spec.Configuration.BaseImage != "" {
			config.BaseImage = &compactBackup.Spec.Configuration.BaseImage
		}
		if compactBackup.Spec.Configuration.Tag != "" {
			config.Tag = &compactBackup.Spec.Configuration.Tag
		}
		if compactBackup.Spec.Configuration.Config != "" {
			config.Config = &compactBackup.Spec.Configuration.Config
		}
		if compactBackup.Spec.Configuration.TiKVImage != "" {
			config.TikvImage = &compactBackup.Spec.Configuration.TiKVImage // 新增
		}
		if compactBackup.Spec.Configuration.TiKVVersion != "" {
			config.TikvVersion = &compactBackup.Spec.Configuration.TiKVVersion // 新增
		}
		pb.Configuration = config
	}

	// 设置状态相关字段
	if compactBackup.Status.Message != "" {
		pb.ErrorMessage = &compactBackup.Status.Message
	}
	if compactBackup.Status.Progress != "" {
		pb.Progress = &compactBackup.Status.Progress
	}
	if compactBackup.Status.BackupPath != "" {
		pb.BackupPath = &compactBackup.Status.BackupPath
	}
	if compactBackup.Status.CompletionTime != nil {
		pb.FinishedAt = timestamppb.New(compactBackup.Status.CompletionTime.Time)
	}
	if compactBackup.Spec.CleanPolicy != nil {
		pb.CleanPolicy = (*string)(compactBackup.Spec.CleanPolicy)
	}

	// Set ResourceRequirements if provided
	if compactBackup.Spec.ResourceRequirements != nil {
		pb.ResourceRequirements = convertK8sResourceRequirementsToProto(compactBackup.Spec.ResourceRequirements)
	}

	return pb
}

// convertCompactBackupPhaseToProtobuf converts CRD phase to protobuf status
func convertCompactBackupPhaseToProtobuf(phase cloudv1.CompactPhase) apiv1.CompactBackupStatus {
	switch phase {
	case cloudv1.CompactPhaseComplete:
		return apiv1.CompactBackupStatus_complete
	case cloudv1.CompactPhaseRunning:
		return apiv1.CompactBackupStatus_running
	case cloudv1.CompactPhaseFailed:
		return apiv1.CompactBackupStatus_failed
	case cloudv1.CompactPhaseRetry:
		return apiv1.CompactBackupStatus_retry
	default:
		return apiv1.CompactBackupStatus_pending
	}
}

// CRRFStatus2PB converts CRRF annotation data from a Backup CR into a CRRFInfo proto response.
func CRRFStatus2PB(clusterID, logBackupName string, backup *cloudv1.Backup) (*apiv1.CRRFInfo, error) {
	info := &apiv1.CRRFInfo{
		ClusterId:     clusterID,
		LogBackupName: logBackupName,
		Phase:         apiv1.CRRFInfo_CRRF_PHASE_UNSPECIFIED,
	}

	// Try to parse crrf-status annotation first (it has the most up-to-date info)
	if statusJSON := backup.Annotations[cloudv1.BackupAnnotationKeyCRRFStatus]; statusJSON != "" {
		var crrfStatus cloudv1.CRRFStatus
		if err := json.Unmarshal([]byte(statusJSON), &crrfStatus); err != nil {
			return nil, fmt.Errorf("unmarshal crrf status: %w", err)
		}

		info.CrrfId = crrfStatus.CRRFID
		info.Phase = convertCRRFPhase(crrfStatus.Phase)
		info.Message = crrfStatus.Message
		info.HelmReleaseName = crrfStatus.HelmReleaseName
		info.UpstreamStorage = crrfStatus.UpstreamStorage
		info.DownstreamStorage = crrfStatus.DownstreamStorage
		info.Image = crrfStatus.Image
		return info, nil
	}

	// If no status, but spec exists, it's pending
	if specJson := backup.Annotations[cloudv1.BackupAnnotationKeyCRRFSpec]; specJson != "" {
		var crrfSpec cloudv1.CRRFSpec
		if err := json.Unmarshal([]byte(specJson), &crrfSpec); err != nil {
			return nil, fmt.Errorf("unmarshal crrf spec: %w", err)
		}
		info.CrrfId = crrfSpec.CRRFID
		info.Phase = apiv1.CRRFInfo_PENDING
		info.DownstreamStorage = crrfSpec.DownstreamStorage
		info.Image = crrfSpec.Image
		info.UpstreamStorage = ptr.Deref(backup.Spec.Storage, "")
	}

	return info, nil
}

func convertCRRFPhase(phase cloudv1.CRRFPhase) apiv1.CRRFInfo_CRRFPhase {
	switch phase {
	case cloudv1.CRRFPhasePending:
		return apiv1.CRRFInfo_PENDING
	case cloudv1.CRRFPhaseRunning:
		return apiv1.CRRFInfo_RUNNING
	case cloudv1.CRRFPhaseDestroying:
		return apiv1.CRRFInfo_DESTROYING
	case cloudv1.CRRFPhaseFailed:
		return apiv1.CRRFInfo_FAILED
	default:
		return apiv1.CRRFInfo_CRRF_PHASE_UNSPECIFIED
	}
}
