package bun

import (
	
	
	
	
	
	
	
	

	
	
	
)

type union struct {
	expr  string
	query *SelectQuery
}

type SelectQuery struct {
	whereBaseQuery

	distinctOn []schema.QueryWithArgs
	joins      []joinQuery
	group      []schema.QueryWithArgs
	having     []schema.QueryWithArgs
	order      []schema.QueryWithArgs
	limit      int32
	offset     int32
	selFor     schema.QueryWithArgs

	union []union
}

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

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

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

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

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

func ( *SelectQuery) () *SelectQuery {
	.distinctOn = make([]schema.QueryWithArgs, 0)
	return 
}

func ( *SelectQuery) ( string,  ...interface{}) *SelectQuery {
	.distinctOn = append(.distinctOn, schema.SafeQuery(, ))
	return 
}

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

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

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

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

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

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

func ( *SelectQuery) ( string,  ...interface{}) *SelectQuery {
	.addColumn(schema.SafeQuery(, ))
	return 
}

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

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

func ( *SelectQuery) () *SelectQuery {
	.flags = .flags.Set(wherePKFlag)
	return 
}

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

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

func ( *SelectQuery) ( string,  func(*SelectQuery) *SelectQuery) *SelectQuery {
	 := .where
	.where = nil

	 = ()

	 := .where
	.where = 

	.addWhereGroup(, )

	return 
}

func ( *SelectQuery) () *SelectQuery {
	.whereDeleted()
	return 
}

func ( *SelectQuery) () *SelectQuery {
	.whereAllWithDeleted()
	return 
}

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

func ( *SelectQuery) ( ...string) *SelectQuery {
	for ,  := range  {
		.group = append(.group, schema.UnsafeIdent())
	}
	return 
}

func ( *SelectQuery) ( string,  ...interface{}) *SelectQuery {
	.group = append(.group, schema.SafeQuery(, ))
	return 
}

func ( *SelectQuery) ( string,  ...interface{}) *SelectQuery {
	.having = append(.having, schema.SafeQuery(, ))
	return 
}

func ( *SelectQuery) ( ...string) *SelectQuery {
	for ,  := range  {
		if  == "" {
			continue
		}

		 := strings.IndexByte(, ' ')
		if  == -1 {
			.order = append(.order, schema.UnsafeIdent())
			continue
		}

		 := [:]
		 := [+1:]

		switch strings.ToUpper() {
		case "ASC", "DESC", "ASC NULLS FIRST", "DESC NULLS FIRST",
			"ASC NULLS LAST", "DESC NULLS LAST":
			.order = append(.order, schema.SafeQuery("? ?", []interface{}{
				Ident(),
				Safe(),
			}))
		default:
			.order = append(.order, schema.UnsafeIdent())
		}
	}
	return 
}

func ( *SelectQuery) ( string,  ...interface{}) *SelectQuery {
	.order = append(.order, schema.SafeQuery(, ))
	return 
}

func ( *SelectQuery) ( int) *SelectQuery {
	.limit = int32()
	return 
}

func ( *SelectQuery) ( int) *SelectQuery {
	.offset = int32()
	return 
}

func ( *SelectQuery) ( string,  ...interface{}) *SelectQuery {
	.selFor = schema.SafeQuery(, )
	return 
}

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

func ( *SelectQuery) ( *SelectQuery) *SelectQuery {
	return .addUnion(" UNION ", )
}

func ( *SelectQuery) ( *SelectQuery) *SelectQuery {
	return .addUnion(" UNION ALL ", )
}

func ( *SelectQuery) ( *SelectQuery) *SelectQuery {
	return .addUnion(" INTERSECT ", )
}

func ( *SelectQuery) ( *SelectQuery) *SelectQuery {
	return .addUnion(" INTERSECT ALL ", )
}

func ( *SelectQuery) ( *SelectQuery) *SelectQuery {
	return .addUnion(" EXCEPT ", )
}

