package sqlite3

import (
	
	
	
	
	
	
	
	
	

	
	
	
)

// Conn is a database connection handle.
// A Conn is not safe for concurrent use by multiple goroutines.
//
// https://sqlite.org/c3ref/sqlite3.html
type Conn struct {
	wrp *sqlite3_wrap.Wrapper

	interrupt  context.Context
	stmts      []*Stmt
	busy       func(context.Context, int) bool
	log        func(xErrorCode, string)
	collation  func(*Conn, string)
	wal        func(*Conn, string, int) error
	trace      func(TraceEvent, any, any) error
	authorizer func(AuthorizerActionCode, string, string, string, string) AuthorizerReturnCode
	update     func(AuthorizerActionCode, string, string, int64)
	commit     func() bool
	rollback   func()

	busy1st time.Time
	busylst time.Time
	arena   sqlite3_wrap.Arena
	handle  ptr_t
	gosched uint8
}

// Open calls [OpenFlags] with [OPEN_READWRITE], [OPEN_CREATE] and [OPEN_URI].
func ( string) (*Conn, error) {
	return newConn(context.Background(), , OPEN_READWRITE|OPEN_CREATE|OPEN_URI)
}

// OpenContext is like [Open] but includes a context,
// which is used to interrupt the process of opening the connection.
func ( context.Context,  string) (*Conn, error) {
	return newConn(, , OPEN_READWRITE|OPEN_CREATE|OPEN_URI)
}

// OpenFlags opens an SQLite database file as specified by the filename argument.
//
// If none of the required flags are used, a combination of [OPEN_READWRITE] and [OPEN_CREATE] is used.
// If a URI filename is used, PRAGMA statements to execute can be specified using "_pragma":
//
//	sqlite3.Open("file:demo.db?_pragma=busy_timeout(10000)")
//
// https://sqlite.org/c3ref/open.html
func ( string,  OpenFlag) (*Conn, error) {
	if &(OPEN_READONLY|OPEN_READWRITE|OPEN_CREATE) == 0 {
		 |= OPEN_READWRITE | OPEN_CREATE
	}
	return newConn(context.Background(), , )
}

func newConn( context.Context,  string,  OpenFlag) ( *Conn,  error) {
	 := .Err()
	if  != nil {
		return nil, 
	}

	 := &Conn{interrupt: }
	.wrp,  = createWrapper()
	if  != nil {
		return nil, 
	}
	defer func() {
		if  == nil {
			.Close()
		} else {
			.interrupt = context.Background()
		}
	}()

	.wrp.DB = 
	if  := defaultLogger.Load();  != nil {
		.ConfigLog(*)
	}
	.arena = .wrp.NewArena()
	.handle,  = .openDB(, )
	if  == nil {
		 = initExtensions()
	}
	if  != nil {
		return nil, 
	}
	return , nil
}

func ( *Conn) ( string,  OpenFlag) (ptr_t, error) {
	defer .arena.Mark()()
	 := .arena.New(ptrlen)
	 := .arena.String()

	 |= OPEN_EXRESCODE
	 := res_t(.wrp.Xsqlite3_open_v2(int32(), int32(), int32(), 0))

	 := ptr_t(.wrp.Read32())
	if  := .errorFor(, );  != nil {
		.closeDB()
		return 0, 
	}

	.wrp.Xsqlite3_progress_handler_go(int32(), 1000)
	if |OPEN_URI != 0 && strings.HasPrefix(, "file:") {
		var  strings.Builder
		if , ,  := strings.Cut(, "?");  {
			,  := url.ParseQuery()
			for ,  := range ["_pragma"] {
				.WriteString(`PRAGMA `)
				.WriteString()
				.WriteString(`;`)
			}
		}
		if .Len() != 0 {
			 := .arena.String(.String())
			 := res_t(.wrp.Xsqlite3_exec(int32(), int32(), 0, 0, 0))
			if  := .errorFor(, , .String());  != nil {
				 = fmt.Errorf("sqlite3: invalid _pragma: %w", )
				.closeDB()
				return 0, 
			}
		}
	}
	return , nil
}

