package pkg

import (
	"context"
	"database/sql"
	"errors"
	"strings"

	"github.com/go-sql-driver/mysql"
)

var InconsistentErr = errors.New("result inconsistent")

type TypeErrIgnoreHandler func(err error) error

func InconsistentHandler(err error) error {
	if err != nil {
		if errors.Is(err, InconsistentErr) {
			return err
		}
	}
	return nil
}

func CtxDoneHandler(err error) error {
	if err != nil {
		if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {
			return nil
		}
		return err
	}
	return nil
}

func TiFlashMemoryLimitHandler(err error) error {
	if err != nil {
		if strings.Contains(err.Error(), "DB::Exception: Memory limit") {
			return nil
		}
	}
	return err
}

func InvalidConnectionHandler(err error) error {
	if err != nil {
		if errors.Is(err, mysql.ErrInvalidConn) {
			return nil
		}
		return err
	}
	return nil
}

func TiFlashStaleSchemaHandler(err error) error {
	if err != nil {
		if strings.Contains(err.Error(), "newer than query schema version") {
			return nil
		}
		return err
	}
	return nil
}

func TiDBInformationSchemaOutDate(err error) error {
	if err != nil {
		if strings.Contains(err.Error(), "Information schema is out of date") {
			return nil
		}
	}
	return err
}

func TiDBUnsupportedModifyColumnHandler(err error) error {
	if err != nil {
		if strings.Contains(err.Error(), "Unsupported modify column: table is partition table") {
			return nil
		}
	}
	return err
}

func DDLWriteConflict(err error) error {
	if err != nil {
		if strings.Contains(err.Error(), "Error 9007: DDL job rollback, error msg: Write conflict") {
			return nil
		}
	}
	return err
}

func DDLDuplicateColumn(err error) error {
	if err != nil {
		if strings.Contains(err.Error(), "Duplicate column") {
			return nil
		}
	}
	return err
}

func ColumnExists(err error) error {
	if err != nil {
		if strings.Contains(err.Error(), "Can't DROP") {
			return nil
		}
	}
	return err
}

func TiDBStaleSchemaHandler(err error) error {
	if err != nil {
		if strings.Contains(err.Error(), "Information schema is changed during the execution of the statement") {
			return nil
		}
		return err
	}
	return nil
}

func DeadlockFoundHandler(err error) error {
	if err != nil {
		if strings.Contains(err.Error(), "Deadlock found when trying to get lock; try restarting transaction") {
			return nil
		}
		return err
	}
	return nil
}

func LockWaitTimeoutHandler(err error) error {
	if err != nil {
		if strings.Contains(err.Error(), "Lock wait timeout exceeded; try restarting transaction") {
			return nil
		}
		return err
	}
	return nil
}

func TiFlashPrimaryLockHandler(err error) error {
	if err != nil {
		if strings.Contains(err.Error(), "DB::Exception: primary_lock") {
			return nil
		}
	}
	return err
}

func ErrNoRowsHandler(err error) error {
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) || strings.Contains(err.Error(), sql.ErrNoRows.Error()) {
			return nil
		}
	}
	return nil
}

func TiFlashMppCancelHandler(err error) error {
	if err != nil {
		if strings.Contains(err.Error(), "DB::Exception: query canceled") {
			return nil
		}
	}
	return err
}

func TiFlashMppCancelHandler2(err error) error {
	if err != nil {
		if strings.Contains(err.Error(), "DB::Exception: Query was cancelled") {
			return nil
		}
	}
	return err
}

func SkipErrorHandler(err error) error {
	return nil
}

func ErrorHandler(handlers ...func(error) error) TypeErrIgnoreHandler {
	return func(err error) error {
		for _, h := range handlers {
			err = h(err)
		}
		return err
	}
}

var TPCommonErrHandler = ErrorHandler(
	CtxDoneHandler,
	InvalidConnectionHandler,
	TiDBStaleSchemaHandler,
	DeadlockFoundHandler,
	LockWaitTimeoutHandler,
	// go-tpc 在 QueryRow 或 QueryRowContext 的时候没有忽略 sql: no rows in result set 错误
	// 需要在 case 中处理一下
	ErrNoRowsHandler)

var APCommonErrHandler = ErrorHandler(
	CtxDoneHandler,
	InvalidConnectionHandler,
	// 多个 case 在同一个 plan 中依次执行，ddl case 中有对大表的 reorg ddl，非常耗时，
	// 在该 case 结束后，ddl job 并没有结束，下一个 case 跑的时候，
	// 可能会产生 DB::TiFlashException: Table 59 schema version 2707 newer than query schema version 错误。
	// case 需要忽略该错误
	TiFlashStaleSchemaHandler,
	// https://github.com/pingcap/tiflash/issues/4823
	TiFlashMppCancelHandler,
	TiFlashMppCancelHandler2,
	// https://github.com/pingcap/tiflash/issues/2207
	TiFlashPrimaryLockHandler,
)