func ( *SelectQuery) ( *SelectQuery) *SelectQuery {
	return .addUnion(" EXCEPT ALL ", )
}

func ( *SelectQuery) ( string,  *SelectQuery) *SelectQuery {
	.union = append(.union, union{
		expr:  ,
		query: ,
	})
	return 
}

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

func ( *SelectQuery) ( string,  ...interface{}) *SelectQuery {
	.joins = append(.joins, joinQuery{
		join: schema.SafeQuery(, ),
	})
	return 
}

func ( *SelectQuery) ( string,  ...interface{}) *SelectQuery {
	return .joinOn(, , " AND ")
}

func ( *SelectQuery) ( string,  ...interface{}) *SelectQuery {
	return .joinOn(, , " OR ")
}

func ( *SelectQuery) ( string,  []interface{},  string) *SelectQuery {
	if len(.joins) == 0 {
		.err = errors.New("bun: query has no joins")
		return 
	}
	 := &.joins[len(.joins)-1]
	.on = append(.on, schema.SafeQueryWithSep(, , ))
	return 
}

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

// Relation adds a relation to the query.
func ( *SelectQuery) ( string,  ...func(*SelectQuery) *SelectQuery) *SelectQuery {
	if len() > 1 {
		panic("only one apply function is supported")
	}

	if .tableModel == nil {
		.setErr(errNilModel)
		return 
	}

	 := .tableModel.Join()
	if  == nil {
		.setErr(fmt.Errorf("%s does not have relation=%q", .table, ))
		return 
	}

	if len() == 1 {
		.apply = [0]
	}

	return 
}

func ( *SelectQuery) ( func(*relationJoin) error) error {
	if .tableModel == nil {
		return nil
	}
	return ._forEachHasOneJoin(, .tableModel.GetJoins())
}

func ( *SelectQuery) ( func(*relationJoin) error,  []relationJoin) error {
	for  := range  {
		 := &[]
		switch .Relation.Type {
		case schema.HasOneRelation, schema.BelongsToRelation:
			if  := ();  != nil {
				return 
			}
			if  := .(, .JoinModel.GetJoins());  != nil {
				return 
			}
		}
	}
	return nil
}

func ( *SelectQuery) ( context.Context,  []relationJoin) error {
	for  := range  {
		 := &[]

		var  error

		switch .Relation.Type {
		case schema.HasOneRelation, schema.BelongsToRelation:
			 = .(, .JoinModel.GetJoins())
		case schema.HasManyRelation:
			 = .selectMany(, .db.NewSelect())
		case schema.ManyToManyRelation:
			 = .selectM2M(, .db.NewSelect())
		default:
			panic("not reached")
		}

		if  != nil {
			return 
		}
	}
	return nil
}

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

func ( *SelectQuery) () string {
	return "SELECT"
}

