package mysql

import (
	
	
	
	

	
	
	
	
)

const indexSql = `
SELECT
	TABLE_NAME,
	COLUMN_NAME,
	INDEX_NAME,
	NON_UNIQUE 
FROM
	information_schema.STATISTICS 
WHERE
	TABLE_SCHEMA = ? 
	AND TABLE_NAME = ? 
ORDER BY
	INDEX_NAME,
	SEQ_IN_INDEX`

var typeAliasMap = map[string][]string{
	"bool":    {"tinyint"},
	"tinyint": {"bool"},
}

type Migrator struct {
	migrator.Migrator
	Dialector
}

func ( Migrator) ( *schema.Field) clause.Expr {
	 := .Migrator.FullDataTypeOf()

	if ,  := .TagSettings["COMMENT"];  {
		.SQL += " COMMENT " + .Dialector.Explain("?", )
	}

	return 
}

func ( Migrator) ( interface{},  string) error {
	return .RunWithValue(, func( *gorm.Statement) error {
		// avoid using the same name field
		 := .Schema.LookUpField()
		if  == nil {
			return fmt.Errorf("failed to look up field with name: %s", )
		}

		if !.IgnoreMigration {
			 := .FullDataTypeOf()
			 := clause.Column{Name: .DBName}
			 := []interface{}{.CurrentTable(), , }
			var  strings.Builder
			.WriteString("ALTER TABLE ? ADD ? ?")
			if .PrimaryKey || strings.Contains(strings.ToLower(.SQL), "auto_increment") {
				.WriteString(", ADD PRIMARY KEY (?)")
				 = append(, )
			}
			return .DB.Exec(.String(), ...).Error
		}

		return nil
	})
}

func ( Migrator) ( interface{},  string) error {
	return .RunWithValue(, func( *gorm.Statement) error {
		if .Schema != nil {
			if  := .Schema.LookUpField();  != nil {
				 := .FullDataTypeOf()
				if .Dialector.DontSupportRenameColumnUnique {
					.SQL = strings.Replace(.SQL, " UNIQUE ", " ", 1)
				}

				return .DB.Exec(
					"ALTER TABLE ? MODIFY COLUMN ? ?",
					clause.Table{Name: .Table}, clause.Column{Name: .DBName}, ,
				).Error
			}
		}
		return fmt.Errorf("failed to look up field with name: %s", )
	})
}

func ( Migrator) () ( bool, , ,  int,  error) {
	// TiDB version string looks like:
	// "5.7.25-TiDB-v6.5.0" or "5.7.25-TiDB-v6.4.0-serverless"
	 := strings.Split(.Dialector.ServerVersion, "-")
	if len() < 3 || [1] != "TiDB" {
		// It isn't TiDB
		return
	}

	 := strings.TrimPrefix([2], "v")
	 := strings.Split(, ".")
	if ,  = strconv.Atoi([0]);  != nil {
		 = fmt.Errorf("failed to parse the version of TiDB, the major version is: %s", [0])
		return
	}

	if ,  = strconv.Atoi([1]);  != nil {
		 = fmt.Errorf("failed to parse the version of TiDB, the minor version is: %s", [0])
		return
	}

	if ,  = strconv.Atoi([2]);  != nil {
		 = fmt.Errorf("failed to parse the version of TiDB, the patch version is: %s", [0])
		return
	}

	 = true
	return
}

func ( Migrator) ( interface{}, ,  string) error {
	return .RunWithValue(, func( *gorm.Statement) error {
		if !.Dialector.DontSupportRenameColumn {
			return .Migrator.RenameColumn(, , )
		}

		var  *schema.Field
		if .Schema != nil {
			if  := .Schema.LookUpField();  != nil {
				 = .DBName
				 = 
			}

			if  := .Schema.LookUpField();  != nil {
				 = .DBName
				 = 
			}
		}

		if  != nil {
			return .DB.Exec(
				"ALTER TABLE ? CHANGE ? ? ?",
				clause.Table{Name: .Table}, clause.Column{Name: },
				clause.Column{Name: }, .FullDataTypeOf(),
			).Error
		}

		return fmt.Errorf("failed to look up field with name: %s", )
	})
}

