package stack

import (
	"context"
	"errors"
	"fmt"
	"strings"
	"unicode"

	abraClient "coopcloud.tech/abra/client"
	"coopcloud.tech/abra/client/convert"
	"github.com/docker/cli/opts"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/api/types/swarm"
	"github.com/docker/docker/client"
)

type StackStatus struct {
	Services []swarm.Service
	Err      error
}

func getStackFilter(namespace string) filters.Args {
	filter := filters.NewArgs()
	filter.Add("label", convert.LabelNamespace+"="+namespace)
	return filter
}

func getStackServiceFilter(namespace string) filters.Args {
	return getStackFilter(namespace)
}

func getStackFilterFromOpt(namespace string, opt opts.FilterOpt) filters.Args {
	filter := opt.Value()
	filter.Add("label", convert.LabelNamespace+"="+namespace)
	return filter
}

func getAllStacksFilter() filters.Args {
	filter := filters.NewArgs()
	filter.Add("label", convert.LabelNamespace)
	return filter
}

func getStackServices(ctx context.Context, apiclient client.APIClient, namespace string) ([]swarm.Service, error) {
	return apiclient.ServiceList(ctx, types.ServiceListOptions{Filters: getStackServiceFilter(namespace)})
}

func GetAllDeployedServices(contextName string) StackStatus {
	cl, err := abraClient.NewClientWithContext(contextName)
	if err != nil {
		if strings.Contains(err.Error(), "does not exist") {
			// No local context found, bail out gracefully
			return StackStatus{[]swarm.Service{}, nil}
		}
		return StackStatus{[]swarm.Service{}, err}
	}
	ctx := context.Background()
	services, err := cl.ServiceList(ctx, types.ServiceListOptions{Filters: getAllStacksFilter()})
	if err != nil {
		return StackStatus{[]swarm.Service{}, err}
	}
	return StackStatus{services, nil}
}

func checkDaemonIsSwarmManager(contextName string) error {
	cl, err := abraClient.NewClientWithContext(contextName)
	if err != nil {
		return err
	}
	info, err := cl.Info(context.Background())
	if err != nil {
		return err
	}
	if !info.Swarm.ControlAvailable {
		return errors.New("this server is not a swarm manager. Did you run \"abra server init\"?")
	}
	return nil
}

// validateStackName checks if the provided string is a valid stack name (namespace).
// It currently only does a rudimentary check if the string is empty, or consists
// of only whitespace and quoting characters.
func validateStackName(namespace string) error {
	v := strings.TrimFunc(namespace, quotesOrWhitespace)
	if v == "" {
		return fmt.Errorf("invalid stack name: %q", namespace)
	}
	return nil
}

func quotesOrWhitespace(r rune) bool {
	return unicode.IsSpace(r) || r == '"' || r == '\''
}

func DeployStack(namespace string) {
}

// TODO: Prune services from stack

func pruneServices() error {
	return nil
}