package pimport

import (
	"fmt"
	"regexp"
	"sort"
	"strings"

	"github.com/pingcap/errcode"
	"github.com/pingcap/errors"
	agentpb "github.com/tidbcloud/dataflow-service/idl/pbgen/proto/agent"
	"github.com/tidbcloud/dataflow-service/idl/pbgen/proto/dataflow"
	"github.com/tidbcloud/dataflow-service/internal/db/entity"
	pimport "github.com/tidbcloud/dataflow-service/internal/model/import"
	"go.uber.org/zap"
)

var unidentifiedError error = errors.New("unidentified error")

func wrapNonErrorCodeAsInternalError(err error, msg string) error {
	if _, ok := err.(errcode.ErrorCode); ok {
		return err
	}

	if msg == "" {
		msg = "Internal error, please retry later or contact support."
	}

	return errcode.NewInternalErr(errors.New(msg))
}

func s3AccessDeniedErr(
	awsAccessKeyId, awsSecretAccessKey, awsRoleARN string,
	sourceURL string,
	operation string,
) error {
	var errMsg string
	var accessMsg, accessFormat string
	if len(awsRoleARN) > 0 {
		accessMsg = fmt.Sprintf(" using the role '%s'", awsRoleARN)
		accessFormat = "role"
	} else if len(awsAccessKeyId) > 0 && len(awsSecretAccessKey) > 0 {
		accessMsg = fmt.Sprintf(" using the access key id '%s' and the secret access key '%s'", awsAccessKeyId, awsSecretAccessKey)
		accessFormat = "ak/sk"
	}
	if len(operation) > 0 {
		errMsg = fmt.Sprintf("Access denied to the source '%s'%s: The %s does not have %s permission to the source. Please ensure the %s has all the necessary permissions configured in the 'permissions policies' settings. For more information, please refer to <a href=\"https://docs.pingcap.com/tidbcloud/troubleshoot-import-access-denied-error\" target=\"_blank\">the trouble-shooting guide</a>.",
			sourceURL, accessMsg, accessFormat, operation, accessFormat)
	} else {
		errMsg = fmt.Sprintf("Access denied to the source '%s'%s. Please refer to <a href=\"https://docs.pingcap.com/tidbcloud/troubleshoot-import-access-denied-error\" target=\"_blank\">the trouble-shooting guide</a> and configure the %s and ensure it can access the source.",
			sourceURL, accessMsg, accessFormat)
	}
	return errcode.NewInvalidInputErr(errors.New(errMsg))
}

func gcsAccessDeniedErr(sourceURL string, operation string) error {
	var errMsg string
	if len(operation) > 0 {
		errMsg = fmt.Sprintf("Access denied to the source '%s': The role doesn't have %s permission to the source. Please refer to <a href=\"https://docs.pingcap.com/tidbcloud/config-s3-and-gcs-access#configure-gcs-access\" target=\"_blank\">this document</a> and configure the role and the source access properly", sourceURL, operation)
	} else {
		errMsg = fmt.Sprintf("Access denied to the source '%s'. Please refer to <a href=\"https://docs.pingcap.com/tidbcloud/config-s3-and-gcs-access#configure-gcs-access\" target=\"_blank\">this document</a> and configure the role and the source access properly", sourceURL)
	}
	return errcode.NewCodedError(errors.New(errMsg), errcode.StateCode)
}

func azblobAccessDeniedErr(sourceURL string, operation string) error {
	var errMsg string
	if len(operation) > 0 {
		errMsg = fmt.Sprintf("Access denied to the source '%s': The SAS token doesn't have %s permission to the source. Please refer to <a href=\"https://docs.pingcap.com/tidbcloud/config-s3-and-gcs-access#configure-azblob-access\" target=\"_blank\">this document</a> and configure the SAS token and the source access properly", sourceURL, operation)
	} else {
		errMsg = fmt.Sprintf("Access denied to the source '%s'. Please refer to <a href=\"https://docs.pingcap.com/tidbcloud/config-s3-and-gcs-access#configure-azblob-access\" target=\"_blank\">this document</a> and configure the SAS token and the source access properly", sourceURL)
	}
	return errcode.NewCodedError(errors.New(errMsg), errcode.StateCode)
}

