package plugin

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

// Trigger is the contract for one redeploy signal source (registry push,
// git push, manual, cron, ...). A Trigger has one job: given an inbound
// event and a workload's TriggerConfig, decide whether a deploy should
// fire and shape the resulting DeploymentIntent.
//
// Triggers do not perform deploys themselves — they hand the intent back
// to the deployer, which routes it to the matching Source. This keeps
// the (M sources × N triggers) cross-product code-free.
type Trigger interface {
	// Kind is the registration key (e.g. "registry", "git", "manual", "cron").
	Kind() string

	// Validate type-checks a raw trigger config blob before it is persisted.
	Validate(cfg json.RawMessage) error

	// Match decides whether evt fires a deploy of w. Returning (nil, nil)
	// means "not interested, skip silently"; an error is reserved for
	// configuration or signature problems the operator should see.
	Match(ctx context.Context, deps Deps, w Workload, evt InboundEvent) (*DeploymentIntent, error)
}

var (
	triggersMu sync.RWMutex
	triggers   = map[string]Trigger{}
)

// RegisterTrigger installs t under t.Kind(). Panics on duplicate
// registration (init-time bug, never a runtime condition).
func RegisterTrigger(t Trigger) {
	triggersMu.Lock()
	defer triggersMu.Unlock()
	k := t.Kind()
	if _, dup := triggers[k]; dup {
		panic(fmt.Sprintf("plugin: trigger %q already registered", k))
	}
	triggers[k] = t
}

// GetTrigger returns the Trigger for kind. Errors carry the missing kind
// for diagnostics.
func GetTrigger(kind string) (Trigger, error) {
	triggersMu.RLock()
	defer triggersMu.RUnlock()
	t, ok := triggers[kind]
	if !ok {
		return nil, fmt.Errorf("plugin: no trigger registered for kind %q", kind)
	}
	return t, nil
}

// TriggerKinds returns all registered trigger kinds, sorted.
func TriggerKinds() []string {
	triggersMu.RLock()
	defer triggersMu.RUnlock()
	out := make([]string, 0, len(triggers))
	for k := range triggers {
		out = append(out, k)
	}
	sortStrings(out)
	return out
}

// sortStrings is shared by SourceKinds / TriggerKinds.
func sortStrings(s []string) { sort.Strings(s) }