func ( *Conn) ( ptr_t) {
	 := res_t(.wrp.Xsqlite3_close_v2(int32()))
	if  := .errorFor(, );  != nil {
		panic()
	}
}

// Close closes the database connection.
//
// If the database connection is associated with unfinalized prepared statements,
// open blob handles, and/or unfinished backup objects,
// Close will leave the database connection open and return [BUSY].
//
// It is safe to close a nil, zero or closed Conn.
//
// https://sqlite.org/c3ref/close.html
func ( *Conn) () error {
	if  == nil || .handle == 0 {
		return nil
	}

	 := res_t(.wrp.Xsqlite3_close(int32(.handle)))
	if  := .error();  != nil {
		return 
	}

	.handle = 0
	return .wrp.Close()
}

// Exec is a convenience function that allows an application to run
// multiple statements of SQL without having to use a lot of code.
//
// https://sqlite.org/c3ref/exec.html
func ( *Conn) ( string) error {
	if .interrupt.Err() != nil {
		return INTERRUPT
	}
	return .exec()
}

func ( *Conn) ( string) error {
	defer .arena.Mark()()
	 := .arena.String()
	 := res_t(.wrp.Xsqlite3_exec(int32(.handle), int32(), 0, 0, 0))
	return .error(, )
}

// Prepare calls [Conn.PrepareFlags] with no flags.
func ( *Conn) ( string) ( *Stmt,  string,  error) {
	return .PrepareFlags(, 0)
}

// PrepareFlags compiles the first SQL statement in sql;
// tail is left pointing to what remains uncompiled.
// If the input text contains no SQL (if the input is an empty string or a comment),
// both stmt and err will be nil.
//
// https://sqlite.org/c3ref/prepare.html
func ( *Conn) ( string,  PrepareFlag) ( *Stmt,  string,  error) {
	if len() > _MAX_SQL_LENGTH {
		return nil, "", TOOBIG
	}
	if .interrupt.Err() != nil {
		return nil, "", INTERRUPT
	}

	defer .arena.Mark()()
	 := .arena.New(ptrlen)
	 := .arena.New(ptrlen)
	 := .arena.String()

	 := res_t(.wrp.Xsqlite3_prepare_v3(int32(.handle),
		int32(), int32(len()+1), int32(),
		int32(), int32()))

	 = &Stmt{c: , sql: }
	.handle = ptr_t(.wrp.Read32())
	if  := [ptr_t(.wrp.Read32())-:];  != "" {
		 = 
	}

	if  := .error(, );  != nil {
		return nil, "", 
	}
	if .handle == 0 {
		return nil, "", nil
	}
	.stmts = append(.stmts, )
	return , , nil
}

// DBName returns the schema name for n-th database on the database connection.
//
// https://sqlite.org/c3ref/db_name.html
func ( *Conn) ( int) string {
	 := ptr_t(.wrp.Xsqlite3_db_name(int32(.handle), int32()))
	if  == 0 {
		return ""
	}
	return .wrp.ReadString(, _MAX_NAME)
}

// Filename returns the filename for a database.
//
// https://sqlite.org/c3ref/db_filename.html
func ( *Conn) ( string) *vfs.Filename {
	var  ptr_t
	if  != "" {
		defer .arena.Mark()()
		 = .arena.String()
	}
	 = ptr_t(.wrp.Xsqlite3_db_filename(int32(.handle), int32()))
	return vfs.GetFilename(.wrp, , vfs.OPEN_MAIN_DB)
}

// ReadOnly determines if a database is read-only.
//
// https://sqlite.org/c3ref/db_readonly.html
func ( *Conn) ( string) ( bool,  bool) {
	var  ptr_t
	if  != "" {
		defer .arena.Mark()()
		 = .arena.String()
	}
	 := .wrp.Xsqlite3_db_readonly(int32(.handle), int32())
	return  > 0,  < 0
}

