package agent

import (
	"encoding/json"
	"os"
	"path/filepath"
	"runtime"
	"sync"
)

// DockerConfig 是用户可配置的 Docker 连接信息。
type DockerConfig struct {
	// Socket 是用户配置的 Docker 连接地址，支持
	// unix:///var/run/docker.sock、npipe:////./pipe/docker_engine、tcp://host:2375 等。
	// 为空时使用默认的本地 Docker。
	Socket string `json:"socket,omitempty"`
}

// DockerConfigStore 持久化用户的 Docker socket 配置，并给出默认值。
type DockerConfigStore struct {
	mu            sync.RWMutex
	path          string
	config        DockerConfig
	defaultSocket string
}

func NewDockerConfigStore(dataDir string) (*DockerConfigStore, error) {
	defaultSocket := getenv("AGENT_DOCKER_SOCKET", "")
	if defaultSocket == "" {
		defaultSocket = defaultDockerSocket()
	}
	store := &DockerConfigStore{
		path:          filepath.Join(dataDir, "docker-config.json"),
		defaultSocket: defaultSocket,
	}
	store.load()
	return store, nil
}

func defaultDockerSocket() string {
	if runtime.GOOS == "windows" {
		return "npipe:////./pipe/docker_engine"
	}
	return "unix:///var/run/docker.sock"
}

func (s *DockerConfigStore) load() {
	data, err := os.ReadFile(s.path)
	if err != nil {
		return
	}
	if len(data) == 0 {
		return
	}
	var config DockerConfig
	if err := json.Unmarshal(data, &config); err != nil {
		return
	}
	s.config = config
}

// Socket 返回当前生效的 docker 连接地址：用户配置优先，否则使用默认本地 Docker。
func (s *DockerConfigStore) Socket() string {
	s.mu.RLock()
	defer s.mu.RUnlock()
	if s.config.Socket != "" {
		return s.config.Socket
	}
	return s.defaultSocket
}

// DefaultSocket 返回未配置时使用的默认地址。
func (s *DockerConfigStore) DefaultSocket() string {
	return s.defaultSocket
}

// IsConfigured 报告用户是否手动配置过 socket。
func (s *DockerConfigStore) IsConfigured() bool {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.config.Socket != ""
}

// SetSocket 保存用户的 socket 配置；传入空字符串表示清除配置、恢复默认。
func (s *DockerConfigStore) SetSocket(socket string) error {
	s.mu.Lock()
	defer s.mu.Unlock()

	s.config.Socket = socket
	data, err := json.MarshalIndent(s.config, "", "  ")
	if err != nil {
		return err
	}
	tmp := s.path + ".tmp"
	if err := os.WriteFile(tmp, data, 0o600); err != nil {
		return err
	}
	return os.Rename(tmp, s.path)
}