func assumeRoleErr(awsRoleARN string) error {
	if len(awsRoleARN) > 0 {
		return errcode.NewInvalidInputErr(errors.Errorf("Cannot assume the provided role '%s'.  Please check the trust relationships settings on the role: for example, please check whether the trust entity has been set to the 'TiDB Cloud account ID' and whether the 'TiDB Cloud External ID' is correctly set in the trust condition.  For further information, please refer to <a href=\"https://docs.pingcap.com/tidbcloud/troubleshoot-import-access-denied-error\" target=\"_blank\">the trouble-shooting guide</a>", awsRoleARN))
	} else {
		return errcode.NewInternalErr(errors.New("Cannot assume the role.  Please contact the admin"))
	}
}

func sourceNotFoundErr(sourceURL string) error {
	return errcode.NewNotFoundErr(errors.New(
		fmt.Sprintf("SourceUrl '%s' is not found, please check whether the source exists", sourceURL),
	))
}

// ErrorTranslator is a function that can translate an TiDB internal error to a more readable error according to different situations.
type ErrorTranslator func(originalErr error) (hasTranslated bool, newErr error)

// ApplyErrorTranslators applies a series of error translators on the original error to get a translated final error.
func ApplyErrorTranslators(originalErr error, errTrs ...ErrorTranslator) (bool, error) {
	for _, tr := range errTrs {
		hasTranslated, newErr := tr(originalErr)
		if hasTranslated {
			return true, newErr
		}
	}
	return false, originalErr
}

func translateInvalidTableSchemaFileContent() ErrorTranslator {
	return func(originalErr error) (bool, error) {
		errMsg := originalErr.Error()
		re := regexp.MustCompile(`(?ms)generate table info from SQL error: (.*): (.*)`)
		subMatch := re.FindSubmatch([]byte(errMsg))
		if subMatch != nil {
			tableName := string(subMatch[1])
			detailReason := string(subMatch[2])
			return true, errcode.NewCodedError(
				errors.Errorf("The table '%s' has some problems with the schema file: %s.  Please check the content of the schema file.", tableName, detailReason),
				errcode.StateCode,
			)
		}
		return false, originalErr
	}
}

func translateTableSchemaFileMissing() ErrorTranslator {
	return func(originalErr error) (bool, error) {
		errMsg := originalErr.Error()
		re := regexp.MustCompile(`get create table statement from schema file error.*schema file is missing for the table '(.*)'`)
		subMatch := re.FindSubmatch([]byte(errMsg))
		if subMatch != nil {
			tableName := string(subMatch[1])
			return true, errcode.NewCodedError(
				errors.Errorf("Cannot find the table schema file for the table '%s'.  Please make sure the table schema file is put in the data source dir, and it has the correct file name.", tableName),
				errcode.StateCode,
			)
		}
		return false, originalErr
	}
}

func translateTableSchemaContentInsistent() ErrorTranslator {
	return func(originalErr error) (bool, error) {
		errMsg := originalErr.Error()
		re := regexp.MustCompile(`\[Lightning:Restore:ErrSchemaNotExists\]table (.*) schema not found`)
		subMatch := re.FindSubmatch([]byte(errMsg))
		if subMatch != nil {
			tableName := string(subMatch[1])
			return true, errcode.NewCodedError(
				errors.Errorf("Cannot find the create statement for the table %s.  Please check the create table statement in the schema file, and make it consistent with the table name the file refers to.", tableName),
				errcode.StateCode,
			)
		}
		return false, originalErr
	}
}

func translateS3RegionMismatch() ErrorTranslator {
	return func(originalErr error) (bool, error) {
		errMsg := originalErr.Error()
		re := regexp.MustCompile("s3 bucket and region are not matched, bucket=(.*), input region=(.*), real region=(.*)")
		subMatch := re.FindSubmatch([]byte(errMsg))
		if subMatch != nil {
			bucketName := subMatch[1]
			inputRegion := subMatch[2]
			realRegion := subMatch[3]
			return true, errcode.NewInvalidInputErr(
				errors.Errorf(
					"The provided region of S3 bucket '%s' is not correct: provided region is '%s'; actual region is '%s'",
					bucketName, inputRegion, realRegion,
				),
			)
		}
		return false, originalErr
	}
}

