package bun

import (
	
	
	
	
	
	

	
	
	
)

const (
	discardUnknownColumns internal.Flag = 1 << iota
)

type DBStats struct {
	Queries uint64
	Errors  uint64
}

type DBOption func(db *DB)

func () DBOption {
	return func( *DB) {
		.flags = .flags.Set(discardUnknownColumns)
	}
}

type DB struct {
	*sql.DB
	dialect  schema.Dialect
	features feature.Feature

	queryHooks []QueryHook

	fmter schema.Formatter
	flags internal.Flag

	stats DBStats
}

func ( *sql.DB,  schema.Dialect,  ...DBOption) *DB {
	.Init()

	 := &DB{
		DB:       ,
		dialect:  ,
		features: .Features(),
		fmter:    schema.NewFormatter(),
	}

	for ,  := range  {
		()
	}

	return 
}

func ( *DB) () string {
	var  strings.Builder
	.WriteString("DB<dialect=")
	.WriteString(.dialect.Name().String())
	.WriteString(">")
	return .String()
}

func ( *DB) () DBStats {
	return DBStats{
		Queries: atomic.LoadUint64(&.stats.Queries),
		Errors:  atomic.LoadUint64(&.stats.Errors),
	}
}

func ( *DB) ( interface{}) *ValuesQuery {
	return NewValuesQuery(, )
}

func ( *DB) () *SelectQuery {
	return NewSelectQuery()
}

func ( *DB) () *InsertQuery {
	return NewInsertQuery()
}

func ( *DB) () *UpdateQuery {
	return NewUpdateQuery()
}

func ( *DB) () *DeleteQuery {
	return NewDeleteQuery()
}

func ( *DB) () *CreateTableQuery {
	return NewCreateTableQuery()
}

func ( *DB) () *DropTableQuery {
	return NewDropTableQuery()
}

func ( *DB) () *CreateIndexQuery {
	return NewCreateIndexQuery()
}

func ( *DB) () *DropIndexQuery {
	return NewDropIndexQuery()
}

func ( *DB) () *TruncateTableQuery {
	return NewTruncateTableQuery()
}

func ( *DB) () *AddColumnQuery {
	return NewAddColumnQuery()
}

func ( *DB) () *DropColumnQuery {
	return NewDropColumnQuery()
}

func ( *DB) ( context.Context,  ...interface{}) error {
	for ,  := range  {
		if ,  := .NewDropTable().Model().IfExists().Exec();  != nil {
			return 
		}
		if ,  := .NewCreateTable().Model().Exec();  != nil {
			return 
		}
	}
	return nil
}

func ( *DB) () schema.Dialect {
	return .dialect
}

func ( *DB) ( context.Context,  *sql.Rows,  ...interface{}) error {
	,  := newModel(, )
	if  != nil {
		return 
	}

	_,  = .ScanRows(, )
	return 
}

func ( *DB) ( context.Context,  *sql.Rows,  ...interface{}) error {
	,  := newModel(, )
	if  != nil {
		return 
	}

	,  := .(rowScanner)
	if ! {
		return fmt.Errorf("bun: %T does not support ScanRow", )
	}

	return .ScanRow(, )
}

func ( *DB) ( QueryHook) {
	.queryHooks = append(.queryHooks, )
}

func ( *DB) ( reflect.Type) *schema.Table {
	return .dialect.Tables().Get()
}

func ( *DB) ( ...interface{}) {
	.dialect.Tables().Register(...)
}

func ( *DB) () *DB {
	 := *

	 := len(.queryHooks)
	.queryHooks = .queryHooks[::]

	return &
}

func ( *DB) ( string,  interface{}) *DB {
	 := .clone()
	.fmter = .fmter.WithNamedArg(, )
	return 
}

func ( *DB) () schema.Formatter {
	return .fmter
}

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

func ( *DB) ( string,  ...interface{}) (sql.Result, error) {
	return .ExecContext(context.Background(), , ...)
}

func ( *DB) (
	 context.Context,  string,  ...interface{},
) (sql.Result, error) {
	,  := .beforeQuery(, nil, , )
	,  := .DB.ExecContext(, .format(, ))
	.afterQuery(, , , )
	return , 
}

func ( *DB) ( string,  ...interface{}) (*sql.Rows, error) {
	return .QueryContext(context.Background(), , ...)
}

func ( *DB) (
	 context.Context,  string,  ...interface{},
) (*sql.Rows, error) {
	,  := .beforeQuery(, nil, , )
	,  := .DB.QueryContext(, .format(, ))
	.afterQuery(, , nil, )
	return , 
}

func ( *DB) ( string,  ...interface{}) *sql.Row {
	return .QueryRowContext(context.Background(), , ...)
}

func ( *DB) ( context.Context,  string,  ...interface{}) *sql.Row {
	,  := .beforeQuery(, nil, , )
	 := .DB.QueryRowContext(, .format(, ))
	.afterQuery(, , nil, .Err())
	return 
}

func ( *DB) ( string,  []interface{}) string {
	return .fmter.FormatQuery(, ...)
}

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

type Conn struct {
	db *DB
	*sql.Conn
}

func ( *DB) ( context.Context) (Conn, error) {
	,  := .DB.Conn()
	if  != nil {
		return Conn{}, 
	}
	return Conn{
		db:   ,
		Conn: ,
	}, nil
}

func ( Conn) (
	 context.Context,  string,  ...interface{},
) (sql.Result, error) {
	,  := .db.beforeQuery(, nil, , )
	,  := .Conn.ExecContext(, .db.format(, ))
	.db.afterQuery(, , , )
	return , 
}

