package handlers

import (
	"context"
	"fmt"
	"strconv"
	"strings"
	"time"

	"armature/store"
)

// SessionConfig holds parsed, clamped session TTL settings.
type SessionConfig struct {
	AccessTokenTTL  time.Duration
	RefreshTokenTTL time.Duration
	IdleTimeout     time.Duration // 0 = disabled
}

// Defaults & clamp bounds
const (
	defaultAccessTTL  = 15 * time.Minute
	defaultRefreshTTL = 7 * 24 * time.Hour // 7d
	defaultIdleTimeout = 0                  // disabled

	minAccessTTL  = 5 * time.Minute
	maxAccessTTL  = 60 * time.Minute
	minRefreshTTL = 1 * time.Hour
	maxRefreshTTL = 90 * 24 * time.Hour // 90d
	minIdleTTL    = 5 * time.Minute
	maxIdleTTL    = 24 * time.Hour
)

// LoadSessionConfig reads session settings from the global_settings table.
// Returns defaults if the key is missing or values are unparseable.
func LoadSessionConfig(ctx context.Context, gc store.GlobalConfigStore) SessionConfig {
	sc := SessionConfig{
		AccessTokenTTL:  defaultAccessTTL,
		RefreshTokenTTL: defaultRefreshTTL,
		IdleTimeout:     defaultIdleTimeout,
	}

	raw, err := gc.Get(ctx, "session")
	if err != nil || raw == nil {
		return sc
	}

	if v, ok := raw["access_token_ttl"].(string); ok && v != "" {
		if d, err := parseDurationString(v); err == nil {
			sc.AccessTokenTTL = clamp(d, minAccessTTL, maxAccessTTL)
		}
	}

	if v, ok := raw["refresh_token_ttl"].(string); ok && v != "" {
		if d, err := parseDurationString(v); err == nil {
			sc.RefreshTokenTTL = clamp(d, minRefreshTTL, maxRefreshTTL)
		}
	}

	if v, ok := raw["idle_timeout"].(string); ok && v != "" {
		if d, err := parseDurationString(v); err == nil {
			sc.IdleTimeout = clamp(d, minIdleTTL, maxIdleTTL)
		}
	}

	return sc
}

// parseDurationString parses strings like "15m", "2h", "7d", "30d".
// Supports suffixes: s (seconds), m (minutes), h (hours), d (days).
func parseDurationString(s string) (time.Duration, error) {
	s = strings.TrimSpace(s)
	if s == "" {
		return 0, fmt.Errorf("empty duration")
	}

	// Try Go's standard parser first (handles "15m", "2h30m", etc.)
	if d, err := time.ParseDuration(s); err == nil {
		return d, nil
	}

	// Handle "Xd" suffix (days)
	if strings.HasSuffix(s, "d") {
		numStr := strings.TrimSuffix(s, "d")
		n, err := strconv.ParseFloat(numStr, 64)
		if err != nil {
			return 0, fmt.Errorf("invalid duration: %s", s)
		}
		return time.Duration(n * float64(24*time.Hour)), nil
	}

	return 0, fmt.Errorf("invalid duration: %s", s)
}

func clamp(d, min, max time.Duration) time.Duration {
	if d < min {
		return min
	}
	if d > max {
		return max
	}
	return d
}
