package db

import (
	"fmt"
	"os"
	"path/filepath"
	"server/internal/config"
	"strings"

	"github.com/gofiber/fiber/v3"
	"gorm.io/driver/postgres"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

var DB *gorm.DB

// GetDB returns the global *gorm.DB instance. It panics if the database is not initialized.
func GetDB() (*gorm.DB, error) {
	if DB == nil {
		cfg, err := config.GetConfig()
		if err != nil {
			return nil, err
		}
		DB, err = InitDB(cfg.Db)
		if err != nil {
			fmt.Printf("Failed to initialize database: %v\n", err)
			return nil, err
		}
	}
	return DB, nil
}

// Init opens the configured database connection and runs schema migrations.
func InitDB(cfg config.DbConfig) (*gorm.DB, error) {
	switch cfg.Driver {
	case "sqlite":
		if err := ensureSQLiteDir(cfg.DSN); err != nil {
			return nil, fmt.Errorf("prepare sqlite path: %w", err)
		}
		DB, err := gorm.Open(sqlite.Open(cfg.DSN), &gorm.Config{})
		if err != nil {
			return nil, fmt.Errorf("open sqlite: %w", err)
		}
		return DB, nil
	case "postgres":
		DB, err := gorm.Open(postgres.Open(cfg.DSN), &gorm.Config{})
		if err != nil {
			return nil, fmt.Errorf("open postgres: %w", err)
		}
		return DB, nil
	default:
		return nil, fmt.Errorf("unsupported driver %q", cfg.Driver)
	}
}

func ensureSQLiteDir(dsn string) error {
	pathSpec := dsn
	if idx := strings.Index(pathSpec, "?"); idx >= 0 {
		pathSpec = pathSpec[:idx]
	}
	pathSpec = strings.TrimPrefix(pathSpec, "file:")
	if pathSpec == "" || strings.Contains(pathSpec, ":memory:") {
		return nil
	}
	dir := filepath.Dir(pathSpec)
	if dir == "." || dir == "" {
		return nil
	}
	return os.MkdirAll(dir, 0o755)
}

// dbFromCtx extracts *gorm.DB from Fiber context.
func dbFromCtx(c fiber.Ctx) (*gorm.DB, error) {
	dbVal := c.Locals("db")
	db, ok := dbVal.(*gorm.DB)
	if !ok || db == nil {
		return nil, fiber.NewError(fiber.StatusInternalServerError, "database unavailable")
	}
	return db, nil
}

func DBFromCtx(c fiber.Ctx) (*gorm.DB, error) {
	return dbFromCtx(c)
}
