package postgres

import (
	"context"
	"database/sql"
	"fmt"
	"sync"
	"time"

	querier "git.gocasts.ir/ebhomengo/niki/pkg/query_transaction/sql"
	_ "github.com/jackc/pgx/v5/stdlib"
)
 
type Config struct {
	Host             string `koanf:"host"`
	Port             int    `koanf:"port"`
	User             string `koanf:"user"`
	Password         string `koanf:"password"`
	DbName           string `koanf:"dbName"`
	SSLMode          string `koanf:"sslMode"`
	MaxIdleConn      int    `koanf:"maxIdleConns"`
	MaxOpenConn      int    `koanf:"maxOpenConns"`
	ConnMaxLifetime  int    `koanf:"connMaxLifetime"`
	PathOfMigrations string `koanf:"pathOfMigrations"`
}

type DB struct {
	config     Config
	db         *querier.SQLDB
	mu         sync.Mutex
	statements map[statementKey]*sql.Stmt
}

func (db *DB) Conn() *querier.SQLDB {
	return db.db
}

func New(config Config) *DB {
	dsn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s",
		config.Host, config.Port, config.User, config.Password, config.DbName, config.SSLMode)

	db, err := sql.Open("pgx", dsn)
	if err != nil {
		panic(fmt.Errorf("can't open postgres db: %w", err))
	}

	maxIdle := config.MaxIdleConn

	maxOpen := config.MaxOpenConn

	lifetime := time.Duration(config.ConnMaxLifetime) * time.Second

	db.SetMaxIdleConns(maxIdle)
	db.SetMaxOpenConns(maxOpen)
	db.SetConnMaxLifetime(lifetime)

	return &DB{
		config:     config,
		db:         &querier.SQLDB{DB: db},
		statements: make(map[statementKey]*sql.Stmt),
	}
}

func (db *DB) PrepareStatement(ctx context.Context, key statementKey, query string) (*sql.Stmt, error) {
	db.mu.Lock()
	defer db.mu.Unlock()

	if stmt, ok := db.statements[key]; ok {
		return stmt, nil
	}

	stmt, err := db.db.PrepareContext(ctx, query)
	if err != nil {
		return nil, fmt.Errorf("prepare statement %q: %w", key, err)
	}
	db.statements[key] = stmt

	return stmt, nil
}

func (db *DB) CloseStatements() error {
	db.mu.Lock()
	defer db.mu.Unlock()

	var lastErr error
	for key, stmt := range db.statements {
		if err := stmt.Close(); err != nil {
			lastErr = err
		}
		delete(db.statements, key)
	}
	return lastErr
}

func (db *DB) Close() error {
	return db.db.DB.Close()
}
