package db

import (
	"eslogad-be/config"
	"fmt"
	"io/fs"
	"os"
	"path/filepath"
	"sort"

	_ "github.com/lib/pq"
	"go.uber.org/zap"
	_ "gopkg.in/yaml.v3"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)

func NewPostgres(c config.Database) (*gorm.DB, error) {
	dialector := postgres.New(postgres.Config{
		DSN: c.DSN(),
	})

	db, err := gorm.Open(dialector, &gorm.Config{})

	if err != nil {
		return nil, err
	}

	zapCfg := zap.NewProductionConfig()
	zapCfg.Level = zap.NewAtomicLevelAt(zap.ErrorLevel)
	zapCfg.DisableCaller = false

	sqlDB, err := db.DB()
	if err != nil {
		return nil, err
	}
	if err := sqlDB.Ping(); err != nil {
		return nil, err
	}

	sqlDB.SetMaxIdleConns(c.MaxIdleConnectionsInSecond)
	sqlDB.SetMaxOpenConns(c.MaxOpenConnectionsInSecond)
	sqlDB.SetConnMaxLifetime(c.ConnectionMaxLifetime())

	fmt.Println("Successfully connected to PostgreSQL database")

	return db, nil
}

func runMigrations(sqlDB *gorm.DB) error {
	// use the underlying *sql.DB for Exec
	db := sqlDB
	sqlConn, err := db.DB()
	if err != nil {
		return err
	}

	migrationsDir := "migrations"
	entries := []string{}
	if err := filepath.WalkDir(migrationsDir, func(path string, d fs.DirEntry, walkErr error) error {
		if walkErr != nil {
			return walkErr
		}
		if d.IsDir() {
			return nil
		}
		if filepath.Ext(d.Name()) == ".sql" {
			entries = append(entries, path)
		}
		return nil
	}); err != nil && !os.IsNotExist(err) {
		return err
	}

	// sort by name to ensure order
	sort.Strings(entries)

	for _, file := range entries {
		contents, err := os.ReadFile(file)
		if err != nil {
			return fmt.Errorf("read migration %s: %w", file, err)
		}
		if _, err := sqlConn.Exec(string(contents)); err != nil {
			return fmt.Errorf("exec migration %s: %w", file, err)
		}
		fmt.Printf("Applied migration: %s\n", filepath.Base(file))
	}
	return nil
}
