package dockerapi

import (
	"context"
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/yusing/godoxy/internal/docker"
	apitypes "github.com/yusing/goutils/apitypes"
	gperr "github.com/yusing/goutils/errs"
	"github.com/yusing/goutils/http/httpheaders"
	"github.com/yusing/goutils/http/websocket"
)

type (
	DockerClients     map[string]*docker.SharedClient
	ResultType[T any] interface {
		map[string]T | []T
	}
)

// closeAllClients closes all docker clients after a delay.
//
// This is used to ensure that all docker clients are closed after the http handler returns.
func closeAllClients(dockerClients DockerClients) {
	for _, dockerClient := range dockerClients {
		dockerClient.Close()
	}
}

func handleResult[V any, T ResultType[V]](c *gin.Context, errs error, result T) {
	if errs != nil {
		if len(result) == 0 {
			c.Error(apitypes.InternalServerError(errs, "docker errors"))
			return
		}
	}
	c.JSON(http.StatusOK, result)
}

func serveHTTP[V any, T ResultType[V]](c *gin.Context, getResult func(ctx context.Context, dockerClients DockerClients) (T, gperr.Error)) {
	dockerClients := docker.Clients()
	defer closeAllClients(dockerClients)

	if httpheaders.IsWebsocket(c.Request.Header) {
		websocket.PeriodicWrite(c, 5*time.Second, func() (any, error) {
			return getResult(c.Request.Context(), dockerClients)
		})
	} else {
		result, err := getResult(c.Request.Context(), dockerClients)
		handleResult[V](c, err, result)
	}
}