func ( *SelectQuery) ( schema.Formatter,  []byte) ( []byte,  error) {
	return .appendQuery(, , false)
}

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

	 :=  && (len(.group) > 0 || .distinctOn != nil)
	if  {
		 = append(, "WITH _count_wrapper AS ("...)
	}

	if len(.union) > 0 {
		 = append(, '(')
	}

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

	 = append(, "SELECT "...)

	if len(.distinctOn) > 0 {
		 = append(, "DISTINCT ON ("...)
		for ,  := range .distinctOn {
			if  > 0 {
				 = append(, ", "...)
			}
			,  = .AppendQuery(, )
			if  != nil {
				return nil, 
			}
		}
		 = append(, ") "...)
	} else if .distinctOn != nil {
		 = append(, "DISTINCT "...)
	}

	if  && ! {
		 = append(, "count(*)"...)
	} else {
		,  = .appendColumns(, )
		if  != nil {
			return nil, 
		}
	}

	if .hasTables() {
		,  = .appendTables(, )
		if  != nil {
			return nil, 
		}
	}

	if  := .forEachHasOneJoin(func( *relationJoin) error {
		 = append(, ' ')
		,  = .appendHasOneJoin(, , )
		return 
	});  != nil {
		return nil, 
	}

	for ,  := range .joins {
		,  = .AppendQuery(, )
		if  != nil {
			return nil, 
		}
	}

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

	if len(.group) > 0 {
		 = append(, " GROUP BY "...)
		for ,  := range .group {
			if  > 0 {
				 = append(, ", "...)
			}
			,  = .AppendQuery(, )
			if  != nil {
				return nil, 
			}
		}
	}

	if len(.having) > 0 {
		 = append(, " HAVING "...)
		for ,  := range .having {
			if  > 0 {
				 = append(, " AND "...)
			}
			 = append(, '(')
			,  = .AppendQuery(, )
			if  != nil {
				return nil, 
			}
			 = append(, ')')
		}
	}

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

		if .limit != 0 {
			 = append(, " LIMIT "...)
			 = strconv.AppendInt(, int64(.limit), 10)
		}

		if .offset != 0 {
			 = append(, " OFFSET "...)
			 = strconv.AppendInt(, int64(.offset), 10)
		}

		if !.selFor.IsZero() {
			 = append(, " FOR "...)
			,  = .selFor.AppendQuery(, )
			if  != nil {
				return nil, 
			}
		}
	}

	if len(.union) > 0 {
		 = append(, ')')

		for ,  := range .union {
			 = append(, .expr...)
			 = append(, '(')
			,  = .query.AppendQuery(, )
			if  != nil {
				return nil, 
			}
			 = append(, ')')
		}
	}

	if  {
		 = append(, ") SELECT count(*) FROM _count_wrapper"...)
	}

	return , nil
}

func ( *SelectQuery) ( schema.Formatter,  []byte) ( []byte,  error) {
	 := len()

	switch {
	case .columns != nil:
		for ,  := range .columns {
			if  > 0 {
				 = append(, ", "...)
			}

			if .Args == nil && .table != nil {
				if ,  := .table.FieldMap[.Query];  {
					 = append(, .table.SQLAlias...)
					 = append(, '.')
					 = append(, .SQLName...)
					continue
				}
			}

			,  = .AppendQuery(, )
			if  != nil {
				return nil, 
			}
		}
	case .table != nil:
		if len(.table.Fields) > 10 && .IsNop() {
			 = append(, .table.SQLAlias...)
			 = append(, '.')
			 = dialect.AppendString(, fmt.Sprintf("%d columns", len(.table.Fields)))
		} else {
			 = appendColumns(, .table.SQLAlias, .table.Fields)
		}
	default:
		 = append(, '*')
	}

	if  := .forEachHasOneJoin(func( *relationJoin) error {
		if len() !=  {
			 = append(, ", "...)
			 = len()
		}

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

		return nil
	});  != nil {
		return nil, 
	}

	 = bytes.TrimSuffix(, []byte(", "))

	return , nil
}

