// Package task contains the process-wide task method contract. It is an
// infrastructure registry, not a system business package, so any module can
// contribute an in-process scheduled task.
package task

import (
	"context"
	"encoding/json"
	"sort"
	"sync"
)

type MethodFunc func(context.Context, json.RawMessage) error

type Method struct {
	Name, Description string
	Run               MethodFunc
}

// Contributor registers task methods whose handlers need constructed runtime
// dependencies. Modules expose a contributor and the application composition
// root activates it; the task runtime never imports business modules.
type Contributor interface {
	RegisterTasks(*Registry)
}

type Registry struct {
	mu      sync.RWMutex
	methods map[string]Method
}

func NewRegistry() *Registry { return &Registry{methods: make(map[string]Method)} }

func Apply(registry *Registry, contributors ...Contributor) {
	if registry == nil {
		return
	}
	for _, contributor := range contributors {
		if contributor != nil {
			contributor.RegisterTasks(registry)
		}
	}
}

func (r *Registry) Register(method Method) {
	if r == nil || method.Name == "" || method.Run == nil {
		return
	}
	r.mu.Lock()
	defer r.mu.Unlock()
	if r.methods == nil {
		r.methods = make(map[string]Method)
	}
	r.methods[method.Name] = method
}

func (r *Registry) RegisterAll(methods []Method) {
	for _, method := range methods {
		r.Register(method)
	}
}

func (r *Registry) Lookup(name string) (MethodFunc, bool) {
	if r == nil {
		return nil, false
	}
	r.mu.RLock()
	defer r.mu.RUnlock()
	method, ok := r.methods[name]
	if !ok {
		return nil, false
	}
	return method.Run, true
}

func (r *Registry) List() []Method {
	if r == nil {
		return nil
	}
	r.mu.RLock()
	defer r.mu.RUnlock()
	methods := make([]Method, 0, len(r.methods))
	for _, method := range r.methods {
		methods = append(methods, method)
	}
	sort.Slice(methods, func(i, j int) bool { return methods[i].Name < methods[j].Name })
	return methods
}
