package bun

import (
	
	
	
	
	

	
	
	
)

type InsertQuery struct {
	whereBaseQuery
	returningQuery
	customValueQuery

	on schema.QueryWithArgs
	setQuery

	ignore  bool
	replace bool
}

func ( *DB) *InsertQuery {
	 := &InsertQuery{
		whereBaseQuery: whereBaseQuery{
			baseQuery: baseQuery{
				db:   ,
				conn: .DB,
			},
		},
	}
	return 
}

func ( *InsertQuery) ( IConn) *InsertQuery {
	.setConn()
	return 
}

func ( *InsertQuery) ( interface{}) *InsertQuery {
	.setTableModel()
	return 
}

// Apply calls the fn passing the SelectQuery as an argument.
func ( *InsertQuery) ( func(*InsertQuery) *InsertQuery) *InsertQuery {
	return ()
}

func ( *InsertQuery) ( string,  schema.QueryAppender) *InsertQuery {
	.addWith(, )
	return 
}

//------------------------------------------------------------------------------

func ( *InsertQuery) ( ...string) *InsertQuery {
	for ,  := range  {
		.addTable(schema.UnsafeIdent())
	}
	return 
}

func ( *InsertQuery) ( string,  ...interface{}) *InsertQuery {
	.addTable(schema.SafeQuery(, ))
	return 
}

func ( *InsertQuery) ( string,  ...interface{}) *InsertQuery {
	.modelTable = schema.SafeQuery(, )
	return 
}

//------------------------------------------------------------------------------

func ( *InsertQuery) ( ...string) *InsertQuery {
	for ,  := range  {
		.addColumn(schema.UnsafeIdent())
	}
	return 
}

func ( *InsertQuery) ( ...string) *InsertQuery {
	.excludeColumn()
	return 
}

// Value overwrites model value for the column.
func ( *InsertQuery) ( string,  string,  ...interface{}) *InsertQuery {
	if .table == nil {
		.err = errNilModel
		return 
	}
	.addValue(.table, , , )
	return 
}

func ( *InsertQuery) ( string,  ...interface{}) *InsertQuery {
	.addWhere(schema.SafeQueryWithSep(, , " AND "))
	return 
}

func ( *InsertQuery) ( string,  ...interface{}) *InsertQuery {
	.addWhere(schema.SafeQueryWithSep(, , " OR "))
	return 
}

//------------------------------------------------------------------------------

// Returning adds a RETURNING clause to the query.
//
// To suppress the auto-generated RETURNING clause, use `Returning("NULL")`.
func ( *InsertQuery) ( string,  ...interface{}) *InsertQuery {
	.addReturning(schema.SafeQuery(, ))
	return 
}

func ( *InsertQuery) () bool {
	if !.db.features.Has(feature.Returning) {
		return false
	}
	return .returningQuery.hasReturning()
}

//------------------------------------------------------------------------------

// Ignore generates an `INSERT IGNORE INTO` query (MySQL).
func ( *InsertQuery) () *InsertQuery {
	.ignore = true
	return 
}

// Replaces generates a `REPLACE INTO` query (MySQL).
func ( *InsertQuery) () *InsertQuery {
	.replace = true
	return 
}

//------------------------------------------------------------------------------

func ( *InsertQuery) () string {
	return "INSERT"
}

func ( *InsertQuery) ( schema.Formatter,  []byte) ( []byte,  error) {
	if .err != nil {
		return nil, .err
	}
	 = formatterWithModel(, )

	,  = .appendWith(, )
	if  != nil {
		return nil, 
	}

	if .replace {
		 = append(, "REPLACE "...)
	} else {
		 = append(, "INSERT "...)
		if .ignore {
			 = append(, "IGNORE "...)
		}
	}
	 = append(, "INTO "...)

	if .db.features.Has(feature.InsertTableAlias) && !.on.IsZero() {
		,  = .appendFirstTableWithAlias(, )
	} else {
		,  = .appendFirstTable(, )
	}
	if  != nil {
		return nil, 
	}

	,  = .appendColumnsValues(, )
	if  != nil {
		return nil, 
	}

	,  = .appendOn(, )
	if  != nil {
		return nil, 
	}

	if .hasReturning() {
		,  = .appendReturning(, )
		if  != nil {
			return nil, 
		}
	}

	return , nil
}