func translateBucketEmpty(sourceURL string) ErrorTranslator {
	return func(originalErr error) (bool, error) {
		errMsg := originalErr.Error()
		var matched bool
		matched, _ = regexp.MatchString("there is no file in this bucket", errMsg)
		if matched {
			return true, sourceNotFoundErr(sourceURL)
		}
		return false, originalErr
	}
}

var gPassThroughError = func(originalErr error) (bool, error) {
	return false, originalErr
}

func translateDetailedS3AccessDenied(
	dataSourceType entity.DataSource,
	awsAccessKeyId, awsSecretAccessKey, awsRoleARN string,
	sourceURL string,
) ErrorTranslator {
	switch dataSourceType {
	case entity.ImportDataSourceS3:
		return func(originalErr error) (bool, error) {
			var matched bool
			errMsg := originalErr.Error()
			matched, _ = regexp.MatchString("list objects error: AccessDenied: Access Denied", errMsg)
			if matched {
				return true, s3AccessDeniedErr(awsAccessKeyId, awsSecretAccessKey, awsRoleARN, sourceURL, "ListBucket")
			}
			matched, _ = regexp.MatchString("get object error: AccessDenied: Access Denied", errMsg)
			if matched {
				return true, s3AccessDeniedErr(awsAccessKeyId, awsSecretAccessKey, awsRoleARN, sourceURL, "GetObject")
			}
			return false, originalErr
		}
	default:
		return gPassThroughError
	}
}

func translateDBAccessDenied() ErrorTranslator {
	return func(originalErr error) (bool, error) {
		errMsg := originalErr.Error()
		matched, _ := regexp.MatchString("Error 1045: Access denied for user", errMsg)
		if matched {
			return true, errcode.NewCodedError(errors.New(
				"Access denied to the TiDB cluster temporarily.  Please try again later.",
			), errcode.StateCode)
		}
		return false, originalErr
	}
}

func translateAccessDenied(
	dataSourceType entity.DataSource,
	awsAccessKeyId, awsSecretAccessKey, awsRoleARN string,
	sourceURL string,
) ErrorTranslator {
	switch dataSourceType {
	case entity.ImportDataSourceS3:
		return func(originalErr error) (bool, error) {
			var matched bool
			errMsg := originalErr.Error()
			matched, _ = regexp.MatchString("AccessDenied: User.* is not authorized to perform: sts:AssumeRole on resource", errMsg)
			if matched {
				return true, assumeRoleErr(awsRoleARN)
			}
			matched, _ = regexp.MatchString("AccessDenied: Access Denied", errMsg)
			if matched {
				return true, s3AccessDeniedErr(awsAccessKeyId, awsSecretAccessKey, awsRoleARN, sourceURL, "")
			}
			return false, originalErr
		}
	case entity.ImportDataSourceGcs:
		return func(originalErr error) (bool, error) {
			var matched bool
			errMsg := originalErr.Error()
			matched, _ = regexp.MatchString("googleapi: Error 403:.* does not have storage\\.buckets\\.get access to", errMsg)
			if matched {
				return true, gcsAccessDeniedErr(sourceURL, "storage.buckets.get")
			}
			matched, _ = regexp.MatchString("googleapi: Error 403:.* does not have storage\\.objects\\.list access to", errMsg)
			if matched {
				return true, gcsAccessDeniedErr(sourceURL, "storage.objects.list")
			}
			matched, _ = regexp.MatchString("googleapi: Error 403:.* does not have storage\\.objects\\.get access to", errMsg)
			if matched {
				return true, gcsAccessDeniedErr(sourceURL, "storage.objects.get")
			}
			return false, originalErr
		}
	case entity.ImportDataSourceAzblob:
		// TODO Dealing with Azure permission issues
		fallthrough
	default:
		return gPassThroughError
	}
}

func translateDataSyntaxError() ErrorTranslator {
	return func(originalErr error) (bool, error) {
		errMsg := originalErr.Error()
		if matched, _ := regexp.MatchString("syntax error: (cannot have consecutive fields without separator|unterminated quoted field|no character after backslash)", errMsg); matched {
			return true, errcode.NewCodedError(
				errors.New("Syntax errors: The CSV source file cannot be parsed with the current 'CSV configuration'. Please modify the 'CSV Configuration' settings to ensure correct parsing of the CSV source file."),
				errcode.InvalidInputCode,
			)
		}
		return false, originalErr
	}
}

