package pgx

import (
	
	

	
	
	
)

// ExtendedQueryBuilder is used to choose the parameter formats, to format the parameters and to choose the result
// formats for an extended query.
type ExtendedQueryBuilder struct {
	ParamValues     [][]byte
	paramValueBytes []byte
	ParamFormats    []int16
	ResultFormats   []int16
}

// Build sets ParamValues, ParamFormats, and ResultFormats for use with *PgConn.ExecParams or *PgConn.ExecPrepared. If
// sd is nil then QueryExecModeExec behavior will be used.
func ( *ExtendedQueryBuilder) ( *pgtype.Map,  *pgconn.StatementDescription,  []any) error {
	.reset()

	anynil.NormalizeSlice()

	if  == nil {
		return .appendParamsForQueryExecModeExec(, )
	}

	if len(.ParamOIDs) != len() {
		return fmt.Errorf("mismatched param and argument count")
	}

	for  := range  {
		 := .appendParam(, .ParamOIDs[], -1, [])
		if  != nil {
			 = fmt.Errorf("failed to encode args[%d]: %v", , )
			return 
		}
	}

	for  := range .Fields {
		.appendResultFormat(.FormatCodeForOID(.Fields[].DataTypeOID))
	}

	return nil
}

// appendParam appends a parameter to the query. format may be -1 to automatically choose the format. If arg is nil it
// must be an untyped nil.
func ( *ExtendedQueryBuilder) ( *pgtype.Map,  uint32,  int16,  any) error {
	if  == -1 {
		 := .chooseParameterFormatCode(, , )
		 := .(, , , )
		if  == nil {
			return nil
		}

		var  int16
		if  == TextFormatCode {
			 = BinaryFormatCode
		} else {
			 = TextFormatCode
		}

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

		return  // return the error from the preferred format
	}

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

	.ParamFormats = append(.ParamFormats, )
	.ParamValues = append(.ParamValues, )

	return nil
}

// appendResultFormat appends a result format to the query.
func ( *ExtendedQueryBuilder) ( int16) {
	.ResultFormats = append(.ResultFormats, )
}

// reset readies eqb to build another query.
func ( *ExtendedQueryBuilder) () {
	.ParamValues = .ParamValues[0:0]
	.paramValueBytes = .paramValueBytes[0:0]
	.ParamFormats = .ParamFormats[0:0]
	.ResultFormats = .ResultFormats[0:0]

	if cap(.ParamValues) > 64 {
		.ParamValues = make([][]byte, 0, 64)
	}

	if cap(.paramValueBytes) > 256 {
		.paramValueBytes = make([]byte, 0, 256)
	}

	if cap(.ParamFormats) > 64 {
		.ParamFormats = make([]int16, 0, 64)
	}
	if cap(.ResultFormats) > 64 {
		.ResultFormats = make([]int16, 0, 64)
	}
}

func ( *ExtendedQueryBuilder) ( *pgtype.Map,  uint32,  int16,  any) ([]byte, error) {
	if anynil.Is() {
		return nil, nil
	}

	if .paramValueBytes == nil {
		.paramValueBytes = make([]byte, 0, 128)
	}

	 := len(.paramValueBytes)

	,  := .Encode(, , , .paramValueBytes)
	if  != nil {
		return nil, 
	}
	if  == nil {
		return nil, nil
	}
	.paramValueBytes = 
	return .paramValueBytes[:], nil
}

// chooseParameterFormatCode determines the correct format code for an
// argument to a prepared statement. It defaults to TextFormatCode if no
// determination can be made.
func ( *ExtendedQueryBuilder) ( *pgtype.Map,  uint32,  any) int16 {
	switch .(type) {
	case string, *string:
		return TextFormatCode
	}

	return .FormatCodeForOID()
}

// appendParamsForQueryExecModeExec appends the args to eqb.
//
// Parameters must be encoded in the text format because of differences in type conversion between timestamps and
// dates. In QueryExecModeExec we don't know what the actual PostgreSQL type is. To determine the type we use the
// Go type to OID type mapping registered by RegisterDefaultPgType. However, the Go time.Time represents both
// PostgreSQL timestamp[tz] and date. To use the binary format we would need to also specify what the PostgreSQL
// type OID is. But that would mean telling PostgreSQL that we have sent a timestamp[tz] when what is needed is a date.
// This means that the value is converted from text to timestamp[tz] to date. This means it does a time zone conversion
// before converting it to date. This means that dates can be shifted by one day. In text format without that double
// type conversion it takes the date directly and ignores time zone (i.e. it works).
//
// Given that the whole point of QueryExecModeExec is to operate without having to know the PostgreSQL types there is
// no way to safely use binary or to specify the parameter OIDs.
func ( *ExtendedQueryBuilder) ( *pgtype.Map,  []any) error {
	for ,  := range  {
		if  == nil {
			 := .appendParam(, 0, TextFormatCode, )
			if  != nil {
				return 
			}
		} else {
			,  := .TypeForValue()
			if ! {
				var  pgtype.TextValuer
				if ,  = .(pgtype.TextValuer);  {
					,  := .TextValue()
					if  != nil {
						return 
					}

					,  = .TypeForOID(pgtype.TextOID)
					if  {
						 = 
					}
				}
			}
			if ! {
				var  driver.Valuer
				if ,  = .(driver.Valuer);  {
					,  := .Value()
					if  != nil {
						return 
					}
					,  = .TypeForValue()
					if  {
						 = 
					}
				}
			}
			if ! {
				var  fmt.Stringer
				if ,  = .(fmt.Stringer);  {
					,  = .TypeForOID(pgtype.TextOID)
					if  {
						 = .String()
					}
				}
			}
			if ! {
				return &unknownArgumentTypeQueryExecModeExecError{arg: }
			}
			 := .appendParam(, .OID, TextFormatCode, )
			if  != nil {
				return 
			}
		}
	}

	return nil
}