func ( *InsertQuery) (
	 schema.Formatter,  []byte,
) ( []byte,  error) {
	if .hasMultiTables() {
		if .columns != nil {
			 = append(, " ("...)
			,  = .appendColumns(, )
			if  != nil {
				return nil, 
			}
			 = append(, ")"...)
		}

		 = append(, " SELECT * FROM "...)
		,  = .appendOtherTables(, )
		if  != nil {
			return nil, 
		}

		return , nil
	}

	if ,  := .model.(*mapModel);  {
		return .appendColumnsValues(, ), nil
	}
	if ,  := .model.(*mapSliceModel);  {
		return nil, fmt.Errorf("Insert(*[]map[string]interface{}) is not supported")
	}

	if .model == nil {
		return nil, errNilModel
	}

	,  := .getFields()
	if  != nil {
		return nil, 
	}

	 = append(, " ("...)
	 = .appendFields(, , )
	 = append(, ") VALUES ("...)

	switch model := .tableModel.(type) {
	case *structTableModel:
		,  = .appendStructValues(, , , .strct)
		if  != nil {
			return nil, 
		}
	case *sliceTableModel:
		,  = .appendSliceValues(, , , .slice)
		if  != nil {
			return nil, 
		}
	default:
		return nil, fmt.Errorf("bun: Insert does not support %T", .tableModel)
	}

	 = append(, ')')

	return , nil
}

func ( *InsertQuery) (
	 schema.Formatter,  []byte,  []*schema.Field,  reflect.Value,
) ( []byte,  error) {
	 := .IsNop()
	for ,  := range  {
		if  > 0 {
			 = append(, ", "...)
		}

		,  := .modelValues[.Name]
		if  {
			,  = .AppendQuery(, )
			if  != nil {
				return nil, 
			}
			.addReturningField()
			continue
		}

		switch {
		case :
			 = append(, '?')
		case .NullZero && .HasZeroValue():
			if .db.features.Has(feature.DefaultPlaceholder) {
				 = append(, "DEFAULT"...)
			} else if .SQLDefault != "" {
				 = append(, .SQLDefault...)
			} else {
				 = append(, "NULL"...)
			}
			.addReturningField()
		default:
			 = .AppendValue(, , )
		}
	}

	for ,  := range .extraValues {
		if  > 0 || len() > 0 {
			 = append(, ", "...)
		}

		,  = .value.AppendQuery(, )
		if  != nil {
			return nil, 
		}
	}

	return , nil
}

func ( *InsertQuery) (
	 schema.Formatter,  []byte,  []*schema.Field,  reflect.Value,
) ( []byte,  error) {
	if .IsNop() {
		return .appendStructValues(, , , reflect.Value{})
	}

	 := .Len()
	for  := 0;  < ; ++ {
		if  > 0 {
			 = append(, "), ("...)
		}
		 := indirect(.Index())
		,  = .appendStructValues(, , , )
		if  != nil {
			return nil, 
		}
	}

	for ,  := range .extraValues {
		if  > 0 || len() > 0 {
			 = append(, ", "...)
		}

		,  = .value.AppendQuery(, )
		if  != nil {
			return nil, 
		}
	}

	return , nil
}

func ( *InsertQuery) () ([]*schema.Field, error) {
	if .db.features.Has(feature.DefaultPlaceholder) || len(.columns) > 0 {
		return .baseQuery.getFields()
	}

	var  reflect.Value

	switch model := .tableModel.(type) {
	case *structTableModel:
		 = .strct
	case *sliceTableModel:
		if .sliceLen == 0 {
			return nil, fmt.Errorf("bun: Insert(empty %T)", .slice.Type())
		}
		 = indirect(.slice.Index(0))
	}

	 := make([]*schema.Field, 0, len(.table.Fields))

	for ,  := range .table.Fields {
		if .NotNull && .NullZero && .SQLDefault == "" && .HasZeroValue() {
			.addReturningField()
			continue
		}
		 = append(, )
	}

	return , nil
}