func translateClusterTemporarilyUnavailable() ErrorTranslator {
	return func(originalErr error) (bool, error) {
		errMsg := originalErr.Error()
		if matched, _ := regexp.MatchString("temporarily unavailable", errMsg); matched {
			return true, errcode.NewCodedError(
				errors.New("The TiDB cluster is temporarily unavailable. Please try again later."),
				errcode.StateCode,
			)
		}
		return false, originalErr
	}
}

// match error: table(`xxxxxx`.`xxx`) is not empty, please truncate table and retry it.
func translateTableNotEmptyError() ErrorTranslator {
	return func(originalErr error) (bool, error) {
		errMsg := originalErr.Error()
		re := regexp.MustCompile(`table\(` +
			"`" + `([^` + "`" + `]+)` + "`" + `\.` + // Matches the database name
			"`" + `([^` + "`" + `]+)` + "`" + // Matches the table name
			`\) is not empty, please truncate table and retry it`)
		subMatch := re.FindSubmatch([]byte(errMsg))
		if len(subMatch) >= 3 {
			schemaName := string(subMatch[1])
			tableName := string(subMatch[2])
			return true, errcode.NewCodedError(
				errors.Errorf("The table '`%s`.`%s`' is not empty. Please truncate the table and retry the operation.", schemaName, tableName),
				errcode.StateCode,
			)
		}
		return false, originalErr
	}
}

func translateInconsistentCompressionError() ErrorTranslator {
	return func(originalErr error) (bool, error) {
		errMsg := originalErr.Error()
		if matched, _ := regexp.MatchString("(gzip: invalid header|s2: corrupt input|magic number mismatch)", errMsg); matched {
			return true, errcode.NewInvalidInputErr(
				errors.New("Unable to extract the compressed data file. The compression algorithm used in the file does not match the file compression suffix. Please ensure that the file was compressed using the correct algorithm and try again."),
			)
		}
		return false, originalErr
	}
}

func translateTempFixOnPrecheckErrors(dataFormat entity.DataFormat, logger *zap.Logger) ErrorTranslator {
	return func(originalErr error) (bool, error) {
		errMsg := originalErr.Error()
		switch dataFormat {
		case entity.ImportDataFormatAuroraSnapshot,
			entity.ImportDataFormatParquet:
			// temporarily ignore parquet file timeout on validation
			// because it is a bit time-consuming when there are many parquet files to check, each having a big size
			// TODO: remove this logic when performance for parquet schema check is fast enough
			if matched, _ := regexp.MatchString(`Post .*: context deadline exceeded \(Client\.Timeout exceeded while awaiting headers\)`, errMsg); matched {
				logger.Warn("The agent timeout when checking parquet files. In order to continue on importing, ignore it")
				return true, nil
			}
			if matched, _ := regexp.MatchString("Post.*: EOF", errMsg); matched {
				logger.Warn("The agent may have OOMed when prechecking parquet files. In order to continue on importing, ignore it")
				return true, nil
			}
		}
		return false, originalErr
	}
}

func translatePreviewAgentError(originalErr error) (bool, error) {
	return ApplyErrorTranslators(originalErr,
		translateTableSchemaFileMissing(),
		translateInvalidTableSchemaFileContent(),
		translateTableSchemaContentInsistent(),
		translateDataSyntaxError(),
		translateInconsistentCompressionError(),
	)
}

func translateLightningPrecheckAgentError(
	originalErr error,
	dataSourceType entity.DataSource,
	dataFormat entity.DataFormat,
	awsAccessKeyId, awsSecretAccessKey, awsRoleARN string,
	sourceURL string,
	logger *zap.Logger,
) error {
	hasTranslated, translatedErr := ApplyErrorTranslators(originalErr,
		translateAccessDenied(dataSourceType, awsAccessKeyId, awsSecretAccessKey, awsRoleARN, sourceURL),
		translateS3RegionMismatch(),
		translateDBAccessDenied(),
		translateTableSchemaFileMissing(),
		translateInvalidTableSchemaFileContent(),
		translateTableSchemaContentInsistent(),
		translateDataSyntaxError(),
		translateInconsistentCompressionError(),
		// temporarily ignore some precheck errors on some scenarios,
		// TODO: remove this translator when every problems on the temp fix has solved.
		translateTempFixOnPrecheckErrors(dataFormat, logger),
	)
	if hasTranslated {
		return translatedErr
	} else {
		return unidentifiedError
	}
}

