package backuperr

import (
	"regexp"
	"strings"
	"unicode/utf8"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	corev1 "k8s.io/api/core/v1"
)

const maxStoredBackupErrorBytes = 4000

const backupAnnotationKeyLastFailedError = "backup.tidbcloud.com/last-failed-error"

var quotedBRErrorPattern = regexp.MustCompile(`error="([^"]+)"`)

func ExtractFailedErrorFromBackupCR(backup *cloudv1.Backup) string {
	if backup == nil {
		return ""
	}
	if backup.Annotations != nil {
		if errMsg := strings.TrimSpace(backup.Annotations[backupAnnotationKeyLastFailedError]); errMsg != "" {
			return truncateToMaxBytes(errMsg, maxStoredBackupErrorBytes)
		}
	}
	return ExtractFailedErrorFromBackupConditions(backup.Status.LogSubCommandStatuses)
}

func ExtractFailedErrorFromBackupConditions(statuses map[cloudv1.LogSubCommandType]cloudv1.LogSubCommandStatus) string {
	for _, status := range statuses {
		for i := len(status.Conditions) - 1; i >= 0; i-- {
			condition := status.Conditions[i]
			if condition.Type != cloudv1.BackupFailed {
				continue
			}
			if condition.Status != corev1.ConditionTrue && condition.Status != "" {
				continue
			}
			return BuildStoredBackupError(condition.Reason, SanitizeBackupErrorMessage(condition.Message))
		}
	}
	return ""
}

func BuildStoredBackupError(reason, message string) string {
	reason = strings.TrimSpace(reason)
	message = strings.TrimSpace(message)
	parts := make([]string, 0, 2)
	if reason != "" {
		parts = append(parts, "reason="+reason)
	}
	if message != "" {
		parts = append(parts, "message="+message)
	}
	if len(parts) == 0 {
		return ""
	}
	return truncateToMaxBytes(strings.Join(parts, "; "), maxStoredBackupErrorBytes)
}

func SanitizeBackupErrorMessage(raw string) string {
	raw = strings.TrimSpace(raw)
	if raw == "" {
		return ""
	}
	if idx := strings.LastIndex(raw, "Error:"); idx >= 0 {
		candidate := strings.TrimSpace(raw[idx+len("Error:"):])
		if candidate != "" {
			return candidate
		}
	}
	if match := quotedBRErrorPattern.FindStringSubmatch(raw); len(match) == 2 {
		return strings.TrimSpace(match[1])
	}
	return raw
}

func truncateToMaxBytes(s string, maxBytes int) string {
	if len(s) <= maxBytes {
		return s
	}
	for len(s) > maxBytes {
		_, size := utf8.DecodeLastRuneInString(s)
		if size <= 0 {
			break
		}
		s = s[:len(s)-size]
	}
	return strings.TrimSpace(s)
}
