package sql

// |@@| C

import (
	"context"
	"database/sql"
	"fmt"
	"gardening/src/lib"
	"gardening/src/lib/config"
	"strconv"
	"strings"
)

type PgSqlIpl struct {
	Config *config.Config
}

func (this *PgSqlIpl) Kind() string {
	return "pg"
}

func (this *PgSqlIpl) Driver() string {
	return DRIVER_POSTGRES
}

func (this *PgSqlIpl) DataSourceName(ctx context.Context, rootDir string) string {
	return this.Config.MustGetEnvValue(ctx, "PG_DB_CONNECTION_STRING")
}

func (this *PgSqlIpl) SchemaPath(ctx context.Context, rootDir string) string {
	return rootDir + "/data/schema.pg.sql"
}

func (this *PgSqlIpl) IsInMemory(ctx context.Context) bool {
	return false
}

func (this *PgSqlIpl) SetMaxOpenConns(db *sql.DB, n int) {
	db.SetMaxOpenConns(n)
}

func (this *PgSqlIpl) Exec(db *sql.DB, query string, args ...any) (sql.Result, error) {
	query = this.ConvertQueryToPostgresPlaceholders(query)
	return db.Exec(query, args...)
}

func (this *PgSqlIpl) BeginTx(ctx context.Context, db *sql.DB, opts *sql.TxOptions) (*sql.Tx, error) {
	return db.BeginTx(ctx, opts)
}

func (this *PgSqlIpl) Open(dataSourceName string) (*sql.DB, error) {
	db, err := sql.Open("postgres", dataSourceName)
	if err != nil {
		return nil, err
	}
	return db, nil
}

func (this *PgSqlIpl) Query(db *sql.DB, query string, args ...any) (*sql.Rows, error) {
	query = this.ConvertQueryToPostgresPlaceholders(query)
	return db.Query(query, args...)
}

func (this *PgSqlIpl) QueryRow(db *sql.DB, query string, args ...any) *sql.Row {
	query = this.ConvertQueryToPostgresPlaceholders(query)
	return db.QueryRow(query, args...)
}

func (this *PgSqlIpl) Prepare(db *sql.DB, query string) (*sql.Stmt, error) {
	query = this.ConvertQueryToPostgresPlaceholders(query)
	return db.Prepare(query)
}

func (this *PgSqlIpl) Create(ctx context.Context, dataSourceName string) {
	db, err := this.Open("postgres://user:password@db/postgres?sslmode=disable")
	lib.Poe(err)

	defer db.Close()

	dbName := this.getDbName(ctx)
	_, err = this.Exec(db, "CREATE DATABASE "+dbName)
	lib.Poe(err)
}

func (this *PgSqlIpl) getDbName(ctx context.Context) string {
	return this.Config.MustGetEnvValue(ctx, "DB_NAME")
}

func (this *PgSqlIpl) Destroy(ctx context.Context, dataSourceName string) {
	db, err := this.Open("postgres://user:password@db/postgres?sslmode=disable")
	lib.Poe(err)

	defer db.Close()

	dbName := this.getDbName(ctx)
	_, err = db.Exec("DROP DATABASE IF EXISTS " + dbName)
	lib.Poe(err)
}

func (this *PgSqlIpl) Placeholder(n int) string {
	return "$" + strconv.Itoa(n)
}

func (this *PgSqlIpl) ConvertQueryToPostgresPlaceholders(query string) string {
	var builder strings.Builder
	placeholderCount := 1

	for i := 0; i < len(query); i++ {
		char := query[i]

		// Untested code but cases like ? into values like "blabla?blabla" need to be taken into account
		//
		//if char == '\'' {
		//	builder.WriteByte(char)
		//	inSingleQuote = !inSingleQuote
		//
		//	// Check for escaped single quote (e.g. 'It''s')
		//	if inSingleQuote && i+1 < len(query) && query[i+1] == '\'' {
		//		builder.WriteByte(query[i+1])
		//		i++
		//		inSingleQuote = !inSingleQuote // skip the escape effect
		//	}
		//	continue
		//}

		if char == '?' {
			builder.WriteString(fmt.Sprintf("$%d", placeholderCount))
			placeholderCount++
		} else {
			builder.WriteByte(char)
		}
	}

	return builder.String()
}