func adjustS3AccessErr(awsAccessKeyId, awsSecretAccessKey, awsRoleARN, sourceURL, errMsg string) error {
	hasTranslated, translatedErr := ApplyErrorTranslators(errors.New(errMsg),
		translateDetailedS3AccessDenied(entity.ImportDataSourceS3, awsAccessKeyId, awsSecretAccessKey, awsRoleARN, sourceURL),
		translateAccessDenied(entity.ImportDataSourceS3, awsAccessKeyId, awsSecretAccessKey, awsRoleARN, sourceURL),
		translateBucketEmpty(sourceURL),
	)
	if hasTranslated {
		return translatedErr
	}
	return s3AccessDeniedErr(awsAccessKeyId, awsSecretAccessKey, awsRoleARN, sourceURL, "")
}

func adjustGCSAccessErr(sourceURL, errMsg string) error {
	hasTranslated, translatedErr := ApplyErrorTranslators(errors.New(errMsg),
		translateAccessDenied(entity.ImportDataSourceGcs, "", "", "", sourceURL),
		translateBucketEmpty(sourceURL),
	)
	if hasTranslated {
		return translatedErr
	}
	return gcsAccessDeniedErr(sourceURL, "")
}

func adjustAzblobAccessErr(sourceURL, errMsg string) error {
	hasTranslated, translatedErr := ApplyErrorTranslators(errors.New(errMsg),
		translateAccessDenied(entity.ImportDataSourceAzblob, "", "", "", sourceURL),
		translateBucketEmpty(sourceURL),
	)
	if hasTranslated {
		return translatedErr
	}
	return azblobAccessDeniedErr(sourceURL, "")
}

func generatePrecheckError(result *agentpb.CheckResult) error {
	switch result.CheckItem {
	case agentpb.CheckItem_CHECK_ITEM_TARGET_TABLE_EMPTY:
		return errcode.NewCodedError(errors.Errorf("Some target tables are not empty: %s.  Please try to drop these tables and try again", result.Msg), errcode.StateCode)
	case agentpb.CheckItem_CHECK_ITEM_SOURCE_SCHEMA:
		errDetailLines := strings.Split(result.Msg, "\n")
		errTableNameMap := map[string]struct{}{}
		for _, errDetail := range errDetailLines {
			re := regexp.MustCompile("TiDB schema (`.*`\\.`.*`)")
			subMatch := re.FindSubmatch([]byte(errDetail))
			if subMatch != nil {
				tableName := string(subMatch[1])
				if _, ok := errTableNameMap[tableName]; !ok {
					errTableNameMap[tableName] = struct{}{}
				}
			}
		}
		errTableNames := []string{}
		for tblName := range errTableNameMap {
			errTableNames = append(errTableNames, tblName)
		}
		sort.Strings(errTableNames)
		var errDigestMsg string
		errDigestMsg += ("affected tables: [" + strings.Join(errTableNames, ",") + "]; error details: ")
		const digestErrCnt = 3
		if len(errDetailLines) > digestErrCnt {
			errDigestMsg += strings.Join(errDetailLines[:digestErrCnt], "; ")
			errDigestMsg += fmt.Sprintf(" ... and %d more errors", len(errDetailLines)-digestErrCnt)
		} else {
			errDigestMsg += strings.Join(errDetailLines, "; ")
		}
		return errcode.NewInvalidInputErr(errors.Errorf("Souce data is inconsistent with the table schema: %s.  Please try to fix the source data and try again", errDigestMsg))
	case agentpb.CheckItem_CHECK_ITEM_CSV_HEADER:
		return errcode.NewInvalidInputErr(errors.Errorf("Souce data is inconsistent with the CSV header setting: %s.  Please try to change the CSV header setting or fix the source data", result.Msg))
	case agentpb.CheckItem_CHECK_ITEM_TARGET_CLUSTER_SIZE:
		return errcode.NewCodedError(errors.Errorf("The source data is too large to import: %s.  Please try to extend the cluster size or reduce the source data", result.Msg), errcode.StateCode)
	default:
		return errcode.NewInternalErr(errors.New(result.Msg))
	}
}

