package redisserver

import (
	"strconv"
	"sync"
	"time"
)

// entry represents a stored value. For strings, value is stored as a string.
// For hashes, value is stored as a map[string]string.
type entry struct {
	value      interface{}
	expiration time.Time // zero means no expiration
}

// Server holds the in-memory datastore and provides thread-safe access.
// It implements a Redis-compatible server using redcon.
type Server struct {
	mu   sync.RWMutex
	data map[string]*entry
}

type ServerConfig struct {
	TCPPort        int
	UnixSocketPath string
}

// NewCustomServer creates a new server instance with custom TCP port and Unix socket path.
// It starts a cleanup goroutine and Redis-compatible servers on the specified addresses.
func NewServer(config ServerConfig) *Server {

	if config.UnixSocketPath == "" {
		config.UnixSocketPath = "/tmp/redis.sock"
	}

	s := &Server{
		data: make(map[string]*entry),
	}
	go s.cleanupExpiredKeys()

	// Start TCP server if port is provided
	if config.TCPPort != 0 {
		tcpAddr := ":" + strconv.Itoa(config.TCPPort)
		go s.startRedisServer(tcpAddr, "")
	}

	// Start Unix socket server if path is provided
	if config.UnixSocketPath != "" {
		go s.startRedisServer(config.UnixSocketPath, "unix")
	}

	return s
}

// cleanupExpiredKeys periodically removes expired keys.
func (s *Server) cleanupExpiredKeys() {
	ticker := time.NewTicker(1 * time.Second)
	defer ticker.Stop()
	for range ticker.C {
		now := time.Now()
		s.mu.Lock()
		for k, ent := range s.data {
			if !ent.expiration.IsZero() && now.After(ent.expiration) {
				delete(s.data, k)
			}
		}
		s.mu.Unlock()
	}
}
