package gorm

import (
	
	
	

	
	
)

// Model specify the model you would like to run db operations
//
//	// update all users's name to `hello`
//	db.Model(&User{}).Update("name", "hello")
//	// if user's primary key is non-blank, will use it as condition, then will only update that user's name to `hello`
//	db.Model(&user).Update("name", "hello")
func ( *DB) ( interface{}) ( *DB) {
	 = .getInstance()
	.Statement.Model = 
	return
}

// Clauses Add clauses
//
// This supports both standard clauses (clause.OrderBy, clause.Limit, clause.Where) and more
// advanced techniques like specifying lock strength and optimizer hints. See the
// [docs] for more depth.
//
//	// add a simple limit clause
//	db.Clauses(clause.Limit{Limit: 1}).Find(&User{})
//	// tell the optimizer to use the `idx_user_name` index
//	db.Clauses(hints.UseIndex("idx_user_name")).Find(&User{})
//	// specify the lock strength to UPDATE
//	db.Clauses(clause.Locking{Strength: "UPDATE"}).Find(&users)
//
// [docs]: https://gorm.io/docs/sql_builder.html#Clauses
func ( *DB) ( ...clause.Expression) ( *DB) {
	 = .getInstance()
	var  []interface{}

	for ,  := range  {
		if ,  := .(clause.Interface);  {
			.Statement.AddClause()
		} else if ,  := .(StatementModifier);  {
			.ModifyStatement(.Statement)
		} else {
			 = append(, )
		}
	}

	if len() > 0 {
		.Statement.AddClause(clause.Where{Exprs: .Statement.BuildCondition([0], [1:]...)})
	}
	return
}

var tableRegexp = regexp.MustCompile(`(?i)(?:.+? AS (\w+)\s*(?:$|,)|^\w+\s+(\w+)$)`)

// Table specify the table you would like to run db operations
//
//	// Get a user
//	db.Table("users").Take(&result)
func ( *DB) ( string,  ...interface{}) ( *DB) {
	 = .getInstance()
	if strings.Contains(, " ") || strings.Contains(, "`") || len() > 0 {
		.Statement.TableExpr = &clause.Expr{SQL: , Vars: }
		if  := tableRegexp.FindStringSubmatch(); len() == 3 {
			if [1] != "" {
				.Statement.Table = [1]
			} else {
				.Statement.Table = [2]
			}
		}
	} else if  := strings.Split(, "."); len() == 2 {
		.Statement.TableExpr = &clause.Expr{SQL: .Statement.Quote()}
		.Statement.Table = [1]
	} else if  != "" {
		.Statement.TableExpr = &clause.Expr{SQL: .Statement.Quote()}
		.Statement.Table = 
	} else {
		.Statement.TableExpr = nil
		.Statement.Table = ""
	}
	return
}

// Distinct specify distinct fields that you want querying
//
//	// Select distinct names of users
//	db.Distinct("name").Find(&results)
//	// Select distinct name/age pairs from users
//	db.Distinct("name", "age").Find(&results)
func ( *DB) ( ...interface{}) ( *DB) {
	 = .getInstance()
	.Statement.Distinct = true
	if len() > 0 {
		 = .Select([0], [1:]...)
	}
	return
}