func ( *SelectQuery) (
	 schema.Formatter,  []byte,  *relationJoin,
) ( []byte,  error) {
	.applyTo()

	if .columns != nil {
		 := .JoinModel.Table()
		for ,  := range .columns {
			if  > 0 {
				 = append(, ", "...)
			}

			if .Args == nil {
				if ,  := .FieldMap[.Query];  {
					 = .appendAlias(, )
					 = append(, '.')
					 = append(, .SQLName...)
					 = append(, " AS "...)
					 = .appendAliasColumn(, , .Name)
					continue
				}
			}

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

	for ,  := range .JoinModel.Table().Fields {
		if  > 0 {
			 = append(, ", "...)
		}
		 = .appendAlias(, )
		 = append(, '.')
		 = append(, .SQLName...)
		 = append(, " AS "...)
		 = .appendAliasColumn(, , .Name)
	}
	return , nil
}

func ( *SelectQuery) ( schema.Formatter,  []byte) ( []byte,  error) {
	 = append(, " FROM "...)
	return .appendTablesWithAlias(, )
}

func ( *SelectQuery) ( schema.Formatter,  []byte) ( []byte,  error) {
	if len(.order) > 0 {
		 = append(, " ORDER BY "...)

		for ,  := range .order {
			if  > 0 {
				 = append(, ", "...)
			}
			,  = .AppendQuery(, )
			if  != nil {
				return nil, 
			}
		}

		return , nil
	}
	return , nil
}

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

func ( *SelectQuery) ( context.Context) (*sql.Rows, error) {
	,  := .AppendQuery(.db.fmter, .db.makeQueryBytes())
	if  != nil {
		return nil, 
	}

	 := internal.String()
	return .conn.QueryContext(, )
}

func ( *SelectQuery) ( context.Context) ( sql.Result,  error) {
	,  := .AppendQuery(.db.fmter, .db.makeQueryBytes())
	if  != nil {
		return nil, 
	}

	 := internal.String()

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

	return , nil
}

func ( *SelectQuery) ( context.Context,  ...interface{}) error {
	,  := .getModel()
	if  != nil {
		return 
	}

	if .limit > 1 {
		if ,  := .(interface{ (int) });  {
			.(int(.limit))
		}
	}

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

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

	 := internal.String()

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

	if ,  := .RowsAffected();  > 0 {
		if ,  := .(tableModel);  {
			if  := .selectJoins(, .GetJoins());  != nil {
				return 
			}
		}
	}

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

	return nil
}

func ( *SelectQuery) ( context.Context) error {
	if ,  := .table.ZeroIface.(BeforeSelectHook);  {
		if  := .BeforeSelect(, );  != nil {
			return 
		}
	}
	return nil
}

func ( *SelectQuery) ( context.Context) error {
	if ,  := .table.ZeroIface.(AfterSelectHook);  {
		if  := .AfterSelect(, );  != nil {
			return 
		}
	}
	return nil
}

func ( *SelectQuery) ( context.Context) (int, error) {
	 := countQuery{}

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

	 := internal.String()
	,  := .db.beforeQuery(, , , nil)

	var  int
	 = .conn.QueryRowContext(, ).Scan(&)

	.db.afterQuery(, , nil, )

	return , 
}

func ( *SelectQuery) ( context.Context,  ...interface{}) (int, error) {
	var  int
	var  sync.WaitGroup
	var  sync.Mutex
	var  error

	if .limit >= 0 {
		.Add(1)
		go func() {
			defer .Done()

			if  := .Scan(, ...);  != nil {
				.Lock()
				if  == nil {
					 = 
				}
				.Unlock()
			}
		}()
	}

	.Add(1)
	go func() {
		defer .Done()

		var  error
		,  = .Count()
		if  != nil {
			.Lock()
			if  == nil {
				 = 
			}
			.Unlock()
		}
	}()

	.Wait()
	return , 
}

func ( *SelectQuery) ( context.Context) (bool, error) {
	 := existsQuery{}

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

	 := internal.String()
	,  := .db.beforeQuery(, , , nil)

	var  bool
	 = .conn.QueryRowContext(, ).Scan(&)

	.db.afterQuery(, , nil, )

	return , 
}

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

type joinQuery struct {
	join schema.QueryWithArgs
	on   []schema.QueryWithSep
}

func ( *joinQuery) ( schema.Formatter,  []byte) ( []byte,  error) {
	 = append(, ' ')

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

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

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

	return , nil
}

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

type countQuery struct {
	*SelectQuery
}

func ( countQuery) ( schema.Formatter,  []byte) ( []byte,  error) {
	return .appendQuery(, , true)
}

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

type existsQuery struct {
	*SelectQuery
}

func ( existsQuery) ( schema.Formatter,  []byte) ( []byte,  error) {
	 = append(, "SELECT EXISTS ("...)

	,  = .appendQuery(, , false)
	if  != nil {
		return nil, 
	}

	 = append(, ")"...)

	return , nil
}