func ( Migrator) ( interface{}, ,  string) error {
	if !.Dialector.DontSupportRenameIndex {
		return .RunWithValue(, func( *gorm.Statement) error {
			return .DB.Exec(
				"ALTER TABLE ? RENAME INDEX ? TO ?",
				clause.Table{Name: .Table}, clause.Column{Name: }, clause.Column{Name: },
			).Error
		})
	}

	return .RunWithValue(, func( *gorm.Statement) error {
		 := .DropIndex(, )
		if  != nil {
			return 
		}

		if .Schema != nil {
			if  := .Schema.LookIndex();  == nil {
				if  = .Schema.LookIndex();  != nil {
					 := .BuildIndexOptions(.Fields, )
					 := []interface{}{clause.Column{Name: }, clause.Table{Name: .Table}, }

					 := "CREATE "
					if .Class != "" {
						 += .Class + " "
					}
					 += "INDEX ? ON ??"

					if .Type != "" {
						 += " USING " + .Type
					}

					return .DB.Exec(, ...).Error
				}
			}
		}

		return .CreateIndex(, )
	})

}

func ( Migrator) ( ...interface{}) error {
	 = .ReorderModels(, false)
	return .DB.Connection(func( *gorm.DB) error {
		.Exec("SET FOREIGN_KEY_CHECKS = 0;")
		for  := len() - 1;  >= 0; -- {
			if  := .RunWithValue([], func( *gorm.Statement) error {
				return .Exec("DROP TABLE IF EXISTS ? CASCADE", clause.Table{Name: .Table}).Error
			});  != nil {
				return 
			}
		}
		return .Exec("SET FOREIGN_KEY_CHECKS = 1;").Error
	})
}

func ( Migrator) ( interface{},  string) error {
	return .RunWithValue(, func( *gorm.Statement) error {
		, ,  := .GuessConstraintAndTable(, )
		if  != nil {
			return .DB.Exec("ALTER TABLE ? DROP CHECK ?", clause.Table{Name: .Table}, clause.Column{Name: .Name}).Error
		}
		if  != nil {
			 = .Name
		}

		return .DB.Exec(
			"ALTER TABLE ? DROP FOREIGN KEY ?", clause.Table{Name: }, clause.Column{Name: },
		).Error
	})
}

// ColumnTypes column types return columnTypes,error
func ( Migrator) ( interface{}) ([]gorm.ColumnType, error) {
	 := make([]gorm.ColumnType, 0)
	 := .RunWithValue(, func( *gorm.Statement) error {
		var (
			,  = .CurrentSchema(, .Table)
			          = "SELECT column_name, column_default, is_nullable = 'YES', data_type, character_maximum_length, column_type, column_key, extra, column_comment, numeric_precision, numeric_scale "
			,               = .DB.Session(&gorm.Session{}).Table().Limit(1).Rows()
		)

		if  != nil {
			return 
		}

		,  := .ColumnTypes()

		if  != nil {
			return 
		}

		if  := .Close();  != nil {
			return 
		}

		if !.DisableDatetimePrecision {
			 += ", datetime_precision "
		}
		 += "FROM information_schema.columns WHERE table_schema = ? AND table_name = ? ORDER BY ORDINAL_POSITION"

		,  := .DB.Table().Raw(, , ).Rows()
		if  != nil {
			return 
		}

		defer .Close()

		for .Next() {
			var (
				            migrator.ColumnType
				 sql.NullInt64
				        sql.NullString
				         sql.NullString
				            = []interface{}{
					&.NameValue, &.DefaultValueValue, &.NullableValue, &.DataTypeValue, &.LengthValue, &.ColumnTypeValue, &, &, &.CommentValue, &.DecimalSizeValue, &.ScaleValue,
				}
			)

			if !.DisableDatetimePrecision {
				 = append(, &)
			}

			if  := .Scan(...);  != nil {
				return 
			}

			.PrimaryKeyValue = sql.NullBool{Bool: false, Valid: true}
			.UniqueValue = sql.NullBool{Bool: false, Valid: true}
			switch .String {
			case "PRI":
				.PrimaryKeyValue = sql.NullBool{Bool: true, Valid: true}
			case "UNI":
				.UniqueValue = sql.NullBool{Bool: true, Valid: true}
			}

			if strings.Contains(.String, "auto_increment") {
				.AutoIncrementValue = sql.NullBool{Bool: true, Valid: true}
			}

			.DefaultValueValue.String = strings.Trim(.DefaultValueValue.String, "'")
			if .Dialector.DontSupportNullAsDefaultValue {
				// rewrite mariadb default value like other version
				if .DefaultValueValue.Valid && .DefaultValueValue.String == "NULL" {
					.DefaultValueValue.Valid = false
					.DefaultValueValue.String = ""
				}
			}

			if .Valid {
				.DecimalSizeValue = 
			}

			for ,  := range  {
				if .Name() == .NameValue.String {
					.SQLColumnType = 
					break
				}
			}

			 = append(, )
		}

		return nil
	})

	return , 
}

