package database

import (
	"context"
	"strconv"
	"strings"

	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
)

// https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html
// https://docs.pingcap.com/zh/tidb/stable/error-codes
var mysqlErrors = []string{

	"1007", //Message: Can't create database '%s'; database exists
	"1008", //Message: Can't drop database '%s'; database doesn't exist
	"1025", //Message: Error on rename of '%s.%s' to '%s.%s' (errno: 168 - Database `%s` doesn't exist)
	"1044", //Message: Access denied for user '%s'@'%s' to database '%s'

	// 1046 #Message: No database selected
	// 1047 #Message: Unknown command
	// 1048 #Message: Column '%s' cannot be null
	// 1049 #Message: Unknown database '%s'
	// 1050 #Message: Table '%s' already exists
	// 1051 #Message: Unknown table '%s'
	// 1052 #Message: Column '%s' in %s is ambiguouso
	"1046-1052",

	"1054",  //Message: Unknown column '%s' in '%s'
	"1055",  //Message: '%s' isn't in GROUP BY
	"1056",  //Message: Can't group on '%s'
	"1059",  //Message: Identifier name '%s' is too long
	"1060",  //Message: Duplicate column name '%s'
	"1061",  //Message: index already exist '%s'
	"1062",  //Message: Duplicate entry '%s' for key %d
	"1064",  //Message: %s near '%s' at line %d
	"1067",  //Message: Invalid default value for '%s'
	"1068",  //Message: Multiple primary key defined
	"1071",  //Message: Specified key was too long (5120 bytes); max key length is 3072 bytes
	"1072",  //Message: Key column '%s' doesn't exist in table
	"1074",  //Message: Column length too big for column 'hash' (max = 255); use BLOB or TEXT instead
	"1090",  //Message: can't drop only column %s in table %s
	"1091",  //Message: Can't DROP '%s'; check that column/key exists
	"1105",  //Message: Invalid JSON path expression. The error is around character position 1.
	"1110",  //Message: Column '%s' specified twice
	"1111",  //Message: Invalid use of group function
	"1113",  //Message: A table must have at least 1 column
	"1136",  //Message: Column count doesn't match value count at row %ld
	"1141",  //Message: There is no such grant defined for user %s on host %s
	"1142",  //Message: %s command denied to user '%s'@'%s' for table '%s'
	"1146",  //Message: Table '%s.%s' doesn't exist
	"1149",  //Message: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use
	"1170",  //Message: BLOB/TEXT column 'logo' used in key specification without a key length
	"1193",  //Message: Unknown system variable '%s'
	"1210",  //Message: Incorrect arguments to %s
	"1221",  //Message: Incorrect usage of %s
	"1222",  //Message: The used SELECT statements have a different number of columns
	"1231",  //Message: Variable '%s' can't be set to the value of '%s'
	"1235",  //Message: This version of MySQL doesn't yet support '%s'
	"1239",  //Message: Incorrect foreign key definition for '%s': Key reference and table reference don't match
	"1241",  //Message: Operand should contain %d column(s)
	"1242",  //Message: Subquery returns more than 1 row
	"1248",  //Message: Every derived table must have its own alias
	"1250",  //Message: Table '%s' from one of the SELECTs cannot be used in group statement
	"1253",  //Message: COLLATION '%s' is not valid for CHARACTER SET '%s'
	"1264",  //Message: Out of range value for column '%s' at row %d
	"1265",  //Message: Data truncated for column '%s', value is '%s'
	"1273",  //Message: Unknown collation: '%s'
	"1292",  //Message: Truncated incorrect INTEGER value: '%s'
	"1305",  //Message: %s %s does not exist
	"1356",  //Message:View '%s.%s' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
	"1364",  //Message: Message: Field '%s' doesn't have a default value.
	"1366",  //Message: Incorrect %s value: '%s' for column '%s' at row %ld
	"1396",  //Message: Operation %s failed for %s
	"1406",  //Message: Data too long for column '%s' at row %d
	"1410",  //Message: You are not allowed to create a user with GRANT
	"1451",  //Message: Cannot delete or update a parent row: a foreign key constraint fails (%s)
	"1452",  //Message: Cannot add or update a child row: a foreign key constraint fails (%s)
	"1468",  //Message: User name must start with `XXXXXXXXXX.` (use `XXXXXXXXXXX` instead), see https://docs.pingcap.com/tidbcloud/select-cluster-tier#user-name-prefix for more details.
	"1470",  //Message: String 'xxx' is too long for user name (should be no longer than 32)
	"1481",  //Message: MAXVALUE can only be used in last partition definition
	"1503",  //Message: A %s must include all columns in the table's partitioning function (prefixed columns are not considered).
	"1517",  //Message: Duplicate partition name %s
	"1553",  //Message: Cannot drop index '%s': needed in a foreign key constraint
	"1582",  //Message: Incorrect parameter count in the call to native function '%s'
	"1701",  //Message: Cannot truncate a table referenced in a foreign key constraint
	"1822",  //Message: Failed to add the foreign key constraint. Missing index for constraint '%s' in the referenced table '%s'
	"1824",  //Message: Failed to open the referenced table '%s'
	"1826",  //Message: Duplicate foreign key constraint name '%s'
	"1828",  //Message: Cannot drop column '%s': needed in a foreign key constraint '%s'
	"3064",  //Message: Incorrect type for argument %s in function %s.
	"3065",  //Message: Expression #%u of ORDER BY clause is not in SELECT list, references column '%s' which is not in SELECT list; this is incompatible with %s
	"3066",  //Message: Expression #%u of ORDER BY clause is not in SELECT list, contains aggregate function; this is incompatible with %s
	"3069",  //Message: Invalid JSON data provided to function %!s(int=2): %s
	"3140",  //Message: Invalid JSON text: "%s" at position %u in value for column '%s'.
	"3143",  //Message: Invalid JSON path expression. The error is around character position %u.%s
	"3730",  //Message: Cannot drop table '%s' referenced by a foreign key constraint '%s' on table '%s'.
	"3734",  //Message: Failed to add the foreign key constraint. Missing column '%s' for constraint '%s' in the referenced table '%s'
	"3770",  //Message: Default value expression of column '%s' contains a disallowed function: %s.
	"3780",  //Message: Referencing column '%s' and referenced column '%s' in foreign key constraint '%s' are incompatible.
	"3854",  //Message: Cannot convert string '%s' from %s to %s
	"3929",  //Message: Dynamic privilege '%s' is not registered with the server.
	"8111",  //Message: Prepared statement not found
	"8112",  //Message: The number of parameters in the EXECUTE statement is not consistent with the Prepare statement.
	"8121",  //Message: privilege check for '%s' fail
	"8122",  //Message: No corresponding table name is found, given the specified wild cards.
	"8123",  //Message: In aggregated query without GROUP BY, expression #1 of SELECT list contains nonaggregated column '%s'; this is incompatible with sql_mode=only_full_group_by
	"8132",  //Message: Feature 'SELECT INTO' is not supported when security enhanced mode is enabled
	"8200",  //message: tidb The DDL syntax is not yet supported.
	"8229",  //message: TTL manager has timed out, pessimistic locks may expire, please commit or rollback this transaction
	"8216",  //Message: The usage of automatic random columns is incorrect.
	"9007",  //Message: ERROR 9007 (HY000) : Write conflict
	"20001", //Message: SHOW TABLE REGIONS' is not supported on TiDB Serverless
}