func ( *InsertQuery) (
	 schema.Formatter,  []byte,  []*schema.Field,
) []byte {
	 = appendColumns(, "", )
	for ,  := range .extraValues {
		if  > 0 || len() > 0 {
			 = append(, ", "...)
		}
		 = .AppendIdent(, .column)
	}
	return 
}

//------------------------------------------------------------------------------

func ( *InsertQuery) ( string,  ...interface{}) *InsertQuery {
	.on = schema.SafeQuery(, )
	return 
}

func ( *InsertQuery) ( string,  ...interface{}) *InsertQuery {
	.addSet(schema.SafeQuery(, ))
	return 
}

func ( *InsertQuery) ( schema.Formatter,  []byte) ( []byte,  error) {
	if .on.IsZero() {
		return , nil
	}

	 = append(, " ON "...)
	,  = .on.AppendQuery(, )
	if  != nil {
		return nil, 
	}

	if len(.set) > 0 {
		if .HasFeature(feature.OnDuplicateKey) {
			 = append(, ' ')
		} else {
			 = append(, " SET "...)
		}

		,  = .appendSet(, )
		if  != nil {
			return nil, 
		}
	} else if .onConflictDoUpdate() {
		,  := .getDataFields()
		if  != nil {
			return nil, 
		}

		if len() == 0 {
			 = .tableModel.Table().DataFields
		}

		 = .appendSetExcluded(, )
	}

	if len(.where) > 0 {
		 = append(, " WHERE "...)

		,  = appendWhere(, , .where)
		if  != nil {
			return nil, 
		}
	}

	return , nil
}

func ( *InsertQuery) () bool {
	return strings.HasSuffix(strings.ToUpper(.on.Query), " DO UPDATE")
}

func ( *InsertQuery) ( []byte,  []*schema.Field) []byte {
	 = append(, " SET "...)
	for ,  := range  {
		if  > 0 {
			 = append(, ", "...)
		}
		 = append(, .SQLName...)
		 = append(, " = EXCLUDED."...)
		 = append(, .SQLName...)
	}
	return 
}

//------------------------------------------------------------------------------

func ( *InsertQuery) ( context.Context,  ...interface{}) (sql.Result, error) {
	if .table != nil {
		if  := .beforeInsertHook();  != nil {
			return nil, 
		}
	}

	,  := .AppendQuery(.db.fmter, .db.makeQueryBytes())
	if  != nil {
		return nil, 
	}

	 := internal.String()
	var  sql.Result

	if  := len() > 0;  || .hasReturning() {
		,  := .getModel()
		if  != nil {
			return nil, 
		}

		,  = .scan(, , , , )
		if  != nil {
			return nil, 
		}
	} else {
		,  = .exec(, , )
		if  != nil {
			return nil, 
		}

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

	if .table != nil {
		if  := .afterInsertHook();  != nil {
			return nil, 
		}
	}

	return , nil
}

func ( *InsertQuery) ( context.Context) error {
	if ,  := .table.ZeroIface.(BeforeInsertHook);  {
		if  := .BeforeInsert(, );  != nil {
			return 
		}
	}
	return nil
}

func ( *InsertQuery) ( context.Context) error {
	if ,  := .table.ZeroIface.(AfterInsertHook);  {
		if  := .AfterInsert(, );  != nil {
			return 
		}
	}
	return nil
}

func ( *InsertQuery) ( sql.Result,  []interface{}) error {
	if .db.features.Has(feature.Returning) || .table == nil || len(.table.PKs) != 1 {
		return nil
	}

	,  := .LastInsertId()
	if  != nil {
		return 
	}
	if  == 0 {
		return nil
	}

	,  := .getModel()
	if  != nil {
		return 
	}

	 := .table.PKs[0]
	switch model := .(type) {
	case *structTableModel:
		if  := .ScanValue(.strct, );  != nil {
			return 
		}
	case *sliceTableModel:
		 := .slice.Len()
		for  := 0;  < ; ++ {
			 := indirect(.slice.Index())
			if  := .ScanValue(, );  != nil {
				return 
			}
			++
		}
	}

	return nil
}