package serverruntime

import (
	"context"
	"database/sql"
	"fmt"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/nexuscrm/db"
)

type namedDBPoolConfig struct {
	PoolOptions database.PoolOptions
	PingTimeout time.Duration
}

type namedDBPoolRole struct {
	PoolName              string
	Label                 string
	MaxOpenEnv            string
	DefaultMaxOpen        int
	PingTimeoutEnv        string
	DefaultPingTimeout    time.Duration
	ConnectFailureMessage string
	EstablishedMessage    string
}

func (r namedDBPoolRole) configFromEnv(getenv func(string) string) namedDBPoolConfig {
	return namedDBPoolConfigFromEnv(
		getenv,
		r.MaxOpenEnv,
		r.DefaultMaxOpen,
		r.PingTimeoutEnv,
		r.DefaultPingTimeout,
	)
}

func (r namedDBPoolRole) open(ctx context.Context, cfg namedDBPoolConfig) (*sql.DB, error) {
	return openNamedDBPool(ctx, r.Label, cfg)
}

func namedDBPoolConfigFromEnv(getenv func(string) string, maxOpenEnv string, defaultMaxOpen int, pingTimeoutEnv string, defaultPingTimeout time.Duration) namedDBPoolConfig {
	if getenv == nil {
		getenv = os.Getenv
	}
	maxOpen := parsePositiveIntEnv(getenv, maxOpenEnv, defaultMaxOpen)
	return namedDBPoolConfig{
		PoolOptions: database.PoolOptions{
			MaxOpenConns:    maxOpen,
			MaxIdleConns:    maxOpen,
			ConnMaxLifetime: 5 * time.Minute,
			ConnMaxIdleTime: 3 * time.Minute,
		},
		PingTimeout: parsePositiveDurationEnv(getenv, pingTimeoutEnv, defaultPingTimeout),
	}
}

func openNamedDBPool(ctx context.Context, label string, cfg namedDBPoolConfig) (*sql.DB, error) {
	pool, err := database.OpenDBFromEnv(cfg.PoolOptions)
	if err != nil {
		return nil, err
	}
	pingCtx := ctx
	cancel := func() {}
	if cfg.PingTimeout > 0 {
		pingCtx, cancel = context.WithTimeout(ctx, cfg.PingTimeout)
	}
	defer cancel()
	if err := pool.PingContext(pingCtx); err != nil {
		_ = pool.Close()
		return nil, fmt.Errorf("ping %s database pool: %w", label, err)
	}
	return pool, nil
}

func parsePositiveIntEnv(getenv func(string) string, name string, fallback int) int {
	value := strings.TrimSpace(getenv(name))
	if value == "" {
		return fallback
	}
	parsed, err := strconv.Atoi(value)
	if err != nil || parsed <= 0 {
		return fallback
	}
	return parsed
}

func parsePositiveDurationEnv(getenv func(string) string, name string, fallback time.Duration) time.Duration {
	value := strings.TrimSpace(getenv(name))
	if value == "" {
		return fallback
	}
	parsed, err := time.ParseDuration(value)
	if err == nil && parsed > 0 {
		return parsed
	}
	millis, intErr := strconv.Atoi(value)
	if intErr == nil && millis > 0 {
		return time.Duration(millis) * time.Millisecond
	}
	return fallback
}