func ( Migrator) () ( string) {
	 := .Migrator.CurrentDatabase()
	.DB.Raw(
		"SELECT SCHEMA_NAME from Information_schema.SCHEMATA where SCHEMA_NAME LIKE ? ORDER BY SCHEMA_NAME=? DESC,SCHEMA_NAME limit 1",
		+"%", ).Scan(&)
	return
}

func ( Migrator) () ( []string,  error) {
	 = .DB.Raw("SELECT TABLE_NAME FROM information_schema.tables where TABLE_SCHEMA=?", .CurrentDatabase()).
		Scan(&).Error
	return
}

func ( Migrator) ( interface{}) ([]gorm.Index, error) {
	 := make([]gorm.Index, 0)
	 := .RunWithValue(, func( *gorm.Statement) error {

		 := make([]*Index, 0)
		,  := .CurrentSchema(, .Table)
		 := .DB.Table().Raw(indexSql, , ).Scan(&).Error
		if  != nil {
			return 
		}
		,  := groupByIndexName()

		for ,  := range  {
			 := []
			if len() == 0 {
				continue
			}
			 := &migrator.Index{
				TableName: [0].TableName,
				NameValue: [0].IndexName,
				PrimaryKeyValue: sql.NullBool{
					Bool:  [0].IndexName == "PRIMARY",
					Valid: true,
				},
				UniqueValue: sql.NullBool{
					Bool:  [0].NonUnique == 0,
					Valid: true,
				},
			}
			for ,  := range  {
				.ColumnList = append(.ColumnList, .ColumnName)
			}
			 = append(, )
		}
		return nil
	})
	return , 
}

// Index table index info
type Index struct {
	TableName  string `gorm:"column:TABLE_NAME"`
	ColumnName string `gorm:"column:COLUMN_NAME"`
	IndexName  string `gorm:"column:INDEX_NAME"`
	NonUnique  int32  `gorm:"column:NON_UNIQUE"`
}

func groupByIndexName( []*Index) (map[string][]*Index, []string) {
	 := make(map[string][]*Index, len())
	 := make([]string, 0, len())
	for ,  := range  {
		if ,  := [.IndexName]; ! {
			 = append(, .IndexName)
		}
		[.IndexName] = append([.IndexName], )
	}
	return , 
}

func ( Migrator) ( *gorm.Statement,  string) (string, string) {
	if  := strings.Split(, `.`); len() == 2 {
		return [0], [1]
	}
	.DB = .DB.Table()
	return .CurrentDatabase(), 
}

func ( Migrator) ( string) []string {
	return typeAliasMap[]
}

// TableType table type return tableType,error
func ( Migrator) ( interface{}) ( gorm.TableType,  error) {
	var  migrator.TableType

	 = .RunWithValue(, func( *gorm.Statement) error {
		var (
			 = []interface{}{
				&.SchemaValue, &.NameValue, &.TypeValue, &.CommentValue,
			}
			,  = .CurrentSchema(, .Table)
			               = "SELECT table_schema, table_name, table_type, table_comment FROM information_schema.tables WHERE table_schema = ? AND table_name = ?"
		)

		 := .DB.Table().Raw(, , ).Row()

		if  := .Scan(...);  != nil {
			return 
		}

		return nil
	})

	return , 
}