func ( Conn) (
	 context.Context,  string,  ...interface{},
) (*sql.Rows, error) {
	,  := .db.beforeQuery(, nil, , )
	,  := .Conn.QueryContext(, .db.format(, ))
	.db.afterQuery(, , nil, )
	return , 
}

func ( Conn) ( context.Context,  string,  ...interface{}) *sql.Row {
	,  := .db.beforeQuery(, nil, , )
	 := .Conn.QueryRowContext(, .db.format(, ))
	.db.afterQuery(, , nil, .Err())
	return 
}

func ( Conn) ( interface{}) *ValuesQuery {
	return NewValuesQuery(.db, ).Conn()
}

func ( Conn) () *SelectQuery {
	return NewSelectQuery(.db).Conn()
}

func ( Conn) () *InsertQuery {
	return NewInsertQuery(.db).Conn()
}

func ( Conn) () *UpdateQuery {
	return NewUpdateQuery(.db).Conn()
}

func ( Conn) () *DeleteQuery {
	return NewDeleteQuery(.db).Conn()
}

func ( Conn) () *CreateTableQuery {
	return NewCreateTableQuery(.db).Conn()
}

func ( Conn) () *DropTableQuery {
	return NewDropTableQuery(.db).Conn()
}

func ( Conn) () *CreateIndexQuery {
	return NewCreateIndexQuery(.db).Conn()
}

func ( Conn) () *DropIndexQuery {
	return NewDropIndexQuery(.db).Conn()
}

func ( Conn) () *TruncateTableQuery {
	return NewTruncateTableQuery(.db).Conn()
}

func ( Conn) () *AddColumnQuery {
	return NewAddColumnQuery(.db).Conn()
}

func ( Conn) () *DropColumnQuery {
	return NewDropColumnQuery(.db).Conn()
}

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

type Stmt struct {
	*sql.Stmt
}

func ( *DB) ( string) (Stmt, error) {
	return .PrepareContext(context.Background(), )
}

func ( *DB) ( context.Context,  string) (Stmt, error) {
	,  := .DB.PrepareContext(, )
	if  != nil {
		return Stmt{}, 
	}
	return Stmt{Stmt: }, nil
}

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

type Tx struct {
	db *DB
	*sql.Tx
}

// RunInTx runs the function in a transaction. If the function returns an error,
// the transaction is rolled back. Otherwise, the transaction is committed.
func ( *DB) (
	 context.Context,  *sql.TxOptions,  func( context.Context,  Tx) error,
) error {
	,  := .BeginTx(, )
	if  != nil {
		return 
	}
	defer .Rollback() //nolint:errcheck

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

func ( *DB) () (Tx, error) {
	return .BeginTx(context.Background(), nil)
}

func ( *DB) ( context.Context,  *sql.TxOptions) (Tx, error) {
	,  := .DB.BeginTx(, )
	if  != nil {
		return Tx{}, 
	}
	return Tx{
		db: ,
		Tx: ,
	}, nil
}

func ( Tx) ( string,  ...interface{}) (sql.Result, error) {
	return .ExecContext(context.TODO(), , ...)
}

func ( Tx) (
	 context.Context,  string,  ...interface{},
) (sql.Result, error) {
	,  := .db.beforeQuery(, nil, , )
	,  := .Tx.ExecContext(, .db.format(, ))
	.db.afterQuery(, , , )
	return , 
}

func ( Tx) ( string,  ...interface{}) (*sql.Rows, error) {
	return .QueryContext(context.TODO(), , ...)
}

func ( Tx) (
	 context.Context,  string,  ...interface{},
) (*sql.Rows, error) {
	,  := .db.beforeQuery(, nil, , )
	,  := .Tx.QueryContext(, .db.format(, ))
	.db.afterQuery(, , nil, )
	return , 
}

func ( Tx) ( string,  ...interface{}) *sql.Row {
	return .QueryRowContext(context.TODO(), , ...)
}

func ( Tx) ( context.Context,  string,  ...interface{}) *sql.Row {
	,  := .db.beforeQuery(, nil, , )
	 := .Tx.QueryRowContext(, .db.format(, ))
	.db.afterQuery(, , nil, .Err())
	return 
}

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

func ( Tx) ( interface{}) *ValuesQuery {
	return NewValuesQuery(.db, ).Conn()
}

func ( Tx) () *SelectQuery {
	return NewSelectQuery(.db).Conn()
}

func ( Tx) () *InsertQuery {
	return NewInsertQuery(.db).Conn()
}

func ( Tx) () *UpdateQuery {
	return NewUpdateQuery(.db).Conn()
}

func ( Tx) () *DeleteQuery {
	return NewDeleteQuery(.db).Conn()
}

func ( Tx) () *CreateTableQuery {
	return NewCreateTableQuery(.db).Conn()
}

func ( Tx) () *DropTableQuery {
	return NewDropTableQuery(.db).Conn()
}

func ( Tx) () *CreateIndexQuery {
	return NewCreateIndexQuery(.db).Conn()
}

func ( Tx) () *DropIndexQuery {
	return NewDropIndexQuery(.db).Conn()
}

func ( Tx) () *TruncateTableQuery {
	return NewTruncateTableQuery(.db).Conn()
}

func ( Tx) () *AddColumnQuery {
	return NewAddColumnQuery(.db).Conn()
}

func ( Tx) () *DropColumnQuery {
	return NewDropColumnQuery(.db).Conn()
}

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

func ( *DB) () []byte {
	// TODO: make this configurable?
	return make([]byte, 0, 4096)
}