// Select specify fields that you want when querying, creating, updating
//
// Use Select when you only want a subset of the fields. By default, GORM will select all fields.
// Select accepts both string arguments and arrays.
//
//	// Select name and age of user using multiple arguments
//	db.Select("name", "age").Find(&users)
//	// Select name and age of user using an array
//	db.Select([]string{"name", "age"}).Find(&users)
func ( *DB) ( interface{},  ...interface{}) ( *DB) {
	 = .getInstance()

	switch v := .(type) {
	case []string:
		.Statement.Selects = 

		for ,  := range  {
			switch arg := .(type) {
			case string:
				.Statement.Selects = append(.Statement.Selects, )
			case []string:
				.Statement.Selects = append(.Statement.Selects, ...)
			default:
				.AddError(fmt.Errorf("unsupported select args %v %v", , ))
				return
			}
		}

		if ,  := .Statement.Clauses["SELECT"];  {
			.Expression = nil
			.Statement.Clauses["SELECT"] = 
		}
	case string:
		if strings.Count(, "?") >= len() && len() > 0 {
			.Statement.AddClause(clause.Select{
				Distinct:   .Statement.Distinct,
				Expression: clause.Expr{SQL: , Vars: },
			})
		} else if strings.Count(, "@") > 0 && len() > 0 {
			.Statement.AddClause(clause.Select{
				Distinct:   .Statement.Distinct,
				Expression: clause.NamedExpr{SQL: , Vars: },
			})
		} else {
			.Statement.Selects = []string{}

			for ,  := range  {
				switch arg := .(type) {
				case string:
					.Statement.Selects = append(.Statement.Selects, )
				case []string:
					.Statement.Selects = append(.Statement.Selects, ...)
				default:
					.Statement.AddClause(clause.Select{
						Distinct:   .Statement.Distinct,
						Expression: clause.Expr{SQL: , Vars: },
					})
					return
				}
			}

			if ,  := .Statement.Clauses["SELECT"];  {
				.Expression = nil
				.Statement.Clauses["SELECT"] = 
			}
		}
	default:
		.AddError(fmt.Errorf("unsupported select args %v %v", , ))
	}

	return
}

// Omit specify fields that you want to ignore when creating, updating and querying
func ( *DB) ( ...string) ( *DB) {
	 = .getInstance()

	if len() == 1 && strings.ContainsRune([0], ',') {
		.Statement.Omits = strings.FieldsFunc([0], utils.IsValidDBNameChar)
	} else {
		.Statement.Omits = 
	}
	return
}

// Where add conditions
//
// See the [docs] for details on the various formats that where clauses can take. By default, where clauses chain with AND.
//
//	// Find the first user with name jinzhu
//	db.Where("name = ?", "jinzhu").First(&user)
//	// Find the first user with name jinzhu and age 20
//	db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
//	// Find the first user with name jinzhu and age not equal to 20
//	db.Where("name = ?", "jinzhu").Where("age <> ?", "20").First(&user)
//
// [docs]: https://gorm.io/docs/query.html#Conditions
func ( *DB) ( interface{},  ...interface{}) ( *DB) {
	 = .getInstance()
	if  := .Statement.BuildCondition(, ...); len() > 0 {
		.Statement.AddClause(clause.Where{Exprs: })
	}
	return
}

// Not add NOT conditions
//
// Not works similarly to where, and has the same syntax.
//
//	// Find the first user with name not equal to jinzhu
//	db.Not("name = ?", "jinzhu").First(&user)
func ( *DB) ( interface{},  ...interface{}) ( *DB) {
	 = .getInstance()
	if  := .Statement.BuildCondition(, ...); len() > 0 {
		.Statement.AddClause(clause.Where{Exprs: []clause.Expression{clause.Not(...)}})
	}
	return
}

// Or add OR conditions
//
// Or is used to chain together queries with an OR.
//
//	// Find the first user with name equal to jinzhu or john
//	db.Where("name = ?", "jinzhu").Or("name = ?", "john").First(&user)
func ( *DB) ( interface{},  ...interface{}) ( *DB) {
	 = .getInstance()
	if  := .Statement.BuildCondition(, ...); len() > 0 {
		.Statement.AddClause(clause.Where{Exprs: []clause.Expression{clause.Or(clause.And(...))}})
	}
	return
}

// Joins specify Joins conditions
//
//	db.Joins("Account").Find(&user)
//	db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Find(&user)
//	db.Joins("Account", DB.Select("id").Where("user_id = users.id AND name = ?", "someName").Model(&Account{}))
func ( *DB) ( string,  ...interface{}) ( *DB) {
	return joins(, clause.LeftJoin, , ...)
}