var mysqlErrorMap = make(map[uint16]bool, len(mysqlErrors))
var mysqlErrorRange [][2]uint16

func init() {
	for _, errorCode := range mysqlErrors {
		if pos := strings.Index(errorCode, "-"); pos == -1 {
			code, err := strconv.ParseUint(errorCode, 10, 16)
			if err != nil {
				tilog.Fatal(context.TODO(), "failed to parse mysql error code", zap.String("code", errorCode), zap.Error(err))
			}
			mysqlErrorMap[uint16(code)] = true
		} else {
			minCode, err1 := strconv.ParseUint(errorCode[:pos], 10, 16)
			maxCode, err2 := strconv.ParseUint(errorCode[pos+1:], 10, 16)
			if err1 != nil {
				tilog.Fatal(context.TODO(), "failed to parse mysql error code", zap.String("code", errorCode), zap.Error(err1))
			}
			if err2 != nil {
				tilog.Fatal(context.TODO(), "failed to parse mysql error code", zap.String("code", errorCode), zap.Error(err2))
			}
			mysqlErrorRange = append(mysqlErrorRange, [2]uint16{uint16(minCode), uint16(maxCode)})
		}
	}
}

func IsCausedByUser(code uint16, message string) bool {
	if mysqlErrorMap[code] {
		return true
	}
	for i := range mysqlErrorRange {
		if code >= mysqlErrorRange[i][0] && code <= mysqlErrorRange[i][1] {
			return true
		}
	}
	return false
}
