package errhandler

import "regexp"

// CommonChangefeedErr classifies TiCDC kernel error codes that represent
// user-configuration mistakes rather than system failures.
// Only errors definitively caused by bad user input are listed here.
type CommonChangefeedErr string

const (
	ErrChangefeedNotCommonErr             CommonChangefeedErr = "NotCommonErr"
	ErrChangefeedKafkaInvalidConfig       CommonChangefeedErr = "KafkaInvalidConfig"
	ErrChangefeedKafkaInvalidPartitionNum CommonChangefeedErr = "KafkaInvalidPartitionNum"
	ErrChangefeedKafkaTopicExprInvalid    CommonChangefeedErr = "KafkaTopicExprInvalid"
	ErrChangefeedTableIneligible          CommonChangefeedErr = "TableIneligible"
	ErrChangefeedAPIInvalidParam          CommonChangefeedErr = "InvalidParam"
	ErrChangefeedStartTsBeforeGC          CommonChangefeedErr = "StartTsBeforeGC"
	ErrChangefeedKafkaNewProducer         CommonChangefeedErr = "KafkaNewProducer" // wraps ErrKafkaInvalidPartitionNum / ErrAvroSchemaAPIError
)

// cdcUserErrPatterns maps pre-compiled regex patterns (matched against the raw kernel error
// message) to the corresponding CommonChangefeedErr value.
// Patterns are checked in order; first match wins.
// Pre-compiled at init time to avoid regex compilation overhead on every call.
var cdcUserErrPatterns = []struct {
	pattern *regexp.Regexp
	errType CommonChangefeedErr
}{
	// ErrKafkaInvalidPartitionNum appears nested inside ErrKafkaNewProducer messages.
	// Check for the inner error first so we can be specific.
	{regexp.MustCompile("ErrKafkaInvalidPartitionNum"), ErrChangefeedKafkaInvalidPartitionNum},
	{regexp.MustCompile("ErrKafkaInvalidConfig"), ErrChangefeedKafkaInvalidConfig},
	{regexp.MustCompile("ErrKafkaTopicExprInvalid"), ErrChangefeedKafkaTopicExprInvalid},
	{regexp.MustCompile("ErrTableIneligible"), ErrChangefeedTableIneligible},
	{regexp.MustCompile("ErrAPIInvalidParam"), ErrChangefeedAPIInvalidParam},
	{regexp.MustCompile("ErrStartTsBeforeGC"), ErrChangefeedStartTsBeforeGC},
	// ErrKafkaNewProducer is a parent error; only classified as user error when
	// none of the inner user errors above matched.
	{regexp.MustCompile("ErrKafkaNewProducer"), ErrChangefeedKafkaNewProducer},
}

func (e CommonChangefeedErr) String() string {
	return string(e)
}

// IsUserError returns true when the error is definitively caused by wrong
// user configuration rather than an internal system failure.
func (e CommonChangefeedErr) IsUserError() bool {
	return e != ErrChangefeedNotCommonErr
}

// ClassifyChangefeedError inspects the raw kernel error message and returns the
// matching CommonChangefeedErr.  Returns ErrChangefeedNotCommonErr when the
// message does not match any known user-error pattern.
func ClassifyChangefeedError(errMsg string) CommonChangefeedErr {
	for _, entry := range cdcUserErrPatterns {
		if entry.pattern.MatchString(errMsg) {
			return entry.errType
		}
	}
	return ErrChangefeedNotCommonErr
}

// ChangefeedErrType returns the err_type label value to use in Prometheus
// metrics.  Returns "SystemError" for unknown/system failures so that alert
// rules can filter on err_type!="SystemError" for user errors.
func ChangefeedErrType(errMsg string) string {
	t := ClassifyChangefeedError(errMsg)
	if t == ErrChangefeedNotCommonErr {
		return "SystemError"
	}
	return t.String()
}
