package handler

import (
	"context"
	"sort"

	"golang.org/x/exp/slices"
	"golang.org/x/xerrors"

	"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
	"github.com/aquasecurity/trivy/pkg/fanal/artifact"
	"github.com/aquasecurity/trivy/pkg/fanal/types"
)

var (
	postHandlerInits = make(map[types.HandlerType]postHandlerInit)
)

type postHandlerInit func(artifact.Option) (PostHandler, error)

type PostHandler interface {
	Type() types.HandlerType
	Version() int
	Handle(context.Context, *analyzer.AnalysisResult, *types.BlobInfo) error
	Priority() int
}

// RegisterPostHandlerInit adds a constructor of post handler
func RegisterPostHandlerInit(t types.HandlerType, init postHandlerInit) {
	postHandlerInits[t] = init
}

func DeregisterPostHandler(t types.HandlerType) {
	delete(postHandlerInits, t)
}

type Manager struct {
	postHandlers []PostHandler
}

func NewManager(artifactOpt artifact.Option) (Manager, error) {
	var m Manager
	for t, handlerInit := range postHandlerInits {
		// Skip the handler if it is disabled
		if slices.Contains(artifactOpt.DisabledHandlers, t) {
			continue
		}
		handler, err := handlerInit(artifactOpt)
		if err != nil {
			return Manager{}, xerrors.Errorf("post handler %s initialize error: %w", t, err)
		}

		m.postHandlers = append(m.postHandlers, handler)
	}

	// Sort post handlers by priority
	sort.Slice(m.postHandlers, func(i, j int) bool {
		return m.postHandlers[i].Priority() > m.postHandlers[j].Priority()
	})

	return m, nil
}

func (m Manager) Versions() map[string]int {
	versions := make(map[string]int)
	for _, h := range m.postHandlers {
		versions[string(h.Type())] = h.Version()
	}
	return versions
}

func (m Manager) PostHandle(ctx context.Context, result *analyzer.AnalysisResult, blob *types.BlobInfo) error {
	for _, h := range m.postHandlers {
		if err := h.Handle(ctx, result, blob); err != nil {
			return xerrors.Errorf("post handler error: %w", err)
		}
	}
	return nil
}