// InnerJoins specify inner joins conditions
// db.InnerJoins("Account").Find(&user)
func ( *DB) ( string,  ...interface{}) ( *DB) {
	return joins(, clause.InnerJoin, , ...)
}

func joins( *DB,  clause.JoinType,  string,  ...interface{}) ( *DB) {
	 = .getInstance()

	if len() == 1 {
		if ,  := [0].(*DB);  {
			 := join{
				Name: , Conds: , Selects: .Statement.Selects,
				Omits: .Statement.Omits, JoinType: ,
			}
			if ,  := .Statement.Clauses["WHERE"].Expression.(clause.Where);  {
				.On = &
			}
			.Statement.Joins = append(.Statement.Joins, )
			return
		}
	}

	.Statement.Joins = append(.Statement.Joins, join{Name: , Conds: , JoinType: })
	return
}

// Group specify the group method on the find
//
//	// Select the sum age of users with given names
//	db.Model(&User{}).Select("name, sum(age) as total").Group("name").Find(&results)
func ( *DB) ( string) ( *DB) {
	 = .getInstance()

	 := strings.FieldsFunc(, utils.IsValidDBNameChar)
	.Statement.AddClause(clause.GroupBy{
		Columns: []clause.Column{{Name: , Raw: len() != 1}},
	})
	return
}

// Having specify HAVING conditions for GROUP BY
//
//	// Select the sum age of users with name jinzhu
//	db.Model(&User{}).Select("name, sum(age) as total").Group("name").Having("name = ?", "jinzhu").Find(&result)
func ( *DB) ( interface{},  ...interface{}) ( *DB) {
	 = .getInstance()
	.Statement.AddClause(clause.GroupBy{
		Having: .Statement.BuildCondition(, ...),
	})
	return
}

// Order specify order when retrieving records from database
//
//	db.Order("name DESC")
//	db.Order(clause.OrderByColumn{Column: clause.Column{Name: "name"}, Desc: true})
func ( *DB) ( interface{}) ( *DB) {
	 = .getInstance()

	switch v := .(type) {
	case clause.OrderByColumn:
		.Statement.AddClause(clause.OrderBy{
			Columns: []clause.OrderByColumn{},
		})
	case string:
		if  != "" {
			.Statement.AddClause(clause.OrderBy{
				Columns: []clause.OrderByColumn{{
					Column: clause.Column{Name: , Raw: true},
				}},
			})
		}
	}
	return
}

// Limit specify the number of records to be retrieved
//
// Limit conditions can be cancelled by using `Limit(-1)`.
//
//	// retrieve 3 users
//	db.Limit(3).Find(&users)
//	// retrieve 3 users into users1, and all users into users2
//	db.Limit(3).Find(&users1).Limit(-1).Find(&users2)
func ( *DB) ( int) ( *DB) {
	 = .getInstance()
	.Statement.AddClause(clause.Limit{Limit: &})
	return
}

// Offset specify the number of records to skip before starting to return the records
//
// Offset conditions can be cancelled by using `Offset(-1)`.
//
//	// select the third user
//	db.Offset(2).First(&user)
//	// select the first user by cancelling an earlier chained offset
//	db.Offset(5).Offset(-1).First(&user)
func ( *DB) ( int) ( *DB) {
	 = .getInstance()
	.Statement.AddClause(clause.Limit{Offset: })
	return
}

// Scopes pass current database connection to arguments `func(DB) DB`, which could be used to add conditions dynamically
//
//	func AmountGreaterThan1000(db *gorm.DB) *gorm.DB {
//	    return db.Where("amount > ?", 1000)
//	}
//
//	func OrderStatus(status []string) func (db *gorm.DB) *gorm.DB {
//	    return func (db *gorm.DB) *gorm.DB {
//	        return db.Scopes(AmountGreaterThan1000).Where("status in (?)", status)
//	    }
//	}
//
//	db.Scopes(AmountGreaterThan1000, OrderStatus([]string{"paid", "shipped"})).Find(&orders)
func ( *DB) ( ...func(*DB) *DB) ( *DB) {
	 = .getInstance()
	.Statement.scopes = append(.Statement.scopes, ...)
	return 
}