func TranslateValidateImportError(originalErr error, imp *pimport.ImportTask, logger *zap.Logger) error {
	switch concreteErr := errors.Cause(originalErr).(type) {
	case *pimport.ValidateImportPrepareError:
		return wrapNonErrorCodeAsInternalError(concreteErr.Err, "Prepare import validation has errors.")
	case *pimport.ValidateImportSingleCheckItemError:
		return translateValidateImportSingleItemError(concreteErr, imp, logger)
	default:
		return wrapNonErrorCodeAsInternalError(originalErr, "Internal error on import validation.")
	}
}

func translateValidateImportSingleItemError(e *pimport.ValidateImportSingleCheckItemError, imp *pimport.ImportTask, logger *zap.Logger) error {
	logger.Error("validate on single item failed", zap.String("check_item", e.Item.String()), zap.Error(e.Err))
	switch e.Item {
	case dataflow.ImportValidationItem_SOURCE_ACCESS_CHECK:
		return translateSourceAccessCheckValidationError(e.Err, logger)
	case dataflow.ImportValidationItem_IMPORT_PRECHECK:
		return translateLightningPrecheckError(e.Err, imp, logger)
	case dataflow.ImportValidationItem_IMPORT_DATA_ONLY_CHECK:
		return translateImportDataOnlyCheckError(e.Err)
	case dataflow.ImportValidationItem_ESTIMATE_SIZE:
		return e.Err
	default:
		return wrapNonErrorCodeAsInternalError(e.Err, fmt.Sprintf("Validate on single item %s failed.", e.Item)).(errcode.ErrorCode)
	}
}

func translateImportDataOnlyCheckError(originalErr error) error {
	hasTranslated, translatedErr := ApplyErrorTranslators(originalErr,
		translateS3RegionMismatch(),
		translateDBAccessDenied(),
		translateDataSyntaxError(),
		translateInconsistentCompressionError(),
		translateClusterTemporarilyUnavailable(),
		translateTableNotEmptyError(),
	)
	if hasTranslated {
		return translatedErr
	}
	return wrapNonErrorCodeAsInternalError(originalErr, "Internal error on import validation, please feel free to contact support.")
}

func translateSourceAccessCheckValidationError(originalErr error, logger *zap.Logger) error {
	logger.Error("source access check has errors", zap.Error(originalErr))
	switch concreteErr := errors.Cause(originalErr).(type) {
	case *pimport.SourceAccessCheckFailedError:
		switch concreteErr.DataSourceType {
		case entity.ImportDataSourceS3:
			return adjustS3AccessErr(
				concreteErr.AWSAccessKeyID,
				concreteErr.AWSSecretAccessKey,
				concreteErr.AWSRoleARN,
				concreteErr.SourceURL,
				concreteErr.ErrorMsg,
			)
		case entity.ImportDataSourceGcs:
			return adjustGCSAccessErr(concreteErr.SourceURL, concreteErr.ErrorMsg)
		case entity.ImportDataSourceAzblob:
			return adjustAzblobAccessErr(concreteErr.SourceURL, concreteErr.ErrorMsg)
		default:
			return errcode.NewCodedError(errors.New("source access check failed"), errcode.StateCode)
		}
	default:
		return wrapNonErrorCodeAsInternalError(originalErr, "Check source access encounters error.")
	}
}

func translateLightningPrecheckError(originalErr error, imp *pimport.ImportTask, logger *zap.Logger) error {
	logger.Error("Lightning precheck has errors", zap.Error(originalErr))
	switch concreteErr := errors.Cause(originalErr).(type) {
	case *pimport.LightningPreCheckFailedError:
		return generatePrecheckError(concreteErr.CheckResult)
	default:
		plainSourceURL, accessConfig, err := imp.ParseSourceURL()
		if err != nil {
			logger.Error("cannot get access key info from entity's source URL", zap.Error(err))
			accessConfig = new(entity.ImportSourceAccessConfig)
		}
		translatedErr := translateLightningPrecheckAgentError(concreteErr,
			imp.DataSourceType,
			imp.DataFormat,
			accessConfig.AWSAccessKeyID,
			accessConfig.AWSSecretAccessKey,
			imp.AwsRoleArn,
			plainSourceURL,
			logger,
		)
		if translatedErr != nil {
			if translatedErr != unidentifiedError {
				return translatedErr
			}
			return wrapNonErrorCodeAsInternalError(originalErr, "Do precheck on import error")
		}
		return nil
	}
}
