package biz

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

// TaskMethodFunc is the execution contract for a registered in-process task.
// Implementations are responsible for decoding params and honoring ctx cancellation.
type TaskMethodFunc func(ctx context.Context, params json.RawMessage) error

type TaskMethod struct {
	Name        string
	Description string
}

type taskMethodEntry struct {
	meta TaskMethod
	fn   TaskMethodFunc
}

var taskMethodRegistry = struct {
	sync.RWMutex
	methods map[string]taskMethodEntry
}{methods: make(map[string]taskMethodEntry)}

// RegisterTaskMethod registers an in-process task. Re-registering the same name
// replaces the previous implementation so startup registration remains idempotent.
func RegisterTaskMethod(name, description string, fn TaskMethodFunc) {
	taskMethodRegistry.Lock()
	defer taskMethodRegistry.Unlock()
	taskMethodRegistry.methods[name] = taskMethodEntry{
		meta: TaskMethod{Name: name, Description: description},
		fn:   fn,
	}
}

// TaskMethodByName returns the registered function for name.
func TaskMethodByName(name string) (TaskMethodFunc, bool) {
	taskMethodRegistry.RLock()
	defer taskMethodRegistry.RUnlock()
	method, ok := taskMethodRegistry.methods[name]
	if !ok {
		return nil, false
	}
	return method.fn, true
}

// RegisteredTaskMethods returns task metadata in a stable name order.
func RegisteredTaskMethods() []TaskMethod {
	taskMethodRegistry.RLock()
	defer taskMethodRegistry.RUnlock()
	methods := make([]TaskMethod, 0, len(taskMethodRegistry.methods))
	for _, method := range taskMethodRegistry.methods {
		methods = append(methods, method.meta)
	}
	sort.Slice(methods, func(i, j int) bool { return methods[i].Name < methods[j].Name })
	return methods
}

func registeredTaskMethod(name string) bool {
	_, ok := TaskMethodByName(name)
	return ok
}