func ( *DB) () ( *DB) {
	 = .getInstance()
	 := .Statement.scopes
	if len() == 0 {
		return 
	}
	.Statement.scopes = nil

	 := make([]clause.Interface, 0, 4)
	if ,  := .Statement.Clauses["WHERE"];  && .Expression != nil {
		 = append(, .Expression.(clause.Interface))
		.Expression = nil
		.Statement.Clauses["WHERE"] = 
	}

	for ,  := range  {
		 = ()
		if ,  := .Statement.Clauses["WHERE"];  && .Expression != nil {
			 = append(, .Expression.(clause.Interface))
			.Expression = nil
			.Statement.Clauses["WHERE"] = 
		}
	}

	for ,  := range  {
		.Statement.AddClause()
	}
	return 
}

// Preload preload associations with given conditions
//
//	// get all users, and preload all non-cancelled orders
//	db.Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
func ( *DB) ( string,  ...interface{}) ( *DB) {
	 = .getInstance()
	if .Statement.Preloads == nil {
		.Statement.Preloads = map[string][]interface{}{}
	}
	.Statement.Preloads[] = 
	return
}

// Attrs provide attributes used in [FirstOrCreate] or [FirstOrInit]
//
// Attrs only adds attributes if the record is not found.
//
//	// assign an email if the record is not found
//	db.Where(User{Name: "non_existing"}).Attrs(User{Email: "fake@fake.org"}).FirstOrInit(&user)
//	// user -> User{Name: "non_existing", Email: "fake@fake.org"}
//
//	// assign an email if the record is not found, otherwise ignore provided email
//	db.Where(User{Name: "jinzhu"}).Attrs(User{Email: "fake@fake.org"}).FirstOrInit(&user)
//	// user -> User{Name: "jinzhu", Age: 20}
//
// [FirstOrCreate]: https://gorm.io/docs/advanced_query.html#FirstOrCreate
// [FirstOrInit]: https://gorm.io/docs/advanced_query.html#FirstOrInit
func ( *DB) ( ...interface{}) ( *DB) {
	 = .getInstance()
	.Statement.attrs = 
	return
}

// Assign provide attributes used in [FirstOrCreate] or [FirstOrInit]
//
// Assign adds attributes even if the record is found. If using FirstOrCreate, this means that
// records will be updated even if they are found.
//
//	// assign an email regardless of if the record is not found
//	db.Where(User{Name: "non_existing"}).Assign(User{Email: "fake@fake.org"}).FirstOrInit(&user)
//	// user -> User{Name: "non_existing", Email: "fake@fake.org"}
//
//	// assign email regardless of if record is found
//	db.Where(User{Name: "jinzhu"}).Assign(User{Email: "fake@fake.org"}).FirstOrInit(&user)
//	// user -> User{Name: "jinzhu", Age: 20, Email: "fake@fake.org"}
//
// [FirstOrCreate]: https://gorm.io/docs/advanced_query.html#FirstOrCreate
// [FirstOrInit]: https://gorm.io/docs/advanced_query.html#FirstOrInit
func ( *DB) ( ...interface{}) ( *DB) {
	 = .getInstance()
	.Statement.assigns = 
	return
}

func ( *DB) () ( *DB) {
	 = .getInstance()
	.Statement.Unscoped = true
	return
}

func ( *DB) ( string,  ...interface{}) ( *DB) {
	 = .getInstance()
	.Statement.SQL = strings.Builder{}

	if strings.Contains(, "@") {
		clause.NamedExpr{SQL: , Vars: }.Build(.Statement)
	} else {
		clause.Expr{SQL: , Vars: }.Build(.Statement)
	}
	return
}