package bun

import (
	
	

	
	
)

type relationJoin struct {
	Parent    *relationJoin
	BaseModel tableModel
	JoinModel tableModel
	Relation  *schema.Relation

	apply   func(*SelectQuery) *SelectQuery
	columns []schema.QueryWithArgs
}

func ( *relationJoin) ( *SelectQuery) {
	if .apply == nil {
		return
	}

	var  *schema.Table
	var  []schema.QueryWithArgs

	// Save state.
	, .table = .table, .JoinModel.Table()
	, .columns = .columns, nil

	 = .apply()

	// Restore state.
	.table = 
	.columns, .columns = .columns, 
}

func ( *relationJoin) ( context.Context,  *SelectQuery) error {
	switch .Relation.Type {
	}
	panic("not reached")
}

func ( *relationJoin) ( context.Context,  *SelectQuery) error {
	 = .manyQuery()
	if  == nil {
		return nil
	}
	return .Scan()
}

func ( *relationJoin) ( *SelectQuery) *SelectQuery {
	 := newHasManyModel()
	if  == nil {
		return nil
	}

	 = .Model()

	var  []byte
	if len(.Relation.JoinFields) > 1 {
		 = append(, '(')
	}
	 = appendColumns(, .JoinModel.Table().SQLAlias, .Relation.JoinFields)
	if len(.Relation.JoinFields) > 1 {
		 = append(, ')')
	}
	 = append(, " IN ("...)
	 = appendChildValues(
		.db.Formatter(),
		,
		.JoinModel.Root(),
		.JoinModel.ParentIndex(),
		.Relation.BaseFields,
	)
	 = append(, ")"...)
	 = .Where(internal.String())

	if .Relation.PolymorphicField != nil {
		 = .Where("? = ?", .Relation.PolymorphicField.SQLName, .Relation.PolymorphicValue)
	}

	.applyTo()
	 = .Apply(.hasManyColumns)

	return 
}

func ( *relationJoin) ( *SelectQuery) *SelectQuery {
	if .Relation.M2MTable != nil {
		 = .ColumnExpr(string(.Relation.M2MTable.SQLAlias) + ".*")
	}

	 := make([]byte, 0, 32)

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

			var  error
			,  = .AppendQuery(.db.fmter, )
			if  != nil {
				.err = 
				return 
			}
		}
	} else {
		 := .JoinModel.Table()
		 = appendColumns(, .SQLAlias, .Fields)
	}

	 = .ColumnExpr(internal.String())

	return 
}

func ( *relationJoin) ( context.Context,  *SelectQuery) error {
	 = .m2mQuery()
	if  == nil {
		return nil
	}
	return .Scan()
}

func ( *relationJoin) ( *SelectQuery) *SelectQuery {
	 := .db.fmter

	 := newM2MModel()
	if  == nil {
		return nil
	}
	 = .Model()

	 := .JoinModel.ParentIndex()
	 := .BaseModel.Table()

	//nolint
	var  []byte
	 = append(, "JOIN "...)
	 = .AppendQuery(, string(.Relation.M2MTable.Name))
	 = append(, " AS "...)
	 = append(, .Relation.M2MTable.SQLAlias...)
	 = append(, " ON ("...)
	for ,  := range .Relation.M2MBaseFields {
		if  > 0 {
			 = append(, ", "...)
		}
		 = append(, .Relation.M2MTable.SQLAlias...)
		 = append(, '.')
		 = append(, .SQLName...)
	}
	 = append(, ") IN ("...)
	 = appendChildValues(, , .BaseModel.Root(), , .PKs)
	 = append(, ")"...)
	 = .Join(internal.String())

	 := .JoinModel.Table()
	for ,  := range .Relation.M2MJoinFields {
		 := .Relation.JoinFields[]
		 = .Where("?.? = ?.?",
			.SQLAlias, .SQLName,
			.Relation.M2MTable.SQLAlias, .SQLName)
	}

	.applyTo()
	 = .Apply(.hasManyColumns)

	return 
}

func ( *relationJoin) () bool {
	if .Parent != nil {
		switch .Parent.Relation.Type {
		case schema.HasOneRelation, schema.BelongsToRelation:
			return true
		}
	}
	return false
}

func ( *relationJoin) ( schema.Formatter,  []byte) []byte {
	 := .IdentQuote()

	 = append(, )
	 = appendAlias(, )
	 = append(, )
	return 
}

func ( *relationJoin) ( schema.Formatter,  []byte,  string) []byte {
	 := .IdentQuote()

	 = append(, )
	 = appendAlias(, )
	 = append(, "__"...)
	 = append(, ...)
	 = append(, )
	return 
}

func ( *relationJoin) ( schema.Formatter,  []byte) []byte {
	 := .IdentQuote()

	if .hasParent() {
		 = append(, )
		 = appendAlias(, .Parent)
		 = append(, )
		return 
	}
	return append(, .BaseModel.Table().SQLAlias...)
}

func ( *relationJoin) ( []byte,  internal.Flag) []byte {
	 = append(, '.')
	 = append(, .JoinModel.Table().SoftDeleteField.SQLName...)
	if .Has(deletedFlag) {
		 = append(, " IS NOT NULL"...)
	} else {
		 = append(, " IS NULL"...)
	}
	return 
}

func ( []byte,  *relationJoin) []byte {
	if .hasParent() {
		 = (, .Parent)
		 = append(, "__"...)
	}
	 = append(, .Relation.Field.Name...)
	return 
}

func ( *relationJoin) (
	 schema.Formatter,  []byte,  *SelectQuery,
) ( []byte,  error) {
	 := .JoinModel.Table().SoftDeleteField != nil && !.flags.Has(allWithDeletedFlag)

	 = append(, "LEFT JOIN "...)
	 = .AppendQuery(, string(.JoinModel.Table().SQLNameForSelects))
	 = append(, " AS "...)
	 = .appendAlias(, )

	 = append(, " ON "...)

	 = append(, '(')
	for ,  := range .Relation.BaseFields {
		if  > 0 {
			 = append(, " AND "...)
		}
		 = .appendAlias(, )
		 = append(, '.')
		 = append(, .Relation.JoinFields[].SQLName...)
		 = append(, " = "...)
		 = .appendBaseAlias(, )
		 = append(, '.')
		 = append(, .SQLName...)
	}
	 = append(, ')')

	if  {
		 = append(, " AND "...)
		 = .appendAlias(, )
		 = .appendSoftDelete(, .flags)
	}

	return , nil
}

func (
	 schema.Formatter,  []byte,  reflect.Value,  []int,  []*schema.Field,
) []byte {
	 := make(map[string]struct{})
	walk(, , func( reflect.Value) {
		 := len()

		if len() > 1 {
			 = append(, '(')
		}
		for ,  := range  {
			if  > 0 {
				 = append(, ", "...)
			}
			 = .AppendValue(, , )
		}
		if len() > 1 {
			 = append(, ')')
		}
		 = append(, ", "...)

		if ,  := [string([:])];  {
			 = [:]
		} else {
			[string([:])] = struct{}{}
		}
	})
	if len() > 0 {
		 = [:len()-2] // trim ", "
	}
	return 
}