// GetAutocommit tests the connection for auto-commit mode.
//
// https://sqlite.org/c3ref/get_autocommit.html
func ( *Conn) () bool {
	 := .wrp.Xsqlite3_get_autocommit(int32(.handle))
	return  != 0
}

// LastInsertRowID returns the rowid of the most recent successful INSERT
// on the database connection.
//
// https://sqlite.org/c3ref/last_insert_rowid.html
func ( *Conn) () int64 {
	return .wrp.Xsqlite3_last_insert_rowid(int32(.handle))
}

// SetLastInsertRowID allows the application to set the value returned by
// [Conn.LastInsertRowID].
//
// https://sqlite.org/c3ref/set_last_insert_rowid.html
func ( *Conn) ( int64) {
	.wrp.Xsqlite3_set_last_insert_rowid(int32(.handle), )
}

// Changes returns the number of rows modified, inserted or deleted
// by the most recently completed INSERT, UPDATE or DELETE statement
// on the database connection.
//
// https://sqlite.org/c3ref/changes.html
func ( *Conn) () int64 {
	return .wrp.Xsqlite3_changes64(int32(.handle))
}

// TotalChanges returns the number of rows modified, inserted or deleted
// by all INSERT, UPDATE or DELETE statements completed
// since the database connection was opened.
//
// https://sqlite.org/c3ref/total_changes.html
func ( *Conn) () int64 {
	return .wrp.Xsqlite3_total_changes64(int32(.handle))
}

// ReleaseMemory frees memory used by a database connection.
//
// https://sqlite.org/c3ref/db_release_memory.html
func ( *Conn) () error {
	 := res_t(.wrp.Xsqlite3_db_release_memory(int32(.handle)))
	return .error()
}

// GetInterrupt gets the context set with [Conn.SetInterrupt].
func ( *Conn) () context.Context {
	return .interrupt
}

// SetInterrupt interrupts a long-running query when a context is done.
//
// Subsequent uses of the connection will return [INTERRUPT]
// until the context is reset by another call to SetInterrupt.
//
// To associate a timeout with a connection:
//
//	ctx, cancel := context.WithTimeout(context.TODO(), 100*time.Millisecond)
//	conn.SetInterrupt(ctx)
//	defer cancel()
//
// SetInterrupt returns the old context assigned to the connection.
//
// https://sqlite.org/c3ref/interrupt.html
func ( *Conn) ( context.Context) ( context.Context) {
	if  == nil {
		panic("nil Context")
	}
	 = .interrupt
	.interrupt = 
	return 
}

func ( *env) ( int32) ( int32) {
	if ,  := .DB.(*Conn);  {
		if .gosched++; .gosched%16 == 0 {
			runtime.Gosched()
		}
		if .interrupt.Err() != nil {
			 = 1
		}
	}
	return 
}

// BusyTimeout sets a busy timeout.
//
// https://sqlite.org/c3ref/busy_timeout.html
func ( *Conn) ( time.Duration) error {
	 := min((+time.Millisecond-1)/time.Millisecond, math.MaxInt32)
	 := res_t(.wrp.Xsqlite3_busy_timeout(int32(.handle), int32()))
	return .error()
}

func ( *env) (,  int32) ( int32) {
	// https://fractaledmind.github.io/2024/04/15/sqlite-on-rails-the-how-and-why-of-optimal-performance/
	if ,  := .DB.(*Conn);  && .interrupt.Err() == nil {
		switch {
		case  == 0:
			.busy1st = time.Now()
		case time.Since(.busy1st) >= time.Duration()*time.Millisecond:
			return 0
		}
		if time.Since(.busylst) < time.Millisecond {
			const  = 2*1024*1024 - 1 // power of two, ~2ms
			time.Sleep(time.Duration(rand.Int63() & ))
		}
		.busylst = time.Now()
		return 1
	}
	return 0
}

// BusyHandler registers a callback to handle [BUSY] errors.
//
// https://sqlite.org/c3ref/busy_handler.html
func ( *Conn) ( func( context.Context,  int) ( bool)) error {
	var  int32
	if  != nil {
		 = 1
	}
	 := res_t(.wrp.Xsqlite3_busy_handler_go(int32(.handle), ))
	if  := .error();  != nil {
		return 
	}
	.busy = 
	return nil
}

