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 {
	*sqlite

	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   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(), , )
}

type connKey = util.ConnKey

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

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

	.ctx = context.WithValue(.ctx, connKey{}, )
	if  := defaultLogger.Load();  != nil {
		.ConfigLog(*)
	}
	.arena = .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(.call("sqlite3_open_v2", stk_t(), stk_t(), stk_t(), 0))

	 := util.Read32[ptr_t](.mod, )
	if  := .sqlite.error(, );  != nil {
		.closeDB()
		return 0, 
	}

	.call("sqlite3_progress_handler_go", stk_t(), 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(.call("sqlite3_exec", stk_t(), stk_t(), 0, 0, 0))
			if  := .sqlite.error(, , .String());  != nil {
				 = fmt.Errorf("sqlite3: invalid _pragma: %w", )
				.closeDB()
				return 0, 
			}
		}
	}
	return , nil
}

func ( *Conn) ( ptr_t) {
	 := res_t(.call("sqlite3_close_v2", stk_t()))
	if  := .sqlite.error(, );  != 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(.call("sqlite3_close", stk_t(.handle)))
	if  := .error();  != nil {
		return 
	}

	.handle = 0
	return .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(.call("sqlite3_exec", stk_t(.handle), stk_t(), 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(.call("sqlite3_prepare_v3", stk_t(.handle),
		stk_t(), stk_t(len()+1), stk_t(),
		stk_t(), stk_t()))

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

	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(.call("sqlite3_db_name", stk_t(.handle), stk_t()))
	if  == 0 {
		return ""
	}
	return util.ReadString(.mod, , _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(.call("sqlite3_db_filename", stk_t(.handle), stk_t()))
	return vfs.GetFilename(.ctx, .mod, , 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()
	}
	 := int32(.call("sqlite3_db_readonly", stk_t(.handle), stk_t()))
	return  > 0,  < 0
}

// GetAutocommit tests the connection for auto-commit mode.
//
// https://sqlite.org/c3ref/get_autocommit.html
func ( *Conn) () bool {
	 := int32(.call("sqlite3_get_autocommit", stk_t(.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 int64(.call("sqlite3_last_insert_rowid", stk_t(.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) {
	.call("sqlite3_set_last_insert_rowid", stk_t(.handle), stk_t())
}

// 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 int64(.call("sqlite3_changes64", stk_t(.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 int64(.call("sqlite3_total_changes64", stk_t(.handle)))
}

// ReleaseMemory frees memory used by a database connection.
//
// https://sqlite.org/c3ref/db_release_memory.html
func ( *Conn) () error {
	 := res_t(.call("sqlite3_db_release_memory", stk_t(.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 progressCallback( context.Context,  api.Module,  ptr_t) ( int32) {
	if ,  := .Value(connKey{}).(*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(.call("sqlite3_busy_timeout", stk_t(.handle), stk_t()))
	return .error()
}

func timeoutCallback( context.Context,  api.Module, ,  int32) ( int32) {
	// https://fractaledmind.github.io/2024/04/15/sqlite-on-rails-the-how-and-why-of-optimal-performance/
	if ,  := .Value(connKey{}).(*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(.call("sqlite3_busy_handler_go", stk_t(.handle), stk_t()))
	if  := .error();  != nil {
		return 
	}
	.busy = 
	return nil
}

func busyCallback( context.Context,  api.Module,  ptr_t,  int32) ( int32) {
	if ,  := .Value(connKey{}).(*Conn);  && .handle ==  && .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) (,  int,  error) {
	defer .arena.mark()()
	 := .arena.new(intlen)
	 := .arena.new(intlen)

	var  int32
	if  {
		 = 1
	}

	 := res_t(.call("sqlite3_db_status", stk_t(.handle),
		stk_t(), stk_t(), stk_t(), stk_t()))
	if  = .error();  == nil {
		 = int(util.Read32[int32](.mod, ))
		 = int(util.Read32[int32](.mod, ))
	}
	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(.call("sqlite3_table_column_metadata", stk_t(.handle),
		stk_t(), stk_t(), stk_t(),
		stk_t(), stk_t(),
		stk_t(), stk_t(), stk_t()))
	if  = .error();  == nil &&  != "" {
		if  := util.Read32[ptr_t](.mod, );  != 0 {
			 = util.ReadString(.mod, , _MAX_NAME)
		}
		if  := util.Read32[ptr_t](.mod, );  != 0 {
			 = util.ReadString(.mod, , _MAX_NAME)
		}
		 = util.ReadBool(.mod, )
		 = util.ReadBool(.mod, )
		 = util.ReadBool(.mod, )
	}
	return
}

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

// 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
			}
		}
	}
}