func ( *env) (,  int32) ( int32) {
	if ,  := .DB.(*Conn);  && .handle == ptr_t() && .busy != nil {
		if  := .interrupt; .Err() == nil &&
			.busy(, int()) {
			 = 1
		}
	}
	return 
}

// Status retrieves runtime status information about a database connection.
//
// https://sqlite.org/c3ref/db_status.html
func ( *Conn) ( DBStatus,  bool) (,  int64,  error) {
	defer .arena.Mark()()
	 := .arena.New(8)
	 := .arena.New(8)

	var  int32
	if  {
		 = 1
	}

	 := res_t(.wrp.Xsqlite3_db_status64(int32(.handle),
		int32(), int32(), int32(), ))
	if  = .error();  == nil {
		 = int64(.wrp.Read64())
		 = int64(.wrp.Read64())
	}
	return
}

// TableColumnMetadata extracts metadata about a column of a table.
//
// https://sqlite.org/c3ref/table_column_metadata.html
func ( *Conn) (, ,  string) (,  string, , ,  bool,  error) {
	defer .arena.Mark()()
	var (
		   ptr_t
		    ptr_t
		    ptr_t
		 ptr_t
		    ptr_t
		     ptr_t
		     ptr_t
	)
	if  != "" {
		 = .arena.New(ptrlen)
		 = .arena.New(ptrlen)
		 = .arena.New(ptrlen)
		 = .arena.New(ptrlen)
		 = .arena.New(ptrlen)
		 = .arena.String()
	}
	if  != "" {
		 = .arena.String()
	}
	 := .arena.String()

	 := res_t(.wrp.Xsqlite3_table_column_metadata(int32(.handle),
		int32(), int32(), int32(),
		int32(), int32(),
		int32(), int32(), int32()))
	if  = .error();  == nil &&  != "" {
		if  := ptr_t(.wrp.Read32());  != 0 {
			 = .wrp.ReadString(, _MAX_NAME)
		}
		if  := ptr_t(.wrp.Read32());  != 0 {
			 = .wrp.ReadString(, _MAX_NAME)
		}
		 = .wrp.ReadBool()
		 = .wrp.ReadBool()
		 = .wrp.ReadBool()
	}
	return
}

func ( *Conn) ( res_t,  ...string) error {
	return .errorFor(.handle, , ...)
}

func ( *Conn) ( ptr_t,  res_t,  ...string) error {
	if  == _OK {
		return nil
	}

	if ErrorCode() == NOMEM || xErrorCode() == IOERR_NOMEM {
		panic(errutil.OOMErr)
	}

	var ,  string
	if  != 0 {
		if  := ptr_t(.wrp.Xsqlite3_errmsg(int32()));  != 0 {
			 = .wrp.ReadString(, _MAX_LENGTH)
			 = strings.TrimPrefix(, "sqlite3: ")
			 = strings.TrimPrefix(, sqlite3_wrap.ErrorCodeString()[len("sqlite3: "):])
			 = strings.TrimPrefix(, ": ")
			if  == "" ||  == "not an error" {
				 = ""
			}
		}

		if len() != 0 {
			if  := int32(.wrp.Xsqlite3_error_offset(int32()));  != -1 {
				 = [0][:]
			}
		}
	}

	var  error
	switch ErrorCode() {
	case CANTOPEN, IOERR:
		 = .wrp.SysError
	}

	if  != nil ||  != "" ||  != "" {
		return &Error{code: , sys: , msg: , sql: }
	}
	return xErrorCode()
}

// Stmts returns an iterator for the prepared statements
// associated with the database connection.
//
// https://sqlite.org/c3ref/next_stmt.html
func ( *Conn) () iter.Seq[*Stmt] {
	return func( func(*Stmt) bool) {
		for ,  := range .stmts {
			if !() {
				break
			}
		}
	}
}